From 2108da79060d7653854e365649e0c18a69e46a1e Mon Sep 17 00:00:00 2001 From: unitydadechin Date: Sat, 28 Jun 2025 11:28:54 +0330 Subject: [PATCH] first commit first commit --- Assets/DefaultPrefabObjects.asset | 28 + Assets/DefaultPrefabObjects.asset.meta | 8 + Assets/FishNet.meta | 8 + Assets/FishNet/CodeGenerating.meta | 8 + Assets/FishNet/CodeGenerating/Extension.meta | 8 + .../Extension/FieldDefinitionExtensions.cs | 22 + .../FieldDefinitionExtensions.cs.meta | 18 + .../Extension/FieldReferenceExtensions.cs | 40 + .../FieldReferenceExtensions.cs.meta | 18 + .../Extension/ILProcessorExtensions.cs | 28 + .../Extension/ILProcessorExtensions.cs.meta | 18 + .../Extension/MethodDefinitionExtensions.cs | 239 + .../MethodDefinitionExtensions.cs.meta | 18 + .../Extension/TypeDefinitionExtensions.cs | 314 ++ .../TypeDefinitionExtensions.cs.meta | 18 + .../Extension/TypeReferenceExtensions.cs | 52 + .../Extension/TypeReferenceExtensions.cs.meta | 18 + Assets/FishNet/CodeGenerating/FN_README.txt | 9 + .../FishNet/CodeGenerating/FN_README.txt.meta | 14 + Assets/FishNet/CodeGenerating/Helpers.meta | 8 + .../CodeGenerating/Helpers/AttributeHelper.cs | 68 + .../Helpers/AttributeHelper.cs.meta | 18 + .../CodeGenerating/Helpers/CodegenSession.cs | 184 + .../Helpers/CodegenSession.cs.meta | 18 + .../Helpers/CreatedSyncVarGenerator.cs | 120 + .../Helpers/CreatedSyncVarGenerator.cs.meta | 18 + .../CodeGenerating/Helpers/Extension.meta | 8 + .../Extension/CustomAttributeExtensions.cs | 54 + .../CustomAttributeExtensions.cs.meta | 18 + .../Helpers/Extension/Diagnostics.cs | 41 + .../Helpers/Extension/Diagnostics.cs.meta | 18 + .../Helpers/Extension/GetConstructor.cs | 191 + .../Helpers/Extension/GetConstructor.cs.meta | 18 + .../Extension/ILProcessorExtensions.cs | 198 + .../Extension/ILProcessorExtensions.cs.meta | 18 + .../Extension/InstructionExtensions.cs | 12 + .../Extension/InstructionExtensions.cs.meta | 18 + .../Extension/MethodReferenceExtensions.cs | 174 + .../MethodReferenceExtensions.cs.meta | 18 + .../Extension/ModuleDefinitionExtensions.cs | 63 + .../ModuleDefinitionExtensions.cs.meta | 18 + .../ParameterDefinitionExtensions.cs | 24 + .../ParameterDefinitionExtensions.cs.meta | 18 + .../Extension/TypeDefinitionExtensions.cs | 504 ++ .../TypeDefinitionExtensions.cs.meta | 18 + .../Extension/TypeReferenceExtensions.cs | 141 + .../Extension/TypeReferenceExtensions.cs.meta | 18 + .../CodeGenerating/Helpers/GeneralHelper.cs | 1454 +++++ .../Helpers/GeneralHelper.cs.meta | 18 + .../Helpers/NetworkBehaviourHelper.cs | 471 ++ .../Helpers/NetworkBehaviourHelper.cs.meta | 18 + .../Helpers/NetworkConnectionImports.cs | 42 + .../Helpers/NetworkConnectionImports.cs.meta | 18 + .../CodeGenerating/Helpers/ObjectHelper.cs | 99 + .../Helpers/ObjectHelper.cs.meta | 18 + .../CodeGenerating/Helpers/PhysicsHelper.cs | 117 + .../Helpers/PhysicsHelper.cs.meta | 18 + .../Helpers/PredictedObjectHelper.cs | 15 + .../Helpers/PredictedObjectHelper.cs.meta | 18 + .../CodeGenerating/Helpers/ReaderImports.cs | 63 + .../Helpers/ReaderImports.cs.meta | 18 + .../Helpers/TimeManagerHelper.cs | 22 + .../Helpers/TimeManagerHelper.cs.meta | 18 + .../CodeGenerating/Helpers/TransportHelper.cs | 36 + .../Helpers/TransportHelper.cs.meta | 18 + .../FishNet/CodeGenerating/Helpers/Typed.meta | 8 + .../CodeGenerating/Helpers/Typed/Comparers.cs | 34 + .../Helpers/Typed/Comparers.cs.meta | 18 + .../Helpers/Typed/GeneratorHelper.cs | 186 + .../Helpers/Typed/GeneratorHelper.cs.meta | 18 + .../Helpers/Typed/QOLAttributeType.cs | 12 + .../Helpers/Typed/QOLAttributeType.cs.meta | 18 + .../Helpers/Typed/SerializatierType.cs | 19 + .../Helpers/Typed/SerializatierType.cs.meta | 18 + .../Helpers/Typed/SyncIndexData.cs | 17 + .../Helpers/Typed/SyncIndexData.cs.meta | 18 + .../CodeGenerating/Helpers/Typed/SyncType.cs | 14 + .../Helpers/Typed/SyncType.cs.meta | 18 + .../CodeGenerating/Helpers/WriterImports.cs | 100 + .../Helpers/WriterImports.cs.meta | 18 + Assets/FishNet/CodeGenerating/ILCore.meta | 8 + .../CodeGenerating/ILCore/FishNetILPP.cs | 425 ++ .../CodeGenerating/ILCore/FishNetILPP.cs.meta | 18 + .../CodeGenerating/ILCore/ILCoreHelper.cs | 38 + .../ILCore/ILCoreHelper.cs.meta | 18 + .../ILCore/PostProcessorAssemblyResolver.cs | 139 + .../PostProcessorAssemblyResolver.cs.meta | 18 + .../ILCore/PostProcessorReflectionImporter.cs | 22 + .../PostProcessorReflectionImporter.cs.meta | 18 + ...PostProcessorReflectionImporterProvider.cs | 12 + ...rocessorReflectionImporterProvider.cs.meta | 18 + Assets/FishNet/CodeGenerating/Processing.meta | 8 + .../CodeGenerating/Processing/CodegenBase.cs | 61 + .../Processing/CodegenBase.cs.meta | 18 + .../Processing/CustomSerializerProcessor.cs | 341 ++ .../CustomSerializerProcessor.cs.meta | 18 + .../Processing/NetworkBehaviourProcessor.cs | 503 ++ .../NetworkBehaviourProcessor.cs.meta | 18 + .../CodeGenerating/Processing/Prediction.meta | 8 + .../Prediction/PredictionProcessor.cs | 1092 ++++ .../Prediction/PredictionProcessor.cs.meta | 18 + .../Processing/QOLAttributeProcessor.cs | 166 + .../Processing/QOLAttributeProcessor.cs.meta | 18 + .../Processing/ReaderProcessor.cs | 1062 ++++ .../Processing/ReaderProcessor.cs.meta | 18 + .../CodeGenerating/Processing/Rpc.meta | 8 + .../Processing/Rpc/AttributeData.cs | 68 + .../Processing/Rpc/AttributeData.cs.meta | 18 + .../Processing/Rpc/Attributes.cs | 166 + .../Processing/Rpc/Attributes.cs.meta | 18 + .../Processing/Rpc/CreatedRpc.cs | 58 + .../Processing/Rpc/CreatedRpc.cs.meta | 18 + .../Processing/Rpc/RpcProcessor.cs | 1154 ++++ .../Processing/Rpc/RpcProcessor.cs.meta | 18 + .../Processing/SyncTypeProcessor.cs | 565 ++ .../Processing/SyncTypeProcessor.cs.meta | 18 + .../Processing/WriterProcessor.cs | 1096 ++++ .../Processing/WriterProcessor.cs.meta | 18 + .../Unity.FishNet.CodeGen.asmdef | 20 + .../Unity.FishNet.CodeGen.asmdef.meta | 14 + .../FishNet/CodeGenerating/cecil-0.11.4.meta | 8 + .../cecil-0.11.4/Directory.Build.props | 30 + .../cecil-0.11.4/Directory.Build.props.meta | 14 + .../CodeGenerating/cecil-0.11.4/LICENSE.txt | 21 + .../cecil-0.11.4/LICENSE.txt.meta | 14 + .../cecil-0.11.4/Mono.Cecil.Cil.meta | 8 + .../cecil-0.11.4/Mono.Cecil.Cil/Code.cs | 234 + .../cecil-0.11.4/Mono.Cecil.Cil/Code.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs | 663 +++ .../Mono.Cecil.Cil/CodeReader.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs | 651 +++ .../Mono.Cecil.Cil/CodeWriter.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/Document.cs | 123 + .../Mono.Cecil.Cil/Document.cs.meta | 18 + .../Mono.Cecil.Cil/ExceptionHandler.cs | 71 + .../Mono.Cecil.Cil/ExceptionHandler.cs.meta | 18 + .../Mono.Cecil.Cil/ILProcessor.cs | 291 + .../Mono.Cecil.Cil/ILProcessor.cs.meta | 18 + .../Mono.Cecil.Cil/Instruction.cs | 296 + .../Mono.Cecil.Cil/Instruction.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs | 426 ++ .../Mono.Cecil.Cil/MethodBody.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs | 439 ++ .../Mono.Cecil.Cil/OpCode.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs | 894 +++ .../Mono.Cecil.Cil/OpCodes.cs.meta | 18 + .../Mono.Cecil.Cil/PortablePdb.cs | 591 ++ .../Mono.Cecil.Cil/PortablePdb.cs.meta | 18 + .../Mono.Cecil.Cil/SequencePoint.cs | 76 + .../Mono.Cecil.Cil/SequencePoint.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs | 1226 +++++ .../Mono.Cecil.Cil/Symbols.cs.meta | 18 + .../Mono.Cecil.Cil/VariableDefinition.cs | 29 + .../Mono.Cecil.Cil/VariableDefinition.cs.meta | 18 + .../Mono.Cecil.Cil/VariableReference.cs | 42 + .../Mono.Cecil.Cil/VariableReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Metadata.meta | 8 + .../Mono.Cecil.Metadata/BlobHeap.cs | 54 + .../Mono.Cecil.Metadata/BlobHeap.cs.meta | 18 + .../Mono.Cecil.Metadata/Buffers.cs | 499 ++ .../Mono.Cecil.Metadata/Buffers.cs.meta | 18 + .../Mono.Cecil.Metadata/CodedIndex.cs | 29 + .../Mono.Cecil.Metadata/CodedIndex.cs.meta | 18 + .../Mono.Cecil.Metadata/ElementType.cs | 55 + .../Mono.Cecil.Metadata/ElementType.cs.meta | 18 + .../Mono.Cecil.Metadata/GuidHeap.cs | 36 + .../Mono.Cecil.Metadata/GuidHeap.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs | 24 + .../Mono.Cecil.Metadata/Heap.cs.meta | 18 + .../Mono.Cecil.Metadata/MetadataToken.cs | 94 + .../Mono.Cecil.Metadata/MetadataToken.cs.meta | 18 + .../Mono.Cecil.Metadata/PdbHeap.cs | 32 + .../Mono.Cecil.Metadata/PdbHeap.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Metadata/Row.cs | 152 + .../Mono.Cecil.Metadata/Row.cs.meta | 18 + .../Mono.Cecil.Metadata/StringHeap.cs | 59 + .../Mono.Cecil.Metadata/StringHeap.cs.meta | 18 + .../Mono.Cecil.Metadata/TableHeap.cs | 101 + .../Mono.Cecil.Metadata/TableHeap.cs.meta | 18 + .../Mono.Cecil.Metadata/TokenType.cs | 49 + .../Mono.Cecil.Metadata/TokenType.cs.meta | 18 + .../Mono.Cecil.Metadata/UserStringHeap.cs | 36 + .../UserStringHeap.cs.meta | 18 + .../Mono.Cecil.Metadata/Utilities.cs | 649 +++ .../Mono.Cecil.Metadata/Utilities.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE.meta | 8 + .../Mono.Cecil.PE/BinaryStreamReader.cs | 53 + .../Mono.Cecil.PE/BinaryStreamReader.cs.meta | 18 + .../Mono.Cecil.PE/BinaryStreamWriter.cs | 88 + .../Mono.Cecil.PE/BinaryStreamWriter.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs | 335 ++ .../Mono.Cecil.PE/ByteBuffer.cs.meta | 18 + .../ByteBufferEqualityComparer.cs | 47 + .../ByteBufferEqualityComparer.cs.meta | 18 + .../Mono.Cecil.PE/DataDirectory.cs | 30 + .../Mono.Cecil.PE/DataDirectory.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE/Image.cs | 169 + .../cecil-0.11.4/Mono.Cecil.PE/Image.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs | 793 +++ .../Mono.Cecil.PE/ImageReader.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs | 860 +++ .../Mono.Cecil.PE/ImageWriter.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE/Section.cs | 22 + .../Mono.Cecil.PE/Section.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.PE/TextMap.cs | 106 + .../Mono.Cecil.PE/TextMap.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil.Tests.props | 16 + .../cecil-0.11.4/Mono.Cecil.Tests.props.meta | 14 + .../cecil-0.11.4/Mono.Cecil.meta | 8 + .../cecil-0.11.4/Mono.Cecil.nunit | 9 + .../cecil-0.11.4/Mono.Cecil.nunit.meta | 14 + .../cecil-0.11.4/Mono.Cecil.nuspec | 42 + .../cecil-0.11.4/Mono.Cecil.nuspec.meta | 14 + .../cecil-0.11.4/Mono.Cecil.sln | 67 + .../cecil-0.11.4/Mono.Cecil.sln.meta | 14 + .../cecil-0.11.4/Mono.Cecil/ArrayType.cs | 145 + .../cecil-0.11.4/Mono.Cecil/ArrayType.cs.meta | 18 + .../Mono.Cecil/AssemblyDefinition.cs | 189 + .../Mono.Cecil/AssemblyDefinition.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs | 24 + .../Mono.Cecil/AssemblyFlags.cs.meta | 18 + .../Mono.Cecil/AssemblyHashAlgorithm.cs | 22 + .../Mono.Cecil/AssemblyHashAlgorithm.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs | 23 + .../Mono.Cecil/AssemblyInfo.cs.meta | 18 + .../Mono.Cecil/AssemblyLinkedResource.cs | 37 + .../Mono.Cecil/AssemblyLinkedResource.cs.meta | 18 + .../Mono.Cecil/AssemblyNameDefinition.cs | 32 + .../Mono.Cecil/AssemblyNameDefinition.cs.meta | 18 + .../Mono.Cecil/AssemblyNameReference.cs | 269 + .../Mono.Cecil/AssemblyNameReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/AssemblyReader.cs | 3889 +++++++++++++ .../Mono.Cecil/AssemblyReader.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs | 3336 ++++++++++++ .../Mono.Cecil/AssemblyWriter.cs.meta | 18 + .../Mono.Cecil/BaseAssemblyResolver.cs | 406 ++ .../Mono.Cecil/BaseAssemblyResolver.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/CallSite.cs | 105 + .../cecil-0.11.4/Mono.Cecil/CallSite.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/Consts.cs | 10 + .../cecil-0.11.4/Mono.Cecil/Consts.cs.meta | 18 + .../Mono.Cecil/CustomAttribute.cs | 221 + .../Mono.Cecil/CustomAttribute.cs.meta | 18 + .../Mono.Cecil/DefaultAssemblyResolver.cs | 61 + .../DefaultAssemblyResolver.cs.meta | 18 + .../Mono.Cecil/EmbeddedResource.cs | 98 + .../Mono.Cecil/EmbeddedResource.cs.meta | 18 + .../Mono.Cecil/EventAttributes.cs | 21 + .../Mono.Cecil/EventAttributes.cs.meta | 18 + .../Mono.Cecil/EventDefinition.cs | 156 + .../Mono.Cecil/EventDefinition.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/EventReference.cs | 40 + .../Mono.Cecil/EventReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/ExportedType.cs | 238 + .../Mono.Cecil/ExportedType.cs.meta | 18 + .../Mono.Cecil/FieldAttributes.cs | 41 + .../Mono.Cecil/FieldAttributes.cs.meta | 18 + .../Mono.Cecil/FieldDefinition.cs | 281 + .../Mono.Cecil/FieldDefinition.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/FieldReference.cs | 68 + .../Mono.Cecil/FieldReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/FileAttributes.cs | 17 + .../Mono.Cecil/FileAttributes.cs.meta | 18 + .../Mono.Cecil/FunctionPointerType.cs | 111 + .../Mono.Cecil/FunctionPointerType.cs.meta | 18 + .../Mono.Cecil/GenericInstanceMethod.cs | 77 + .../Mono.Cecil/GenericInstanceMethod.cs.meta | 18 + .../Mono.Cecil/GenericInstanceType.cs | 75 + .../Mono.Cecil/GenericInstanceType.cs.meta | 18 + .../Mono.Cecil/GenericParameter.cs | 360 ++ .../Mono.Cecil/GenericParameter.cs.meta | 18 + .../Mono.Cecil/GenericParameterAttributes.cs | 27 + .../GenericParameterAttributes.cs.meta | 18 + .../Mono.Cecil/GenericParameterResolver.cs | 175 + .../GenericParameterResolver.cs.meta | 18 + .../Mono.Cecil/IConstantProvider.cs | 44 + .../Mono.Cecil/IConstantProvider.cs.meta | 18 + .../Mono.Cecil/ICustomAttributeProvider.cs | 44 + .../ICustomAttributeProvider.cs.meta | 18 + .../Mono.Cecil/IGenericInstance.cs | 47 + .../Mono.Cecil/IGenericInstance.cs.meta | 18 + .../Mono.Cecil/IGenericParameterProvider.cs | 58 + .../IGenericParameterProvider.cs.meta | 18 + .../Mono.Cecil/IMarshalInfoProvider.cs | 38 + .../Mono.Cecil/IMarshalInfoProvider.cs.meta | 18 + .../Mono.Cecil/IMemberDefinition.cs | 82 + .../Mono.Cecil/IMemberDefinition.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/IMetadataScope.cs | 23 + .../Mono.Cecil/IMetadataScope.cs.meta | 18 + .../Mono.Cecil/IMetadataTokenProvider.cs | 17 + .../Mono.Cecil/IMetadataTokenProvider.cs.meta | 18 + .../Mono.Cecil/IMethodSignature.cs | 56 + .../Mono.Cecil/IMethodSignature.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/Import.cs | 858 +++ .../cecil-0.11.4/Mono.Cecil/Import.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/LinkedResource.cs | 42 + .../Mono.Cecil/LinkedResource.cs.meta | 18 + .../Mono.Cecil/ManifestResourceAttributes.cs | 21 + .../ManifestResourceAttributes.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/MarshalInfo.cs | 153 + .../Mono.Cecil/MarshalInfo.cs.meta | 18 + .../Mono.Cecil/MemberDefinitionCollection.cs | 73 + .../MemberDefinitionCollection.cs.meta | 18 + .../Mono.Cecil/MemberReference.cs | 102 + .../Mono.Cecil/MemberReference.cs.meta | 18 + .../Mono.Cecil/MetadataResolver.cs | 391 ++ .../Mono.Cecil/MetadataResolver.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/MetadataSystem.cs | 431 ++ .../Mono.Cecil/MetadataSystem.cs.meta | 18 + .../Mono.Cecil/MethodAttributes.cs | 48 + .../Mono.Cecil/MethodAttributes.cs.meta | 18 + .../Mono.Cecil/MethodCallingConvention.cs | 22 + .../MethodCallingConvention.cs.meta | 18 + .../Mono.Cecil/MethodDefinition.cs | 558 ++ .../Mono.Cecil/MethodDefinition.cs.meta | 18 + .../Mono.Cecil/MethodImplAttributes.cs | 36 + .../Mono.Cecil/MethodImplAttributes.cs.meta | 18 + .../Mono.Cecil/MethodReference.cs | 202 + .../Mono.Cecil/MethodReference.cs.meta | 18 + .../Mono.Cecil/MethodReferenceComparer.cs | 144 + .../MethodReferenceComparer.cs.meta | 18 + .../Mono.Cecil/MethodReturnType.cs | 97 + .../Mono.Cecil/MethodReturnType.cs.meta | 18 + .../Mono.Cecil/MethodSemanticsAttributes.cs | 25 + .../MethodSemanticsAttributes.cs.meta | 18 + .../Mono.Cecil/MethodSpecification.cs | 83 + .../Mono.Cecil/MethodSpecification.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/Modifiers.cs | 112 + .../cecil-0.11.4/Mono.Cecil/Modifiers.cs.meta | 18 + .../Mono.Cecil/ModuleDefinition.cs | 1353 +++++ .../Mono.Cecil/ModuleDefinition.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/ModuleKind.cs | 55 + .../Mono.Cecil/ModuleKind.cs.meta | 18 + .../Mono.Cecil/ModuleReference.cs | 49 + .../Mono.Cecil/ModuleReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/NativeType.cs | 55 + .../Mono.Cecil/NativeType.cs.meta | 18 + .../Mono.Cecil/PInvokeAttributes.cs | 44 + .../Mono.Cecil/PInvokeAttributes.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs | 120 + .../Mono.Cecil/PInvokeInfo.cs.meta | 18 + .../Mono.Cecil/ParameterAttributes.cs | 27 + .../Mono.Cecil/ParameterAttributes.cs.meta | 18 + .../Mono.Cecil/ParameterDefinition.cs | 146 + .../Mono.Cecil/ParameterDefinition.cs.meta | 18 + .../ParameterDefinitionCollection.cs | 60 + .../ParameterDefinitionCollection.cs.meta | 18 + .../Mono.Cecil/ParameterReference.cs | 57 + .../Mono.Cecil/ParameterReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/PinnedType.cs | 35 + .../Mono.Cecil/PinnedType.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/PointerType.cs | 43 + .../Mono.Cecil/PointerType.cs.meta | 18 + .../Mono.Cecil/PropertyAttributes.cs | 23 + .../Mono.Cecil/PropertyAttributes.cs.meta | 18 + .../Mono.Cecil/PropertyDefinition.cs | 245 + .../Mono.Cecil/PropertyDefinition.cs.meta | 18 + .../Mono.Cecil/PropertyReference.cs | 43 + .../Mono.Cecil/PropertyReference.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/ReferenceType.cs | 43 + .../Mono.Cecil/ReferenceType.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/Resource.cs | 58 + .../cecil-0.11.4/Mono.Cecil/Resource.cs.meta | 18 + .../Mono.Cecil/SecurityDeclaration.cs | 201 + .../Mono.Cecil/SecurityDeclaration.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/SentinelType.cs | 35 + .../Mono.Cecil/SentinelType.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TargetRuntime.cs | 19 + .../Mono.Cecil/TargetRuntime.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/Treatments.cs | 61 + .../Mono.Cecil/Treatments.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TypeAttributes.cs | 63 + .../Mono.Cecil/TypeAttributes.cs.meta | 18 + .../Mono.Cecil/TypeComparisonMode.cs | 11 + .../Mono.Cecil/TypeComparisonMode.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TypeDefinition.cs | 618 +++ .../Mono.Cecil/TypeDefinition.cs.meta | 18 + .../Mono.Cecil/TypeDefinitionCollection.cs | 98 + .../TypeDefinitionCollection.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TypeParser.cs | 531 ++ .../Mono.Cecil/TypeParser.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TypeReference.cs | 352 ++ .../Mono.Cecil/TypeReference.cs.meta | 18 + .../TypeReferenceEqualityComparer.cs | 253 + .../TypeReferenceEqualityComparer.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TypeResolver.cs | 220 + .../Mono.Cecil/TypeResolver.cs.meta | 18 + .../Mono.Cecil/TypeSpecification.cs | 66 + .../Mono.Cecil/TypeSpecification.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/TypeSystem.cs | 330 ++ .../Mono.Cecil/TypeSystem.cs.meta | 18 + .../cecil-0.11.4/Mono.Cecil/VariantType.cs | 37 + .../Mono.Cecil/VariantType.cs.meta | 18 + .../Mono.Cecil/WindowsRuntimeProjections.cs | 959 ++++ .../WindowsRuntimeProjections.cs.meta | 18 + .../Mono.Collections.Generic.meta | 8 + .../Mono.Collections.Generic/Collection.cs | 427 ++ .../Collection.cs.meta | 18 + .../ReadOnlyCollection.cs | 101 + .../ReadOnlyCollection.cs.meta | 18 + .../Mono.Security.Cryptography.meta | 8 + .../CryptoConvert.cs | 290 + .../CryptoConvert.cs.meta | 18 + .../CryptoService.cs | 202 + .../CryptoService.cs.meta | 18 + .../CodeGenerating/cecil-0.11.4/Mono.meta | 8 + .../cecil-0.11.4/Mono/Disposable.cs | 45 + .../cecil-0.11.4/Mono/Disposable.cs.meta | 18 + .../CodeGenerating/cecil-0.11.4/Mono/Empty.cs | 62 + .../cecil-0.11.4/Mono/Empty.cs.meta | 18 + .../cecil-0.11.4/Mono/MergeSort.cs | 66 + .../cecil-0.11.4/Mono/MergeSort.cs.meta | 18 + .../cecil-0.11.4/MonoFN.Cecil.asmdef | 15 + .../cecil-0.11.4/MonoFN.Cecil.asmdef.meta | 14 + .../cecil-0.11.4/ProjectInfo.cs | 21 + .../cecil-0.11.4/ProjectInfo.cs.meta | 18 + .../CodeGenerating/cecil-0.11.4/README.md | 17 + .../cecil-0.11.4/README.md.meta | 14 + .../CodeGenerating/cecil-0.11.4/cecil.snk | Bin 0 -> 596 bytes .../cecil-0.11.4/cecil.snk.meta | 14 + .../CodeGenerating/cecil-0.11.4/rocks.meta | 8 + .../cecil-0.11.4/rocks/Mono.Cecil.Rocks.meta | 8 + .../rocks/Mono.Cecil.Rocks/AssemblyInfo.cs | 15 + .../Mono.Cecil.Rocks/AssemblyInfo.cs.meta | 18 + .../rocks/Mono.Cecil.Rocks/DocCommentId.cs | 261 + .../Mono.Cecil.Rocks/DocCommentId.cs.meta | 18 + .../rocks/Mono.Cecil.Rocks/Functional.cs | 41 + .../rocks/Mono.Cecil.Rocks/Functional.cs.meta | 18 + .../rocks/Mono.Cecil.Rocks/ILParser.cs | 228 + .../rocks/Mono.Cecil.Rocks/ILParser.cs.meta | 18 + .../rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs | 411 ++ .../Mono.Cecil.Rocks/MethodBodyRocks.cs.meta | 18 + .../Mono.Cecil.Rocks/MethodDefinitionRocks.cs | 72 + .../MethodDefinitionRocks.cs.meta | 18 + .../Mono.Cecil.Rocks/ModuleDefinitionRocks.cs | 32 + .../ModuleDefinitionRocks.cs.meta | 18 + .../ParameterReferenceRocks.cs | 11 + .../ParameterReferenceRocks.cs.meta | 18 + .../SecurityDeclarationRocks.cs | 157 + .../SecurityDeclarationRocks.cs.meta | 18 + .../Mono.Cecil.Rocks/TypeDefinitionRocks.cs | 65 + .../TypeDefinitionRocks.cs.meta | 18 + .../Mono.Cecil.Rocks/TypeReferenceRocks.cs | 87 + .../TypeReferenceRocks.cs.meta | 18 + Assets/FishNet/DOCUMENTATION.txt | 3 + Assets/FishNet/DOCUMENTATION.txt.meta | 14 + Assets/FishNet/Demos.meta | 8 + Assets/FishNet/Demos/Authenticator.meta | 8 + .../FishNet/Demos/Authenticator/Scenes.meta | 8 + .../Authenticator/Scenes/Authenticator.unity | 359 ++ .../Scenes/Authenticator.unity.meta | 14 + .../FishNet/Demos/Authenticator/Scripts.meta | 8 + .../Demos/Authenticator/Scripts/Broadcasts.cs | 21 + .../Authenticator/Scripts/Broadcasts.cs.meta | 18 + .../Scripts/HostAuthenticator.cs | 152 + .../Scripts/HostAuthenticator.cs.meta | 18 + .../Scripts/PasswordAuthenticator.cs | 132 + .../Scripts/PasswordAuthenticator.cs.meta | 18 + Assets/FishNet/Demos/Benchmarks.meta | 8 + Assets/FishNet/Demos/Benchmarks/License.txt | 11 + .../FishNet/Demos/Benchmarks/License.txt.meta | 14 + .../Demos/Benchmarks/NetworkTransform.meta | 8 + .../NetworkTransform/Materials.meta | 8 + .../Materials/Translucent_Unlit.mat | 144 + .../Materials/Translucent_Unlit.mat.meta | 15 + .../Benchmarks/NetworkTransform/Prefabs.meta | 8 + .../NetworkTransform Benchmark 2D.prefab | 330 ++ .../NetworkTransform Benchmark 2D.prefab.meta | 15 + ...etworkTransform Benchmark 3D Normal.prefab | 244 + ...kTransform Benchmark 3D Normal.prefab.meta | 15 + ...kTransform Benchmark 3D Rigidbodies.prefab | 521 ++ ...sform Benchmark 3D Rigidbodies.prefab.meta | 15 + .../NetworkTransform Benchmark Cubes.prefab | 244 + ...tworkTransform Benchmark Cubes.prefab.meta | 15 + ...workTransform Benchmark Rigidbodies.prefab | 269 + ...ransform Benchmark Rigidbodies.prefab.meta | 15 + .../Benchmarks/NetworkTransform/ReadMe.txt | 21 + .../NetworkTransform/ReadMe.txt.meta | 14 + .../Benchmarks/NetworkTransform/Scenes.meta | 8 + .../Scenes/NetworkTransform Benchmark.unity | 1374 +++++ .../NetworkTransform Benchmark.unity.meta | 15 + .../Benchmarks/NetworkTransform/Scripts.meta | 8 + .../Scripts/MoveRandomlyNonPhysics.cs | 214 + .../Scripts/MoveRandomlyNonPhysics.cs.meta | 18 + .../Scripts/MoveRandomlyPhysics.cs | 75 + .../Scripts/MoveRandomlyPhysics.cs.meta | 18 + .../NetworkTransform/Scripts/PrefabSpawner.cs | 93 + .../Scripts/PrefabSpawner.cs.meta | 18 + Assets/FishNet/Demos/ColliderRollback.meta | 8 + .../FishNet/Demos/ColliderRollback/Audio.meta | 8 + .../Demos/ColliderRollback/Audio/Glock.wav | Bin 0 -> 122720 bytes .../ColliderRollback/Audio/Glock.wav.meta | 29 + .../Demos/ColliderRollback/Materials.meta | 8 + .../ColliderRollback/Materials/Ground.mat | 138 + .../Materials/Ground.mat.meta | 15 + .../ColliderRollback/Materials/Particles.meta | 8 + .../Materials/Particles/Circle Additive.mat | 29 + .../Particles/Circle Additive.mat.meta | 15 + .../Materials/Rollback Visualization.meta | 8 + .../Rollback Visualization/ClientPosition.mat | 140 + .../ClientPosition.mat.meta | 15 + .../Rollback Visualization/ServerPosition.mat | 140 + .../ServerPosition.mat.meta | 15 + .../ColliderRollback/Materials/Target.mat | 138 + .../Materials/Target.mat.meta | 15 + .../Demos/ColliderRollback/Materials/Wall.mat | 138 + .../ColliderRollback/Materials/Wall.mat.meta | 15 + .../Demos/ColliderRollback/Models.meta | 8 + .../ColliderRollback/Models/Weapons.meta | 8 + .../ColliderRollback/Models/Weapons/Glock.obj | 949 ++++ .../Models/Weapons/Glock.obj.meta | 88 + .../Models/Weapons/Materials.meta | 8 + .../Models/Weapons/Materials/Gray4.mat | 139 + .../Models/Weapons/Materials/Gray4.mat.meta | 15 + .../Models/Weapons/Materials/Gray6.mat | 139 + .../Models/Weapons/Materials/Gray6.mat.meta | 15 + .../Models/Weapons/Materials/Gray8.mat | 139 + .../Models/Weapons/Materials/Gray8.mat.meta | 15 + .../Mesh1_Group2_Group1_ModelMat.mat | 138 + .../Mesh1_Group2_Group1_ModelMat.mat.meta | 15 + .../Mesh2_Group3_Group1_ModelMat.mat | 138 + .../Mesh2_Group3_Group1_ModelMat.mat.meta | 15 + .../Demos/ColliderRollback/Prefabs.meta | 8 + .../ColliderRollback/Prefabs/Particles.meta | 8 + .../Particles/Pistol Muzzle Flash.prefab | 4787 ++++++++++++++++ .../Particles/Pistol Muzzle Flash.prefab.meta | 15 + .../ColliderRollback/Prefabs/Player.prefab | 162 + .../Prefabs/Player.prefab.meta | 14 + .../Prefabs/Rollback Visualization.meta | 8 + .../ClientPosition.prefab | 95 + .../ClientPosition.prefab.meta | 14 + .../ServerPosition.prefab | 95 + .../ServerPosition.prefab.meta | 14 + .../Rollback Visualization/TextCanvas.prefab | 282 + .../TextCanvas.prefab.meta | 14 + .../FishNet/Demos/ColliderRollback/ReadMe.txt | 12 + .../Demos/ColliderRollback/ReadMe.txt.meta | 14 + .../Demos/ColliderRollback/Scenes.meta | 8 + .../Scenes/ColliderRollbackDemo.unity | 2446 +++++++++ .../Scenes/ColliderRollbackDemo.unity.meta | 14 + .../Demos/ColliderRollback/Scripts.meta | 8 + .../Scripts/DestroyAfterDelay.cs | 17 + .../Scripts/DestroyAfterDelay.cs.meta | 18 + .../ColliderRollback/Scripts/Player.meta | 8 + .../ColliderRollback/Scripts/Player/Aim.cs | 67 + .../Scripts/Player/Aim.cs.meta | 18 + .../ColliderRollback/Scripts/Player/Fire.cs | 22 + .../Scripts/Player/Fire.cs.meta | 18 + .../Scripts/Player/PlayerCamera.cs | 19 + .../Scripts/Player/PlayerCamera.cs.meta | 18 + .../Scripts/Player/PlayerMotor.cs | 50 + .../Scripts/Player/PlayerMotor.cs.meta | 18 + .../Scripts/Rollback Visualization.meta | 8 + .../RollbackVisualizer.cs | 65 + .../RollbackVisualizer.cs.meta | 18 + .../Rollback Visualization/TextCanvas.cs | 29 + .../Rollback Visualization/TextCanvas.cs.meta | 18 + .../Demos/ColliderRollback/Scripts/Strafe.cs | 35 + .../ColliderRollback/Scripts/Strafe.cs.meta | 18 + .../Demos/ColliderRollback/Textures.meta | 8 + .../ColliderRollback/Textures/Crosshair.png | Bin 0 -> 2673 bytes .../Textures/Crosshair.png.meta | 111 + .../ColliderRollback/Textures/Particles.meta | 8 + .../Textures/Particles/circle.png | Bin 0 -> 6439 bytes .../Textures/Particles/circle.png.meta | 117 + Assets/FishNet/Demos/CustomSyncType.meta | 8 + .../CustomSyncType/Component State Sync.meta | 8 + .../Component State Sync/AMonoScript.cs | 17 + .../Component State Sync/AMonoScript.cs.meta | 18 + .../ComponentStateSync.cs | 154 + .../ComponentStateSync.cs.meta | 18 + .../ComponentSyncStateBehaviour.cs | 53 + .../ComponentSyncStateBehaviour.cs.meta | 18 + .../CustomSyncType/Custom Struct Sync.meta | 8 + .../Custom Struct Sync/StructSyncBehaviour.cs | 41 + .../StructSyncBehaviour.cs.meta | 18 + .../Custom Struct Sync/StructySync.cs | 255 + .../Custom Struct Sync/StructySync.cs.meta | 18 + Assets/FishNet/Demos/FishNet.Demos.asmdef | 17 + .../FishNet/Demos/FishNet.Demos.asmdef.meta | 14 + Assets/FishNet/Demos/HashGrid.meta | 8 + Assets/FishNet/Demos/HashGrid/Prefabs.meta | 8 + .../HashGrid/Prefabs/HashGrid_Moving.prefab | 234 + .../Prefabs/HashGrid_Moving.prefab.meta | 14 + .../HashGrid/Prefabs/HashGrid_Static.prefab | 188 + .../Prefabs/HashGrid_Static.prefab.meta | 14 + Assets/FishNet/Demos/HashGrid/ReadMe.txt | 12 + Assets/FishNet/Demos/HashGrid/ReadMe.txt.meta | 14 + Assets/FishNet/Demos/HashGrid/Scenes.meta | 8 + .../Demos/HashGrid/Scenes/HashGrid_Demo.unity | 652 +++ .../HashGrid/Scenes/HashGrid_Demo.unity.meta | 14 + Assets/FishNet/Demos/HashGrid/Scripts.meta | 8 + .../Demos/HashGrid/Scripts/GridSpawner.cs | 43 + .../HashGrid/Scripts/GridSpawner.cs.meta | 18 + .../Demos/HashGrid/Scripts/MoveRandomly.cs | 86 + .../HashGrid/Scripts/MoveRandomly.cs.meta | 18 + Assets/FishNet/Demos/HashGrid/Textures.meta | 8 + .../Demos/HashGrid/Textures/1x1 Pixel.png | Bin 0 -> 1793 bytes .../HashGrid/Textures/1x1 Pixel.png.meta | 111 + Assets/FishNet/Demos/IntermediateLayer.meta | 8 + .../Demos/IntermediateLayer/Scenes.meta | 8 + .../Scenes/IntermediateLayer.unity | 356 ++ .../Scenes/IntermediateLayer.unity.meta | 14 + .../Demos/IntermediateLayer/Scripts.meta | 8 + .../Scripts/IntermediateLayerCipher.cs | 60 + .../Scripts/IntermediateLayerCipher.cs.meta | 18 + Assets/FishNet/Demos/Prediction.meta | 8 + .../Demos/Prediction/CharacterController.meta | 8 + .../CharacterController Prediction Demo.unity | 1544 ++++++ ...acterController Prediction Demo.unity.meta | 15 + .../CharacterController/Materials.meta | 8 + .../CharacterController/Materials/BlueMat.mat | 138 + .../Materials/BlueMat.mat.meta | 15 + .../CharacterController/Prefabs.meta | 8 + .../CharacterControllerPrediction.prefab | 381 ++ .../CharacterControllerPrediction.prefab.meta | 14 + .../Prediction/CharacterController/ReadMe.txt | 17 + .../CharacterController/ReadMe.txt.meta | 14 + .../CharacterController/Scripts.meta | 8 + .../Scripts/CharacterControllerPrediction.cs | 487 ++ .../CharacterControllerPrediction.cs.meta | 18 + .../Scripts/MovingPlatform.cs | 149 + .../Scripts/MovingPlatform.cs.meta | 18 + .../Scripts/StaminaCanvas.cs | 41 + .../Scripts/StaminaCanvas.cs.meta | 18 + .../CharacterController/Textures.meta | 8 + .../Textures/Stamina_Bar.png | Bin 0 -> 1793 bytes .../Textures/Stamina_Bar.png.meta | 111 + .../FishNet/Demos/Prediction/Rigidbody.meta | 8 + .../Demos/Prediction/Rigidbody/Materials.meta | 8 + .../Rigidbody/Materials/BlueMat.mat | 138 + .../Rigidbody/Materials/BlueMat.mat.meta | 15 + .../Rigidbody/Materials/FrontWheel.mat | 138 + .../Rigidbody/Materials/FrontWheel.mat.meta | 15 + .../Rigidbody/Materials/GreenMat.mat | 140 + .../Rigidbody/Materials/GreenMat.mat.meta | 15 + .../Rigidbody/Materials/OrangeMat.mat | 138 + .../Rigidbody/Materials/OrangeMat.mat.meta | 15 + .../Materials/Slippery.physicMaterial | 14 + .../Materials/Slippery.physicMaterial.meta | 15 + .../Demos/Prediction/Rigidbody/Prefabs.meta | 8 + .../Prefabs/RigidbodyPrediction.prefab | 501 ++ .../Prefabs/RigidbodyPrediction.prefab.meta | 14 + .../Rigidbody/Rigidbody Prediction Demo.unity | 1234 +++++ .../Rigidbody Prediction Demo.unity.meta | 14 + .../Demos/Prediction/Rigidbody/Scripts.meta | 8 + .../Prediction/Rigidbody/Scripts/Boost.cs | 36 + .../Rigidbody/Scripts/Boost.cs.meta | 18 + .../Rigidbody/Scripts/RigidbodyPrediction.cs | 293 + .../Scripts/RigidbodyPrediction.cs.meta | 18 + .../Demos/Prediction/Rigidbody/Textures.meta | 8 + .../Rigidbody/Textures/Stamina_Bar.png | Bin 0 -> 1793 bytes .../Rigidbody/Textures/Stamina_Bar.png.meta | 111 + Assets/FishNet/Demos/Prefabs.meta | 8 + .../Demos/Prefabs/NetworkHudCanvas.prefab | 618 +++ .../Prefabs/NetworkHudCanvas.prefab.meta | 14 + .../Demos/Prefabs/NetworkManager.prefab | 208 + .../Demos/Prefabs/NetworkManager.prefab.meta | 14 + .../Demos/SceneManager (Old Examples).meta | 8 + .../Materials.meta | 8 + .../Materials/Black.mat | 140 + .../Materials/Black.mat.meta | 15 + .../Materials/Blue.mat | 141 + .../Materials/Blue.mat.meta | 15 + .../Materials/Green.mat | 140 + .../Materials/Green.mat.meta | 15 + .../Materials/Red.mat | 141 + .../Materials/Red.mat.meta | 15 + .../SceneManager (Old Examples)/Prefabs.meta | 8 + .../Prefabs/Player.prefab | 368 ++ .../Prefabs/Player.prefab.meta | 14 + .../SceneManager (Old Examples)/Scenes.meta | 8 + .../Scenes/Additive.meta | 8 + .../Scenes/Additive/AdditiveConnection.unity | 291 + .../Additive/AdditiveConnection.unity.meta | 14 + .../Scenes/Additive/AdditiveGlobal.unity | 291 + .../Scenes/Additive/AdditiveGlobal.unity.meta | 14 + .../Scenes/Additive/AdditiveMain.unity | 881 +++ .../Scenes/Additive/AdditiveMain.unity.meta | 14 + .../Scenes/Replace.meta | 8 + .../Scenes/Replace/ReplaceConnection.unity | 666 +++ .../Replace/ReplaceConnection.unity.meta | 14 + .../Scenes/Replace/ReplaceGlobal.unity | 785 +++ .../Scenes/Replace/ReplaceGlobal.unity.meta | 14 + .../Scenes/Replace/ReplaceMain.unity | 867 +++ .../Scenes/Replace/ReplaceMain.unity.meta | 14 + .../Replace/ReplaceMainSettings.lighting | 64 + .../Replace/ReplaceMainSettings.lighting.meta | 15 + .../SceneManager (Old Examples)/Scripts.meta | 8 + .../Scripts/PlayerController.cs | 78 + .../Scripts/PlayerController.cs.meta | 18 + .../Scripts/SceneLoaderExample.cs | 148 + .../Scripts/SceneLoaderExample.cs.meta | 18 + .../Scripts/SceneUnloaderExample.cs | 91 + .../Scripts/SceneUnloaderExample.cs.meta | 18 + Assets/FishNet/Demos/SceneManager.meta | 8 + .../Demos/SceneManager/Additive Scenes.meta | 8 + .../Additive Scenes/Materials.meta | 8 + .../Additive Scenes/Materials/Ground.mat | 138 + .../Additive Scenes/Materials/Ground.mat.meta | 15 + .../Additive Scenes/Materials/Player.mat | 138 + .../Additive Scenes/Materials/Player.mat.meta | 15 + .../Additive Scenes/Materials/TreeGrowth.mat | 138 + .../Materials/TreeGrowth.mat.meta | 15 + .../Additive Scenes/Materials/TreeStump.mat | 138 + .../Materials/TreeStump.mat.meta | 15 + .../SceneManager/Additive Scenes/Models.meta | 8 + .../Additive Scenes/Models/Tree.fbx | Bin 0 -> 17116 bytes .../Additive Scenes/Models/Tree.fbx.meta | 104 + .../Additive Scenes/Observer Conditions.meta | 8 + .../DistanceCondition.asset | 17 + .../DistanceCondition.asset.meta | 15 + .../SceneManager/Additive Scenes/Prefabs.meta | 8 + .../Additive Scenes/Prefabs/Player.prefab | 562 ++ .../Prefabs/Player.prefab.meta | 14 + .../SceneManager/Additive Scenes/ReadMe.txt | 27 + .../Additive Scenes/ReadMe.txt.meta | 14 + .../SceneManager/Additive Scenes/Scenes.meta | 8 + .../Scenes/AdditiveScenes_0.meta | 8 + .../Scenes/AdditiveScenes_0.unity | 3549 ++++++++++++ .../Scenes/AdditiveScenes_0.unity.meta | 14 + .../AdditiveScenes_0/LightingData.asset | Bin 0 -> 20040 bytes .../AdditiveScenes_0/LightingData.asset.meta | 15 + .../AdditiveScenes_0/ReflectionProbe-0.exr | Bin 0 -> 138601 bytes .../ReflectionProbe-0.exr.meta | 99 + .../Scenes/AdditiveScenes_1.unity | 3448 ++++++++++++ .../Scenes/AdditiveScenes_1.unity.meta | 14 + .../Scenes/AdditiveScenes_2.unity | 3448 ++++++++++++ .../Scenes/AdditiveScenes_2.unity.meta | 14 + .../Scenes/AdditiveScenes_3.unity | 3448 ++++++++++++ .../Scenes/AdditiveScenes_3.unity.meta | 14 + .../Scenes/AdditiveScenes_Start.unity | 489 ++ .../Scenes/AdditiveScenes_Start.unity.meta | 14 + .../SceneManager/Additive Scenes/Scripts.meta | 8 + .../Additive Scenes/Scripts/LevelLoader.cs | 96 + .../Scripts/LevelLoader.cs.meta | 18 + .../Additive Scenes/Scripts/Player.cs | 82 + .../Additive Scenes/Scripts/Player.cs.meta | 18 + .../Scripts/ServerScenePrewarmer.cs | 43 + .../Scripts/ServerScenePrewarmer.cs.meta | 18 + .../Additive Scenes/Scripts/Waypoint.cs | 11 + .../Additive Scenes/Scripts/Waypoint.cs.meta | 18 + .../SceneManager Event Diagram.png | Bin 0 -> 45367 bytes .../SceneManager Event Diagram.png.meta | 99 + Assets/FishNet/Demos/Scripts.meta | 8 + .../Demos/Scripts/NetworkHudCanvases.cs | 253 + .../Demos/Scripts/NetworkHudCanvases.cs.meta | 18 + Assets/FishNet/LICENSE.txt | 37 + Assets/FishNet/LICENSE.txt.meta | 14 + Assets/FishNet/Runtime.meta | 8 + Assets/FishNet/Runtime/Authenticating.meta | 8 + .../Runtime/Authenticating/Authenticator.cs | 51 + .../Authenticating/Authenticator.cs.meta | 18 + Assets/FishNet/Runtime/Broadcast.meta | 8 + Assets/FishNet/Runtime/Broadcast/Helping.meta | 8 + .../Broadcast/Helping/BroadcastHelpers.cs | 199 + .../Helping/BroadcastHelpers.cs.meta | 18 + .../FishNet/Runtime/Broadcast/IBroadcast.cs | 8 + .../Runtime/Broadcast/IBroadcast.cs.meta | 18 + Assets/FishNet/Runtime/CodeGenerating.meta | 8 + .../Runtime/CodeGenerating/Attributes.cs | 64 + .../Runtime/CodeGenerating/Attributes.cs.meta | 18 + Assets/FishNet/Runtime/Config.json | 1 + Assets/FishNet/Runtime/Config.json.meta | 14 + Assets/FishNet/Runtime/Connection.meta | 8 + Assets/FishNet/Runtime/Connection/Buffer.cs | 296 + .../FishNet/Runtime/Connection/Buffer.cs.meta | 18 + .../Runtime/Connection/EstimatedTick.cs | 217 + .../Runtime/Connection/EstimatedTick.cs.meta | 18 + .../Connection/NetworkConnection.Buffer.cs | 111 + .../NetworkConnection.Buffer.cs.meta | 18 + .../Connection/NetworkConnection.Observers.cs | 98 + .../NetworkConnection.Observers.cs.meta | 18 + .../Connection/NetworkConnection.PingPong.cs | 105 + .../NetworkConnection.PingPong.cs.meta | 18 + .../NetworkConnection.Prediction.cs | 122 + .../NetworkConnection.Prediction.cs.meta | 18 + .../Connection/NetworkConnection.QOL.cs | 81 + .../Connection/NetworkConnection.QOL.cs.meta | 18 + .../Runtime/Connection/NetworkConnection.cs | 470 ++ .../Connection/NetworkConnection.cs.meta | 18 + .../Runtime/Connection/OldTickOption.cs | 7 + .../Runtime/Connection/OldTickOption.cs.meta | 18 + Assets/FishNet/Runtime/Documenting.meta | 8 + .../FishNet/Runtime/Documenting/Attributes.cs | 8 + .../Runtime/Documenting/Attributes.cs.meta | 18 + Assets/FishNet/Runtime/Editor.meta | 8 + .../FishNet/Runtime/Editor/BuildIdentifier.cs | 48 + .../Runtime/Editor/BuildIdentifier.cs.meta | 18 + .../FishNet/Runtime/Editor/CodeStripping.cs | 116 + .../Runtime/Editor/CodeStripping.cs.meta | 18 + .../FishNet/Runtime/Editor/Configuring.meta | 8 + .../Editor/Configuring/BetaModeMenu.cs | 77 + .../Editor/Configuring/BetaModeMenu.cs.meta | 18 + .../Editor/Configuring/ConfigurationData.cs | 156 + .../Configuring/ConfigurationData.cs.meta | 18 + .../Editor/Configuring/ConfigurationEditor.cs | 100 + .../Configuring/ConfigurationEditor.cs.meta | 18 + .../Runtime/Editor/Configuring/Configuring.cs | 110 + .../Editor/Configuring/Configuring.cs.meta | 18 + .../Editor/Configuring/DelayedEditorTasks.cs | 60 + .../Configuring/DelayedEditorTasks.cs.meta | 18 + .../FIshNetGettingStartedEditor.cs | 139 + .../FIshNetGettingStartedEditor.cs.meta | 18 + .../ReserializeNetworkObjectsEditor.cs | 462 ++ .../ReserializeNetworkObjectsEditor.cs.meta | 18 + .../Configuring/ReviewReminderEditor.cs | 170 + .../Configuring/ReviewReminderEditor.cs.meta | 18 + .../Editor/Configuring/SettingsProvider.cs | 86 + .../Configuring/SettingsProvider.cs.meta | 18 + Assets/FishNet/Runtime/Editor/Constants.cs | 13 + .../FishNet/Runtime/Editor/Constants.cs.meta | 18 + .../Runtime/Editor/DefaultPrefabsFinder.cs | 1 + .../Editor/DefaultPrefabsFinder.cs.meta | 18 + Assets/FishNet/Runtime/Editor/Finding.cs | 199 + Assets/FishNet/Runtime/Editor/Finding.cs.meta | 18 + .../Runtime/Editor/ForceInstallPreventor.cs | 57 + .../Editor/ForceInstallPreventor.cs.meta | 18 + .../Runtime/Editor/NewNetworkBehaviour.meta | 8 + .../CreateNewNetworkBehaviour.cs | 65 + .../CreateNewNetworkBehaviour.cs.meta | 18 + .../NewNetworkBehaviour/SettingsProvider.cs | 103 + .../SettingsProvider.cs.meta | 18 + .../FishNet/Runtime/Editor/PlayModeTracker.cs | 51 + .../Runtime/Editor/PlayModeTracker.cs.meta | 18 + .../Editor/PrefabCollectionGenerator.meta | 8 + .../PrefabCollectionGenerator/Generator.cs | 697 +++ .../Generator.cs.meta | 18 + .../SettingsProvider.cs | 239 + .../SettingsProvider.cs.meta | 18 + .../Runtime/Editor/ScriptingDefines.cs | 98 + .../Runtime/Editor/ScriptingDefines.cs.meta | 18 + Assets/FishNet/Runtime/Editor/Textures.meta | 8 + .../FishNet/Runtime/Editor/Textures/Icon.meta | 8 + .../Editor/Textures/Icon/fishnet_light.png | Bin 0 -> 41016 bytes .../Textures/Icon/fishnet_light.png.meta | 99 + .../FishNet/Runtime/Editor/Textures/UI.meta | 8 + .../Editor/Textures/UI/Client_Text.png | Bin 0 -> 4845 bytes .../Editor/Textures/UI/Client_Text.png.meta | 142 + .../Editor/Textures/UI/FishNet_Text.png | Bin 0 -> 10737 bytes .../Editor/Textures/UI/FishNet_Text.png.meta | 142 + .../Editor/Textures/UI/Logo_With_Text.png | Bin 0 -> 37614 bytes .../Textures/UI/Logo_With_Text.png.meta | 142 + .../Editor/Textures/UI/Server_Text.png | Bin 0 -> 6573 bytes .../Editor/Textures/UI/Server_Text.png.meta | 142 + Assets/FishNet/Runtime/Editor/Upgrading.meta | 8 + .../Editor/Upgrading/UpgradeFromV3ToV4Menu.cs | 1 + .../Upgrading/UpgradeFromV3ToV4Menu.cs.meta | 18 + Assets/FishNet/Runtime/FishNet.Runtime.asmdef | 39 + .../Runtime/FishNet.Runtime.asmdef.meta | 14 + Assets/FishNet/Runtime/Generated.meta | 8 + .../FishNet/Runtime/Generated/Component.meta | 8 + .../Generated/Component/NetworkAnimator.meta | 8 + .../Component/NetworkAnimator/Editor.meta | 8 + .../Editor/NetworkAnimatorEditor.cs | 189 + .../Editor/NetworkAnimatorEditor.cs.meta | 18 + .../NetworkAnimator/NetworkAnimator.cs | 1512 +++++ .../NetworkAnimator/NetworkAnimator.cs.meta | 18 + .../Generated/Component/NetworkTransform.meta | 8 + .../Component/NetworkTransform/Editor.meta | 8 + .../Editor/NetworkTransformEditor.cs | 143 + .../Editor/NetworkTransformEditor.cs.meta | 18 + .../NetworkTransform/NetworkTransform.cs | 2467 +++++++++ .../NetworkTransform/NetworkTransform.cs.meta | 18 + .../NetworkTransform/SynchronizedProperty.cs | 12 + .../SynchronizedProperty.cs.meta | 18 + .../Generated/Component/Prediction.meta | 8 + .../Component/Prediction/NetworkCollider.cs | 382 ++ .../Prediction/NetworkCollider.cs.meta | 18 + .../Component/Prediction/NetworkCollider2D.cs | 505 ++ .../Prediction/NetworkCollider2D.cs.meta | 18 + .../Component/Prediction/NetworkCollision.cs | 14 + .../Prediction/NetworkCollision.cs.meta | 18 + .../Prediction/NetworkCollision2D.cs | 15 + .../Prediction/NetworkCollision2D.cs.meta | 18 + .../Component/Prediction/NetworkTrigger.cs | 14 + .../Prediction/NetworkTrigger.cs.meta | 18 + .../Component/Prediction/NetworkTrigger2D.cs | 13 + .../Prediction/NetworkTrigger2D.cs.meta | 18 + .../Component/Prediction/OfflineRigidbody.cs | 114 + .../Prediction/OfflineRigidbody.cs.meta | 18 + .../Component/Prediction/RigidbodyPauser.cs | 438 ++ .../Prediction/RigidbodyPauser.cs.meta | 18 + .../Component/Prediction/RigidbodyState.cs | 179 + .../Prediction/RigidbodyState.cs.meta | 18 + .../Component/Prediction/RigidbodyType.cs | 12 + .../Prediction/RigidbodyType.cs.meta | 18 + .../Runtime/Generated/Component/Spawning.meta | 8 + .../Component/Spawning/PlayerSpawner.cs | 160 + .../Component/Spawning/PlayerSpawner.cs.meta | 18 + .../Component/Spawning/ServerSpawner.cs | 102 + .../Component/Spawning/ServerSpawner.cs.meta | 18 + .../Generated/Component/TakeOwnership.meta | 8 + .../Component/TakeOwnership/PredictedOwner.cs | 167 + .../TakeOwnership/PredictedOwner.cs.meta | 18 + .../Component/TakeOwnership/PredictedSpawn.cs | 81 + .../TakeOwnership/PredictedSpawn.cs.meta | 18 + .../Generated/Component/TickSmoothing.meta | 8 + .../AdaptiveInterpolationType.cs | 36 + .../AdaptiveInterpolationType.cs.meta | 18 + .../Component/TickSmoothing/Editor.meta | 8 + .../Editor/MovementSettingsDrawer.cs | 51 + .../Editor/MovementSettingsDrawer.cs.meta | 18 + .../Editor/NetworkTickSmootherEditor.cs | 55 + .../Editor/NetworkTickSmootherEditor.cs.meta | 18 + .../Editor/OfflineTickSmootherEditor.cs | 50 + .../Editor/OfflineTickSmootherEditor.cs.meta | 18 + .../TickSmoothing/InitializationSettings.cs | 66 + .../InitializationSettings.cs.meta | 18 + .../TickSmoothing/MovementSettings.cs | 53 + .../TickSmoothing/MovementSettings.cs.meta | 18 + .../TickSmoothing/NetworkTickSmoother.cs | 85 + .../TickSmoothing/NetworkTickSmoother.cs.meta | 18 + .../TickSmoothing/OfflineTickSmoother.cs | 143 + .../TickSmoothing/OfflineTickSmoother.cs.meta | 18 + .../TickSmoothing/TickSmootherController.cs | 298 + .../TickSmootherController.cs.meta | 18 + .../TickSmoothing/UniversalTickSmoother.cs | 937 ++++ .../UniversalTickSmoother.cs.meta | 18 + .../Runtime/Generated/Component/Utility.meta | 8 + .../Component/Utility/BandwidthDisplay.cs | 295 + .../Utility/BandwidthDisplay.cs.meta | 18 + .../Component/Utility/DefaultScene.cs | 250 + .../Component/Utility/DefaultScene.cs.meta | 18 + .../Utility/DetachableNetworkTickSmoother.cs | 254 + .../DetachableNetworkTickSmoother.cs.meta | 18 + .../Generated/Component/Utility/Editor.meta | 8 + .../DetachableNetworkTickSmootherEditor.cs | 78 + ...etachableNetworkTickSmootherEditor.cs.meta | 18 + .../Component/Utility/MonoTickSmoother.cs | 156 + .../Utility/MonoTickSmoother.cs.meta | 18 + .../Component/Utility/PingDisplay.cs | 111 + .../Component/Utility/PingDisplay.cs.meta | 18 + Assets/FishNet/Runtime/InstanceFinder.cs | 317 ++ Assets/FishNet/Runtime/InstanceFinder.cs.meta | 18 + Assets/FishNet/Runtime/Managing.meta | 8 + Assets/FishNet/Runtime/Managing/Client.meta | 8 + .../Client/ClientManager.Broadcast.cs | 103 + .../Client/ClientManager.Broadcast.cs.meta | 18 + .../Managing/Client/ClientManager.QOL.cs | 11 + .../Managing/Client/ClientManager.QOL.cs.meta | 18 + .../Runtime/Managing/Client/ClientManager.cs | 679 +++ .../Managing/Client/ClientManager.cs.meta | 18 + .../Runtime/Managing/Client/Editor.meta | 8 + .../Client/Editor/ClientManagerEditor.cs | 60 + .../Client/Editor/ClientManagerEditor.cs.meta | 18 + .../Runtime/Managing/Client/Object.meta | 8 + .../Client/Object/ClientObjects.RpcLinks.cs | 101 + .../Object/ClientObjects.RpcLinks.cs.meta | 18 + .../Managing/Client/Object/ClientObjects.cs | 721 +++ .../Client/Object/ClientObjects.cs.meta | 18 + .../Managing/Client/Object/ObjectCaching.cs | 659 +++ .../Client/Object/ObjectCaching.cs.meta | 18 + .../FishNet/Runtime/Managing/Debugging.meta | 8 + .../Managing/Debugging/DebugManager.cs | 51 + .../Managing/Debugging/DebugManager.cs.meta | 18 + .../Runtime/Managing/Debugging/Editor.meta | 8 + .../Debugging/Editor/DebugManagerEditor.cs | 70 + .../Editor/DebugManagerEditor.cs.meta | 18 + .../Managing/Debugging/PacketIdHistory.cs | 103 + .../Debugging/PacketIdHistory.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Editor.meta | 8 + .../Managing/Editor/NetworkManagerEditor.cs | 71 + .../Editor/NetworkManagerEditor.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Logging.meta | 8 + .../Runtime/Managing/Logging/Editor.meta | 8 + .../Editor/LevelLoggingConfigurationEditor.cs | 67 + .../LevelLoggingConfigurationEditor.cs.meta | 18 + .../Logging/LevelLoggingConfiguration.cs | 196 + .../Logging/LevelLoggingConfiguration.cs.meta | 18 + .../Managing/Logging/LoggingConfiguration.cs | 63 + .../Logging/LoggingConfiguration.cs.meta | 18 + .../Runtime/Managing/Logging/LoggingType.cs | 25 + .../Managing/Logging/LoggingType.cs.meta | 18 + .../Managing/NetworkManager.Logging.cs | 190 + .../Managing/NetworkManager.Logging.cs.meta | 18 + .../Managing/NetworkManager.ObjectPooling.cs | 145 + .../NetworkManager.ObjectPooling.cs.meta | 18 + .../Runtime/Managing/NetworkManager.Pro.cs | 20 + .../Managing/NetworkManager.Pro.cs.meta | 18 + .../Runtime/Managing/NetworkManager.QOL.cs | 342 ++ .../Managing/NetworkManager.QOL.cs.meta | 18 + .../Runtime/Managing/NetworkManager.cs | 533 ++ .../Runtime/Managing/NetworkManager.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Object.meta | 8 + .../Runtime/Managing/Object/DualPrefab.cs | 16 + .../Managing/Object/DualPrefab.cs.meta | 18 + .../Object/ManagedObjects.Spawning.cs | 525 ++ .../Object/ManagedObjects.Spawning.cs.meta | 18 + .../Runtime/Managing/Object/ManagedObjects.cs | 507 ++ .../Managing/Object/ManagedObjects.cs.meta | 18 + .../Managing/Object/ObjectSpawnType.cs | 45 + .../Managing/Object/ObjectSpawnType.cs.meta | 18 + .../Managing/Object/PrefabObjects.meta | 8 + .../PrefabObjects/DefaultPrefabObjects.cs | 105 + .../DefaultPrefabObjects.cs.meta | 18 + .../Object/PrefabObjects/DualPrefabObjects.cs | 133 + .../PrefabObjects/DualPrefabObjects.cs.meta | 18 + .../Object/PrefabObjects/PrefabObjects.cs | 36 + .../PrefabObjects/PrefabObjects.cs.meta | 18 + .../PrefabObjects/SinglePrefabObjects.cs | 125 + .../PrefabObjects/SinglePrefabObjects.cs.meta | 18 + .../Managing/Object/SpawnParentType.cs | 10 + .../Managing/Object/SpawnParentType.cs.meta | 18 + .../FishNet/Runtime/Managing/Observing.meta | 8 + .../Runtime/Managing/Observing/Editor.meta | 8 + .../Observing/Editor/ObserverManagerEditor.cs | 48 + .../Editor/ObserverManagerEditor.cs.meta | 18 + .../Managing/Observing/ObserverManager.cs | 225 + .../Observing/ObserverManager.cs.meta | 18 + .../FishNet/Runtime/Managing/Prediction.meta | 8 + .../Runtime/Managing/Prediction/Editor.meta | 8 + .../Editor/PredictionManagerEditor.cs | 74 + .../Editor/PredictionManagerEditor.cs.meta | 18 + .../Managing/Prediction/PredictionManager.cs | 800 +++ .../Prediction/PredictionManager.cs.meta | 18 + .../Runtime/Managing/Prediction/StateOrder.cs | 19 + .../Managing/Prediction/StateOrder.cs.meta | 18 + .../Runtime/Managing/RemoteTimeoutType.cs | 21 + .../Managing/RemoteTimeoutType.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Scened.meta | 8 + .../Runtime/Managing/Scened/Broadcast.meta | 8 + .../Scened/Broadcast/SceneBroadcasts.cs | 38 + .../Scened/Broadcast/SceneBroadcasts.cs.meta | 18 + .../Managing/Scened/DefaultSceneProcessor.cs | 177 + .../Scened/DefaultSceneProcessor.cs.meta | 18 + .../Runtime/Managing/Scened/Events.meta | 8 + .../Events/ClientPresenceChangeEventArgs.cs | 34 + .../ClientPresenceChangeEventArgs.cs.meta | 18 + .../Scened/Events/LoadSceneEventArgs.cs | 78 + .../Scened/Events/LoadSceneEventArgs.cs.meta | 18 + .../Scened/Events/UnloadSceneEventArgs.cs | 56 + .../Events/UnloadSceneEventArgs.cs.meta | 18 + .../Managing/Scened/LoadUnloadDatas.meta | 8 + .../Scened/LoadUnloadDatas/LoadOptions.cs | 39 + .../LoadUnloadDatas/LoadOptions.cs.meta | 18 + .../Scened/LoadUnloadDatas/LoadParams.cs | 19 + .../Scened/LoadUnloadDatas/LoadParams.cs.meta | 18 + .../Scened/LoadUnloadDatas/LoadQueueData.cs | 51 + .../LoadUnloadDatas/LoadQueueData.cs.meta | 18 + .../LoadUnloadDatas/PreferredActiveScenes.cs | 36 + .../PreferredActiveScenes.cs.meta | 18 + .../Scened/LoadUnloadDatas/ReplaceOption.cs | 25 + .../LoadUnloadDatas/ReplaceOption.cs.meta | 18 + .../Scened/LoadUnloadDatas/SceneLoadData.cs | 202 + .../LoadUnloadDatas/SceneLoadData.cs.meta | 18 + .../Scened/LoadUnloadDatas/SceneScopeTypes.cs | 18 + .../LoadUnloadDatas/SceneScopeTypes.cs.meta | 18 + .../Scened/LoadUnloadDatas/SceneUnloadData.cs | 124 + .../LoadUnloadDatas/SceneUnloadData.cs.meta | 18 + .../Scened/LoadUnloadDatas/UnloadOptions.cs | 35 + .../LoadUnloadDatas/UnloadOptions.cs.meta | 18 + .../Scened/LoadUnloadDatas/UnloadParams.cs | 19 + .../LoadUnloadDatas/UnloadParams.cs.meta | 18 + .../Scened/LoadUnloadDatas/UnloadQueueData.cs | 53 + .../LoadUnloadDatas/UnloadQueueData.cs.meta | 18 + .../Managing/Scened/SceneLookupData.cs | 410 ++ .../Managing/Scened/SceneLookupData.cs.meta | 18 + .../Runtime/Managing/Scened/SceneManager.cs | 4843 +++++++++++++++++ .../Managing/Scened/SceneManager.cs.meta | 18 + .../Managing/Scened/SceneProcessorBase.cs | 163 + .../Scened/SceneProcessorBase.cs.meta | 18 + .../Runtime/Managing/Scened/SceneSpawner.cs | 356 ++ .../Managing/Scened/SceneSpawner.cs.meta | 18 + .../Runtime/Managing/Scened/UnloadedScene.cs | 39 + .../Managing/Scened/UnloadedScene.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Server.meta | 8 + .../Server/ClientConnectionBroadcast.cs | 42 + .../Server/ClientConnectionBroadcast.cs.meta | 18 + .../Runtime/Managing/Server/Editor.meta | 8 + .../Server/Editor/ServerManagerEditor.cs | 96 + .../Server/Editor/ServerManagerEditor.cs.meta | 18 + .../Runtime/Managing/Server/KickReasons.cs | 36 + .../Managing/Server/KickReasons.cs.meta | 18 + .../Runtime/Managing/Server/Object.meta | 8 + .../Server/Object/ServerObjects.Observers.cs | 460 ++ .../Object/ServerObjects.Observers.cs.meta | 18 + .../Server/Object/ServerObjects.Parsing.cs | 42 + .../Object/ServerObjects.Parsing.cs.meta | 18 + .../Managing/Server/Object/ServerObjects.cs | 1162 ++++ .../Server/Object/ServerObjects.cs.meta | 18 + .../Server/ServerManager.Broadcast.cs | 352 ++ .../Server/ServerManager.Broadcast.cs.meta | 18 + .../Managing/Server/ServerManager.QOL.cs | 224 + .../Managing/Server/ServerManager.QOL.cs.meta | 18 + .../Managing/Server/ServerManager.RpcLinks.cs | 71 + .../Server/ServerManager.RpcLinks.cs.meta | 18 + .../Runtime/Managing/Server/ServerManager.cs | 968 ++++ .../Managing/Server/ServerManager.cs.meta | 18 + .../FishNet/Runtime/Managing/Statistic.meta | 8 + .../Managing/Statistic/NetworkTrafficArgs.cs | 21 + .../Statistic/NetworkTrafficArgs.cs.meta | 18 + .../Statistic/NetworkTrafficStatistics.cs | 206 + .../NetworkTrafficStatistics.cs.meta | 18 + .../Managing/Statistic/StatisticsManager.cs | 22 + .../Statistic/StatisticsManager.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Timing.meta | 8 + .../Runtime/Managing/Timing/Editor.meta | 8 + .../Timing/Editor/TimeManagerEditor.cs | 81 + .../Timing/Editor/TimeManagerEditor.cs.meta | 18 + .../Runtime/Managing/Timing/MovingAverage.cs | 101 + .../Managing/Timing/MovingAverage.cs.meta | 18 + .../Runtime/Managing/Timing/PhysicsMode.cs | 23 + .../Managing/Timing/PhysicsMode.cs.meta | 18 + .../Runtime/Managing/Timing/PreciseTick.cs | 195 + .../Managing/Timing/PreciseTick.cs.meta | 18 + .../Runtime/Managing/Timing/TickRounding.cs | 23 + .../Managing/Timing/TickRounding.cs.meta | 18 + .../Runtime/Managing/Timing/TickType.cs | 10 + .../Runtime/Managing/Timing/TickType.cs.meta | 18 + .../Runtime/Managing/Timing/TimeManager.cs | 1183 ++++ .../Managing/Timing/TimeManager.cs.meta | 18 + .../Runtime/Managing/Transporting.meta | 8 + .../Transporting/IntermediateLayer.cs | 39 + .../Transporting/IntermediateLayer.cs.meta | 18 + .../Managing/Transporting/LatencySimulator.cs | 380 ++ .../Transporting/LatencySimulator.cs.meta | 18 + .../Managing/Transporting/SplitReader.cs | 99 + .../Managing/Transporting/SplitReader.cs.meta | 18 + .../Transporting/TransportManager.QOL.cs | 103 + .../Transporting/TransportManager.QOL.cs.meta | 18 + .../Managing/Transporting/TransportManager.cs | 921 ++++ .../Transporting/TransportManager.cs.meta | 18 + Assets/FishNet/Runtime/Managing/Utility.meta | 8 + .../Runtime/Managing/Utility/Utility.cs | 54 + .../Runtime/Managing/Utility/Utility.cs.meta | 18 + Assets/FishNet/Runtime/Object.meta | 8 + .../Object/ChangedTransformProperties.cs | 1 + .../Object/ChangedTransformProperties.cs.meta | 18 + Assets/FishNet/Runtime/Object/Editor.meta | 8 + .../Object/Editor/NetworkObjectEditor.cs | 187 + .../Object/Editor/NetworkObjectEditor.cs.meta | 18 + Assets/FishNet/Runtime/Object/Helping.meta | 8 + .../FishNet/Runtime/Object/Helping/RpcLink.cs | 40 + .../Runtime/Object/Helping/RpcLink.cs.meta | 18 + .../FishNet/Runtime/Object/Helping/RpcType.cs | 13 + .../Runtime/Object/Helping/RpcType.cs.meta | 18 + .../Runtime/Object/Helping/StaticShortcuts.cs | 49 + .../Object/Helping/StaticShortcuts.cs.meta | 18 + .../Runtime/Object/NetworkBehaviour.meta | 8 + .../Object/NetworkBehaviour/Attributes.cs | 202 + .../NetworkBehaviour/Attributes.cs.meta | 18 + .../Object/NetworkBehaviour/Delegates.cs | 13 + .../Object/NetworkBehaviour/Delegates.cs.meta | 18 + .../NetworkBehaviour/EmptyNetworkBehaviour.cs | 13 + .../EmptyNetworkBehaviour.cs.meta | 18 + .../NetworkBehaviour.Callbacks.cs | 211 + .../NetworkBehaviour.Callbacks.cs.meta | 18 + .../NetworkBehaviour.Logging.cs | 23 + .../NetworkBehaviour.Logging.cs.meta | 18 + .../NetworkBehaviour.Prediction.cs | 1515 ++++++ .../NetworkBehaviour.Prediction.cs.meta | 18 + .../NetworkBehaviour/NetworkBehaviour.QOL.cs | 335 ++ .../NetworkBehaviour.QOL.cs.meta | 18 + .../NetworkBehaviour.RPCLinks.cs | 166 + .../NetworkBehaviour.RPCLinks.cs.meta | 18 + .../NetworkBehaviour/NetworkBehaviour.RPCs.cs | 519 ++ .../NetworkBehaviour.RPCs.cs.meta | 18 + .../NetworkBehaviour.SyncTypes.cs | 522 ++ .../NetworkBehaviour.SyncTypes.cs.meta | 18 + .../NetworkBehaviour/NetworkBehaviour.cs | 262 + .../NetworkBehaviour/NetworkBehaviour.cs.meta | 18 + .../Object/NetworkBehaviour/RpcLinkType.cs | 31 + .../NetworkBehaviour/RpcLinkType.cs.meta | 18 + .../NetworkBehaviour/SyncTypeWriteType.cs | 12 + .../SyncTypeWriteType.cs.meta | 18 + .../FishNet/Runtime/Object/NetworkObject.meta | 8 + .../NetworkObject/NetworkObject.Broadcast.cs | 31 + .../NetworkObject.Broadcast.cs.meta | 18 + .../NetworkObject/NetworkObject.Callbacks.cs | 182 + .../NetworkObject.Callbacks.cs.meta | 18 + .../NetworkObject/NetworkObject.Observers.cs | 296 + .../NetworkObject.Observers.cs.meta | 18 + .../NetworkObject/NetworkObject.Prediction.cs | 464 ++ .../NetworkObject.Prediction.cs.meta | 18 + .../Object/NetworkObject/NetworkObject.QOL.cs | 408 ++ .../NetworkObject/NetworkObject.QOL.cs.meta | 18 + .../NetworkObject.ReferenceIds.cs | 1 + .../NetworkObject.ReferenceIds.cs.meta | 18 + .../NetworkObject/NetworkObject.RpcLinks.cs | 32 + .../NetworkObject.RpcLinks.cs.meta | 18 + .../NetworkObject/NetworkObject.Serialized.cs | 238 + .../NetworkObject.Serialized.cs.meta | 18 + .../NetworkObject/NetworkObject.SyncTypes.cs | 32 + .../NetworkObject.SyncTypes.cs.meta | 18 + .../Object/NetworkObject/NetworkObject.cs | 1431 +++++ .../NetworkObject/NetworkObject.cs.meta | 18 + .../Object/NetworkObject/NetworkObjectData.cs | 80 + .../NetworkObject/NetworkObjectData.cs.meta | 18 + Assets/FishNet/Runtime/Object/Prediction.meta | 8 + .../Runtime/Object/Prediction/Attributes.cs | 16 + .../Object/Prediction/Attributes.cs.meta | 18 + .../Runtime/Object/Prediction/Delegates.cs | 20 + .../Object/Prediction/Delegates.cs.meta | 18 + .../Runtime/Object/Prediction/Interfaces.cs | 40 + .../Object/Prediction/Interfaces.cs.meta | 18 + .../Object/Prediction/LocalReconcile.cs | 47 + .../Object/Prediction/LocalReconcile.cs.meta | 18 + .../Runtime/Object/Prediction/MoveRates.cs | 440 ++ .../Object/Prediction/MoveRates.cs.meta | 18 + .../Object/Prediction/PredictionRigidbody.cs | 441 ++ .../Prediction/PredictionRigidbody.cs.meta | 18 + .../Prediction/PredictionRigidbody2D.cs | 368 ++ .../Prediction/PredictionRigidbody2D.cs.meta | 18 + .../Prediction/ReplicateDataContainer.cs | 102 + .../Prediction/ReplicateDataContainer.cs.meta | 18 + .../Object/Prediction/ReplicateState.cs | 175 + .../Object/Prediction/ReplicateState.cs.meta | 18 + .../FishNet/Runtime/Object/Synchronizing.meta | 8 + .../Runtime/Object/Synchronizing/Beta.meta | 8 + .../Synchronizing/Beta/SyncDictionary.cs | 631 +++ .../Synchronizing/Beta/SyncDictionary.cs.meta | 18 + .../Object/Synchronizing/Beta/SyncHashset.cs | 627 +++ .../Synchronizing/Beta/SyncHashset.cs.meta | 18 + .../Object/Synchronizing/Beta/SyncList.cs | 746 +++ .../Synchronizing/Beta/SyncList.cs.meta | 18 + .../Synchronizing/Beta/SyncStopwatch.cs | 387 ++ .../Synchronizing/Beta/SyncStopwatch.cs.meta | 18 + .../Object/Synchronizing/Beta/SyncTimer.cs | 485 ++ .../Synchronizing/Beta/SyncTimer.cs.meta | 18 + .../Object/Synchronizing/Beta/SyncVar.cs | 475 ++ .../Object/Synchronizing/Beta/SyncVar.cs.meta | 18 + .../Object/Synchronizing/ICustomSync.cs | 19 + .../Object/Synchronizing/ICustomSync.cs.meta | 18 + .../Synchronizing/InterpolatedSyncVars.cs | 159 + .../InterpolatedSyncVars.cs.meta | 18 + .../MissingObjectPacketLength.cs | 11 + .../MissingObjectPacketLength.cs.meta | 18 + .../Object/Synchronizing/ReadPermissions.cs | 21 + .../Synchronizing/ReadPermissions.cs.meta | 18 + .../Runtime/Object/Synchronizing/SyncBase.cs | 552 ++ .../Object/Synchronizing/SyncBase.cs.meta | 18 + .../Object/Synchronizing/SyncDictionary.cs | 642 +++ .../Synchronizing/SyncDictionary.cs.meta | 18 + .../Synchronizing/SyncDictionaryOperation.cs | 31 + .../SyncDictionaryOperation.cs.meta | 18 + .../Synchronizing/SyncHashSetOperation.cs | 31 + .../SyncHashSetOperation.cs.meta | 18 + .../Object/Synchronizing/SyncHashset.cs | 657 +++ .../Object/Synchronizing/SyncHashset.cs.meta | 18 + .../Runtime/Object/Synchronizing/SyncList.cs | 775 +++ .../Object/Synchronizing/SyncList.cs.meta | 18 + .../Object/Synchronizing/SyncListOperation.cs | 35 + .../Synchronizing/SyncListOperation.cs.meta | 18 + .../Object/Synchronizing/SyncStopwatch.cs | 390 ++ .../Synchronizing/SyncStopwatch.cs.meta | 18 + .../Synchronizing/SyncStopwatchOperation.cs | 36 + .../SyncStopwatchOperation.cs.meta | 18 + .../Runtime/Object/Synchronizing/SyncTimer.cs | 462 ++ .../Object/Synchronizing/SyncTimer.cs.meta | 18 + .../Synchronizing/SyncTimerOperation.cs | 38 + .../Synchronizing/SyncTimerOperation.cs.meta | 18 + .../Object/Synchronizing/SyncTypeSetting.cs | 82 + .../Synchronizing/SyncTypeSetting.cs.meta | 18 + .../Object/Synchronizing/SyncTypeWriteFlag.cs | 15 + .../Synchronizing/SyncTypeWriteFlag.cs.meta | 18 + .../Runtime/Object/Synchronizing/SyncVar.cs | 463 ++ .../Object/Synchronizing/SyncVar.cs.meta | 18 + .../Object/Synchronizing/WritePermissions.cs | 17 + .../Synchronizing/WritePermissions.cs.meta | 18 + .../Runtime/Object/TransformProperties.cs | 221 + .../Object/TransformProperties.cs.meta | 18 + .../Runtime/Object/TransformPropertiesFlag.cs | 26 + .../Object/TransformPropertiesFlag.cs.meta | 18 + Assets/FishNet/Runtime/Observing.meta | 8 + .../FishNet/Runtime/Observing/Conditions.meta | 8 + .../Observing/Conditions/DistanceCondition.cs | 106 + .../Conditions/DistanceCondition.cs.meta | 18 + .../Observing/Conditions/GridCondition.meta | 8 + .../GridCondition/GridCondition.asset | 15 + .../GridCondition/GridCondition.asset.meta | 15 + .../Conditions/GridCondition/GridCondition.cs | 35 + .../GridCondition/GridCondition.cs.meta | 18 + .../Conditions/GridCondition/HashGrid.cs | 276 + .../Conditions/GridCondition/HashGrid.cs.meta | 18 + .../Observing/Conditions/HostOnlyCondition.cs | 25 + .../Conditions/HostOnlyCondition.cs.meta | 18 + .../Observing/Conditions/MatchCondition.cs | 770 +++ .../Conditions/MatchCondition.cs.meta | 18 + .../Conditions/OwnerOnlyCondition.cs | 35 + .../Conditions/OwnerOnlyCondition.cs.meta | 18 + .../Observing/Conditions/SceneCondition.cs | 39 + .../Conditions/SceneCondition.cs.meta | 18 + .../Conditions/ScriptableObjects.meta | 8 + .../ScriptableObjects/DistanceCondition.asset | 18 + .../DistanceCondition.asset.meta | 15 + .../ScriptableObjects/HostOnlyCondition.asset | 15 + .../HostOnlyCondition.asset.meta | 15 + .../ScriptableObjects/MatchCondition.asset | 15 + .../MatchCondition.asset.meta | 15 + .../OwnerOnlyCondition.asset | 15 + .../OwnerOnlyCondition.asset.meta | 15 + .../ScriptableObjects/SceneCondition.asset | 17 + .../SceneCondition.asset.meta | 15 + .../Observing/HostVisibilityUpdateTypes.cs | 17 + .../HostVisibilityUpdateTypes.cs.meta | 18 + .../Observing/NetworkObserver.Rework.cs | 620 +++ .../Observing/NetworkObserver.Rework.cs.meta | 18 + .../Runtime/Observing/NetworkObserver.cs | 499 ++ .../Runtime/Observing/NetworkObserver.cs.meta | 18 + .../Runtime/Observing/ObserverCondition.cs | 109 + .../Observing/ObserverCondition.cs.meta | 18 + .../Observing/ObserverConditionType.cs | 19 + .../Observing/ObserverConditionType.cs.meta | 18 + .../Runtime/Observing/ObserverStateChange.cs | 12 + .../Observing/ObserverStateChange.cs.meta | 18 + Assets/FishNet/Runtime/Plugins.meta | 8 + .../FishNet/Runtime/Plugins/CodeAnalysis.meta | 8 + .../FishNet.CodeAnalysis.Analyzers.dll | Bin 0 -> 21504 bytes .../FishNet.CodeAnalysis.Analyzers.dll.meta | 79 + .../CodeAnalysis/FishNet.CodeAnalysis.dll | Bin 0 -> 5120 bytes .../FishNet.CodeAnalysis.dll.meta | 40 + .../Runtime/Plugins/CodeAnalysis/LICENSE.txt | 21 + .../Plugins/CodeAnalysis/LICENSE.txt.meta | 14 + .../Runtime/Plugins/CodeAnalysis/README.txt | 2 + .../Plugins/CodeAnalysis/README.txt.meta | 14 + .../Runtime/Plugins/ColliderRollback.meta | 8 + .../Plugins/ColliderRollback/LICENSE.txt | 1 + .../Plugins/ColliderRollback/LICENSE.txt.meta | 14 + .../Plugins/ColliderRollback/Scripts.meta | 8 + .../Scripts/ColliderRollback.Types.cs | 24 + .../Scripts/ColliderRollback.Types.cs.meta | 18 + .../Scripts/ColliderRollback.cs | 40 + .../Scripts/ColliderRollback.cs.meta | 18 + .../Scripts/ColliderRollbackEditor.cs | 59 + .../Scripts/ColliderRollbackEditor.cs.meta | 18 + .../Scripts/RollbackManager.cs | 224 + .../Scripts/RollbackManager.cs.meta | 18 + .../Scripts/RollbackPhysicsType.cs | 13 + .../Scripts/RollbackPhysicsType.cs.meta | 18 + Assets/FishNet/Runtime/Plugins/GameKit.meta | 8 + .../Runtime/Plugins/GameKit/Dependencies.meta | 8 + .../Plugins/GameKit/Dependencies/Editor.meta | 8 + .../Editor/PlaceholderAttributes.cs | 43 + .../Editor/PlaceholderAttributes.cs.meta | 18 + .../Dependencies/GameKit.Dependencies.asmdef | 22 + .../GameKit.Dependencies.asmdef.meta | 14 + .../GameKit/Dependencies/Utilities.meta | 8 + .../Utilities/ApplicationState.cs | 84 + .../Utilities/ApplicationState.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Arrays.cs | 147 + .../Dependencies/Utilities/Arrays.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Bools.cs | 15 + .../Dependencies/Utilities/Bools.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Bytes.cs | 69 + .../Dependencies/Utilities/Bytes.cs.meta | 20 + .../Dependencies/Utilities/CanvasGroups.cs | 93 + .../Utilities/CanvasGroups.cs.meta | 18 + .../Dependencies/Utilities/Colliders.cs | 162 + .../Dependencies/Utilities/Colliders.cs.meta | 18 + .../Dependencies/Utilities/Dictionaries.cs | 63 + .../Utilities/Dictionaries.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Disks.cs | 102 + .../Dependencies/Utilities/Disks.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Editing.cs | 12 + .../Dependencies/Utilities/Editing.cs.meta | 20 + .../Dependencies/Utilities/EditorTools.cs | 294 + .../Utilities/EditorTools.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Enums.cs | 133 + .../Dependencies/Utilities/Enums.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Floats.cs | 232 + .../Dependencies/Utilities/Floats.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Hashing.cs | 85 + .../Dependencies/Utilities/Hashing.cs.meta | 18 + .../GameKit/Dependencies/Utilities/IOs.cs | 61 + .../Dependencies/Utilities/IOs.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Ints.cs | 91 + .../Dependencies/Utilities/Ints.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Layers.cs | 107 + .../Dependencies/Utilities/Layers.cs.meta | 20 + .../Dependencies/Utilities/LayoutGroups.cs | 20 + .../Utilities/LayoutGroups.cs.meta | 18 + .../Dependencies/Utilities/Materials.cs | 36 + .../Dependencies/Utilities/Materials.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Maths.cs | 44 + .../Dependencies/Utilities/Maths.cs.meta | 18 + .../Dependencies/Utilities/NewInput.cs | 208 + .../Dependencies/Utilities/NewInput.cs.meta | 18 + .../Dependencies/Utilities/ObjectCaching.cs | 771 +++ .../Utilities/ObjectCaching.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Objects.cs | 67 + .../Dependencies/Utilities/Objects.cs.meta | 18 + .../Dependencies/Utilities/Particles.cs | 109 + .../Dependencies/Utilities/Particles.cs.meta | 18 + .../Dependencies/Utilities/Quaternions.cs | 64 + .../Utilities/Quaternions.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Strings.cs | 128 + .../Dependencies/Utilities/Strings.cs.meta | 20 + .../Dependencies/Utilities/Transforms.cs | 195 + .../Dependencies/Utilities/Transforms.cs.meta | 18 + .../GameKit/Dependencies/Utilities/Types.meta | 8 + .../Utilities/Types/BasicQueue.cs | 243 + .../Utilities/Types/BasicQueue.cs.meta | 18 + .../Dependencies/Utilities/Types/ByteRange.cs | 48 + .../Utilities/Types/ByteRange.cs.meta | 18 + .../Utilities/Types/CanvasGroupFader.cs | 237 + .../Utilities/Types/CanvasGroupFader.cs.meta | 18 + .../Utilities/Types/Canvases.meta | 8 + .../Utilities/Types/Canvases/ButtonData.cs | 59 + .../Types/Canvases/ButtonData.cs.meta | 18 + .../Utilities/Types/Canvases/CanvasTracker.cs | 121 + .../Types/Canvases/CanvasTracker.cs.meta | 18 + .../Types/Canvases/FloatingContainer.cs | 234 + .../Types/Canvases/FloatingContainer.cs.meta | 18 + .../Utilities/Types/Canvases/FloatingImage.cs | 36 + .../Types/Canvases/FloatingImage.cs.meta | 18 + .../Types/Canvases/FloatingOptions.cs | 44 + .../Types/Canvases/FloatingOptions.cs.meta | 18 + .../Types/Canvases/ImageButtonData.cs | 37 + .../Types/Canvases/ImageButtonData.cs.meta | 18 + .../Types/Canvases/ImageOptionButton.cs | 29 + .../Types/Canvases/ImageOptionButton.cs.meta | 18 + .../Utilities/Types/Canvases/OptionButton.cs | 36 + .../Types/Canvases/OptionButton.cs.meta | 18 + .../Types/Canvases/RectTransformResizer.cs | 111 + .../Canvases/RectTransformResizer.cs.meta | 18 + .../Types/Canvases/ResizableContainer.cs | 53 + .../Types/Canvases/ResizableContainer.cs.meta | 18 + .../Dependencies/Utilities/Types/DDOL.cs | 41 + .../Dependencies/Utilities/Types/DDOL.cs.meta | 20 + .../Dependencies/Utilities/Types/Editor.meta | 8 + .../Utilities/Types/Editor/SceneDrawer.cs | 51 + .../Types/Editor/SceneDrawer.cs.meta | 18 + .../Utilities/Types/FloatRange.cs | 40 + .../Utilities/Types/FloatRange.cs.meta | 18 + .../Utilities/Types/FloatRange2D.cs | 58 + .../Utilities/Types/FloatRange2D.cs.meta | 18 + .../Utilities/Types/IOrderable.cs | 10 + .../Utilities/Types/IOrderable.cs.meta | 18 + .../Dependencies/Utilities/Types/IntRange.cs | 55 + .../Utilities/Types/IntRange.cs.meta | 18 + .../Utilities/Types/ObjectPooling.meta | 8 + .../Types/ObjectPooling/CHANGELOG.txt | 3 + .../Types/ObjectPooling/CHANGELOG.txt.meta | 14 + .../Utilities/Types/ObjectPooling/Demo.meta | 8 + .../Types/ObjectPooling/Demo/Prefabs.meta | 8 + .../Demo/Prefabs/Projectile.prefab | 1616 ++++++ .../Demo/Prefabs/Projectile.prefab.meta | 14 + .../Types/ObjectPooling/Demo/Scenes.meta | 8 + .../Demo/Scenes/ObjectPool.unity | 398 ++ .../Demo/Scenes/ObjectPool.unity.meta | 14 + .../Demo/Scenes/ObjectPoolSettings.lighting | 64 + .../Scenes/ObjectPoolSettings.lighting.meta | 15 + .../Types/ObjectPooling/Demo/Scripts.meta | 8 + .../ObjectPooling/Demo/Scripts/Projectile.cs | 87 + .../Demo/Scripts/Projectile.cs.meta | 18 + .../Demo/Scripts/ProjectileSpawner.cs | 34 + .../Demo/Scripts/ProjectileSpawner.cs.meta | 18 + .../Utilities/Types/ObjectPooling/README.txt | 18 + .../Types/ObjectPooling/README.txt.meta | 14 + .../Types/ObjectPooling/Scripts.meta | 8 + .../Types/ObjectPooling/Scripts/ListStack.cs | 153 + .../ObjectPooling/Scripts/ListStack.cs.meta | 18 + .../Types/ObjectPooling/Scripts/ObjectPool.cs | 633 +++ .../ObjectPooling/Scripts/ObjectPool.cs.meta | 18 + .../Types/ObjectPooling/Scripts/PoolData.cs | 62 + .../ObjectPooling/Scripts/PoolData.cs.meta | 20 + .../Utilities/Types/PointerMonoBehaviour.cs | 31 + .../Types/PointerMonoBehaviour.cs.meta | 18 + .../Utilities/Types/ResettableRingBuffer.cs | 572 ++ .../Types/ResettableRingBuffer.cs.meta | 18 + .../Utilities/Types/RingBuffer.cs | 468 ++ .../Utilities/Types/RingBuffer.cs.meta | 18 + .../Utilities/Types/SceneAttribute.cs | 12 + .../Utilities/Types/SceneAttribute.cs.meta | 18 + .../Utilities/Types/ScrollbarValueSetter.cs | 64 + .../Types/ScrollbarValueSetter.cs.meta | 18 + .../Types/SingletonScriptableObject.cs | 41 + .../Types/SingletonScriptableObject.cs.meta | 18 + .../Utilities/Types/SmoothCameraTarget.cs | 43 + .../Types/SmoothCameraTarget.cs.meta | 20 + .../Utilities/Types/TimedOperation.cs | 99 + .../Utilities/Types/TimedOperation.cs.meta | 18 + .../Dependencies/Utilities/Types/UIntRange.cs | 49 + .../Utilities/Types/UIntRange.cs.meta | 18 + .../Utilities/Types/Vector2Range.cs | 43 + .../Utilities/Types/Vector2Range.cs.meta | 18 + .../GameKit/Dependencies/Utilities/UInts.cs | 69 + .../Dependencies/Utilities/UInts.cs.meta | 20 + .../GameKit/Dependencies/Utilities/Vectors.cs | 251 + .../Dependencies/Utilities/Vectors.cs.meta | 20 + .../Dependencies/Utilities/WeightedRandom.cs | 95 + .../Utilities/WeightedRandom.cs.meta | 18 + Assets/FishNet/Runtime/Plugins/Yak.meta | 8 + Assets/FishNet/Runtime/Plugins/Yak/Core.meta | 8 + .../Runtime/Plugins/Yak/Core/ClientSocket.cs | 57 + .../Plugins/Yak/Core/ClientSocket.cs.meta | 18 + .../Runtime/Plugins/Yak/Core/CommonSocket.cs | 50 + .../Plugins/Yak/Core/CommonSocket.cs.meta | 18 + .../Runtime/Plugins/Yak/Core/LocalPacket.cs | 11 + .../Plugins/Yak/Core/LocalPacket.cs.meta | 18 + .../Runtime/Plugins/Yak/Core/ServerSocket.cs | 83 + .../Plugins/Yak/Core/ServerSocket.cs.meta | 18 + Assets/FishNet/Runtime/Plugins/Yak/Yak.cs | 317 ++ .../FishNet/Runtime/Plugins/Yak/Yak.cs.meta | 18 + Assets/FishNet/Runtime/Serializing.meta | 8 + .../Runtime/Serializing/AutoPackType.cs | 21 + .../Runtime/Serializing/AutoPackType.cs.meta | 18 + .../Serializing/DeltaSerializerOption.cs | 16 + .../Serializing/DeltaSerializerOption.cs.meta | 18 + .../FishNet/Runtime/Serializing/DeltaTypes.cs | 186 + .../Runtime/Serializing/DeltaTypes.cs.meta | 18 + .../Runtime/Serializing/GenericDeltaReader.cs | 42 + .../Serializing/GenericDeltaReader.cs.meta | 18 + .../Runtime/Serializing/GenericDeltaWriter.cs | 43 + .../Serializing/GenericDeltaWriter.cs.meta | 18 + .../Runtime/Serializing/GenericReader.cs | 42 + .../Runtime/Serializing/GenericReader.cs.meta | 18 + .../Runtime/Serializing/GenericWriter.cs | 40 + .../Runtime/Serializing/GenericWriter.cs.meta | 18 + .../FishNet/Runtime/Serializing/Helping.meta | 8 + .../Runtime/Serializing/Helping/Broadcasts.cs | 223 + .../Serializing/Helping/Broadcasts.cs.meta | 18 + .../Runtime/Serializing/Helping/Comparers.cs | 66 + .../Serializing/Helping/Comparers.cs.meta | 18 + .../Serializing/Helping/Quaternion32.cs | 201 + .../Serializing/Helping/Quaternion32.cs.meta | 18 + .../Serializing/Helping/Quaternion64.cs | 192 + .../Serializing/Helping/Quaternion64.cs.meta | 18 + .../Helping/QuaternionConverter.cs | 11 + .../Helping/QuaternionConverter.cs.meta | 18 + .../QuaternionDeltaPrecisionCompression.cs | 280 + ...uaternionDeltaPrecisionCompression.cs.meta | 18 + .../Helping/QuaternionPrecisionCompression.cs | 212 + .../QuaternionPrecisionCompression.cs.meta | 18 + .../Serializing/Helping/ReservedWriters.cs | 166 + .../Helping/ReservedWriters.cs.meta | 18 + .../Serializing/Helping/ValueConversions.cs | 38 + .../Helping/ValueConversions.cs.meta | 18 + .../Runtime/Serializing/Reader.Delta.cs | 468 ++ .../Runtime/Serializing/Reader.Delta.cs.meta | 18 + Assets/FishNet/Runtime/Serializing/Reader.cs | 1606 ++++++ .../Runtime/Serializing/Reader.cs.meta | 18 + .../Runtime/Serializing/ReaderExtensions.cs | 18 + .../Serializing/ReaderExtensions.cs.meta | 18 + .../FishNet/Runtime/Serializing/ReaderPool.cs | 82 + .../Runtime/Serializing/ReaderPool.cs.meta | 18 + .../Runtime/Serializing/ReaderStatics.cs | 1 + .../Runtime/Serializing/ReaderStatics.cs.meta | 18 + .../Runtime/Serializing/SceneComparer.cs | 18 + .../Runtime/Serializing/SceneComparer.cs.meta | 18 + .../Runtime/Serializing/SubStream.meta | 8 + .../Serializing/SubStream/Reader.SubStream.cs | 26 + .../SubStream/Reader.SubStream.cs.meta | 18 + .../Serializing/SubStream/SubStream.cs | 241 + .../Serializing/SubStream/SubStream.cs.meta | 18 + .../Serializing/SubStream/Writer.SubStream.cs | 28 + .../SubStream/Writer.SubStream.cs.meta | 18 + .../Serializing/TransformPackingData.cs | 10 + .../Serializing/TransformPackingData.cs.meta | 18 + .../Runtime/Serializing/UnityMathmatics.meta | 8 + .../Serializers.UnityMathmaticsBoolean.cs | 470 ++ ...Serializers.UnityMathmaticsBoolean.cs.meta | 18 + .../Serializers.UnityMathmaticsDouble.cs | 194 + .../Serializers.UnityMathmaticsDouble.cs.meta | 18 + .../Serializers.UnityMathmaticsFloat.cs | 192 + .../Serializers.UnityMathmaticsFloat.cs.meta | 18 + .../Serializers.UnityMathmaticsHalf.cs | 84 + .../Serializers.UnityMathmaticsHalf.cs.meta | 18 + .../Serializers.UnityMathmaticsInt.cs | 190 + .../Serializers.UnityMathmaticsInt.cs.meta | 18 + .../Serializers.UnityMathmaticsMisc.cs | 86 + .../Serializers.UnityMathmaticsMisc.cs.meta | 18 + .../Serializers.UnityMathmaticsUInt.cs | 190 + .../Serializers.UnityMathmaticsUInt.cs.meta | 18 + .../Runtime/Serializing/Writer.Delta.cs | 830 +++ .../Runtime/Serializing/Writer.Delta.cs.meta | 18 + Assets/FishNet/Runtime/Serializing/Writer.cs | 1298 +++++ .../Runtime/Serializing/Writer.cs.meta | 18 + .../Runtime/Serializing/WriterExtensions.cs | 90 + .../Serializing/WriterExtensions.cs.meta | 18 + .../FishNet/Runtime/Serializing/WriterPool.cs | 210 + .../Runtime/Serializing/WriterPool.cs.meta | 18 + .../Runtime/Serializing/WriterStatics.cs | 1 + .../Runtime/Serializing/WriterStatics.cs.meta | 18 + Assets/FishNet/Runtime/Transporting.meta | 8 + .../FishNet/Runtime/Transporting/Channels.cs | 19 + .../Runtime/Transporting/Channels.cs.meta | 18 + .../Runtime/Transporting/ConnectionStates.cs | 59 + .../Transporting/ConnectionStates.cs.meta | 18 + .../Runtime/Transporting/EventStructures.cs | 172 + .../Transporting/EventStructures.cs.meta | 18 + .../Runtime/Transporting/IPAddressType.cs | 19 + .../Transporting/IPAddressType.cs.meta | 18 + .../Runtime/Transporting/NetworkReaderLoop.cs | 33 + .../Transporting/NetworkReaderLoop.cs.meta | 18 + .../Runtime/Transporting/NetworkWriterLoop.cs | 38 + .../Transporting/NetworkWriterLoop.cs.meta | 18 + .../FishNet/Runtime/Transporting/PacketId.cs | 35 + .../Runtime/Transporting/PacketId.cs.meta | 18 + .../FishNet/Runtime/Transporting/Transport.cs | 264 + .../Runtime/Transporting/Transport.cs.meta | 18 + .../Runtime/Transporting/TransportConsts.cs | 16 + .../Transporting/TransportConsts.cs.meta | 18 + .../Runtime/Transporting/Transports.meta | 8 + .../Transporting/Transports/Multipass.meta | 8 + .../Transports/Multipass/CHANGELOG.txt | 2 + .../Transports/Multipass/CHANGELOG.txt.meta | 14 + .../Transports/Multipass/Multipass.cs | 1083 ++++ .../Transports/Multipass/Multipass.cs.meta | 18 + .../Transports/Multipass/VERSION.txt | 1 + .../Transports/Multipass/VERSION.txt.meta | 14 + .../Transporting/Transports/Tugboat.meta | 8 + .../Transporting/Transports/Tugboat/Core.meta | 8 + .../Transports/Tugboat/Core/ClientSocket.cs | 279 + .../Tugboat/Core/ClientSocket.cs.meta | 18 + .../Transports/Tugboat/Core/CommonSocket.cs | 216 + .../Tugboat/Core/CommonSocket.cs.meta | 18 + .../Transports/Tugboat/Core/ServerSocket.cs | 502 ++ .../Tugboat/Core/ServerSocket.cs.meta | 18 + .../Transports/Tugboat/Core/Supporting.cs | 63 + .../Tugboat/Core/Supporting.cs.meta | 18 + .../Transports/Tugboat/Editor.meta | 8 + .../Tugboat/Editor/TugboatEditor.cs | 94 + .../Tugboat/Editor/TugboatEditor.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib.meta | 8 + .../Tugboat/LiteNetLib/BaseChannel.cs | 48 + .../Tugboat/LiteNetLib/BaseChannel.cs.meta | 18 + .../Tugboat/LiteNetLib/ConnectionRequest.cs | 134 + .../LiteNetLib/ConnectionRequest.cs.meta | 18 + .../Tugboat/LiteNetLib/INetEventListener.cs | 272 + .../LiteNetLib/INetEventListener.cs.meta | 18 + .../Tugboat/LiteNetLib/InternalPackets.cs | 133 + .../LiteNetLib/InternalPackets.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib/Layers.meta | 8 + .../Tugboat/LiteNetLib/Layers/Crc32cLayer.cs | 41 + .../LiteNetLib/Layers/Crc32cLayer.cs.meta | 18 + .../LiteNetLib/Layers/PacketLayerBase.cs | 17 + .../LiteNetLib/Layers/PacketLayerBase.cs.meta | 18 + .../LiteNetLib/Layers/XorEncryptLayer.cs | 59 + .../LiteNetLib/Layers/XorEncryptLayer.cs.meta | 18 + .../Tugboat/LiteNetLib/LiteNetLib.csproj | 66 + .../Tugboat/LiteNetLib/LiteNetLib.csproj.meta | 14 + .../Tugboat/LiteNetLib/NatPunchModule.cs | 264 + .../Tugboat/LiteNetLib/NatPunchModule.cs.meta | 18 + .../Tugboat/LiteNetLib/NativeSocket.cs | 179 + .../Tugboat/LiteNetLib/NativeSocket.cs.meta | 18 + .../Tugboat/LiteNetLib/NetConstants.cs | 75 + .../Tugboat/LiteNetLib/NetConstants.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib/NetDebug.cs | 92 + .../Tugboat/LiteNetLib/NetDebug.cs.meta | 18 + .../Tugboat/LiteNetLib/NetManager.HashSet.cs | 312 ++ .../LiteNetLib/NetManager.HashSet.cs.meta | 18 + .../LiteNetLib/NetManager.PacketPool.cs | 82 + .../LiteNetLib/NetManager.PacketPool.cs.meta | 18 + .../Tugboat/LiteNetLib/NetManager.Socket.cs | 707 +++ .../LiteNetLib/NetManager.Socket.cs.meta | 18 + .../Tugboat/LiteNetLib/NetManager.cs | 1820 +++++++ .../Tugboat/LiteNetLib/NetManager.cs.meta | 18 + .../Tugboat/LiteNetLib/NetPacket.cs | 164 + .../Tugboat/LiteNetLib/NetPacket.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib/NetPeer.cs | 1440 +++++ .../Tugboat/LiteNetLib/NetPeer.cs.meta | 18 + .../Tugboat/LiteNetLib/NetStatistics.cs | 81 + .../Tugboat/LiteNetLib/NetStatistics.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib/NetUtils.cs | 238 + .../Tugboat/LiteNetLib/NetUtils.cs.meta | 18 + .../Tugboat/LiteNetLib/PausedSocketFix.cs | 57 + .../LiteNetLib/PausedSocketFix.cs.meta | 18 + .../Tugboat/LiteNetLib/PooledPacket.cs | 32 + .../Tugboat/LiteNetLib/PooledPacket.cs.meta | 18 + .../Tugboat/LiteNetLib/ReliableChannel.cs | 337 ++ .../LiteNetLib/ReliableChannel.cs.meta | 18 + .../Tugboat/LiteNetLib/SequencedChannel.cs | 114 + .../LiteNetLib/SequencedChannel.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib/Trimming.cs | 12 + .../Tugboat/LiteNetLib/Trimming.cs.meta | 18 + .../Transports/Tugboat/LiteNetLib/Utils.meta | 8 + .../Tugboat/LiteNetLib/Utils/CRC32C.cs | 150 + .../Tugboat/LiteNetLib/Utils/CRC32C.cs.meta | 18 + .../LiteNetLib/Utils/FastBitConverter.cs | 175 + .../LiteNetLib/Utils/FastBitConverter.cs.meta | 18 + .../LiteNetLib/Utils/INetSerializable.cs | 8 + .../LiteNetLib/Utils/INetSerializable.cs.meta | 18 + .../Tugboat/LiteNetLib/Utils/NetDataReader.cs | 783 +++ .../LiteNetLib/Utils/NetDataReader.cs.meta | 18 + .../Tugboat/LiteNetLib/Utils/NetDataWriter.cs | 389 ++ .../LiteNetLib/Utils/NetDataWriter.cs.meta | 18 + .../LiteNetLib/Utils/NetPacketProcessor.cs | 288 + .../Utils/NetPacketProcessor.cs.meta | 18 + .../Tugboat/LiteNetLib/Utils/NetSerializer.cs | 762 +++ .../LiteNetLib/Utils/NetSerializer.cs.meta | 18 + .../Tugboat/LiteNetLib/Utils/NtpPacket.cs | 423 ++ .../LiteNetLib/Utils/NtpPacket.cs.meta | 18 + .../Tugboat/LiteNetLib/Utils/NtpRequest.cs | 42 + .../LiteNetLib/Utils/NtpRequest.cs.meta | 18 + .../Tugboat/LiteNetLib/Utils/Preserve.cs | 12 + .../Tugboat/LiteNetLib/Utils/Preserve.cs.meta | 18 + .../Tugboat/LiteNetLib/package.json | 11 + .../Tugboat/LiteNetLib/package.json.meta | 14 + .../Transports/Tugboat/Tugboat.cs | 612 +++ .../Transports/Tugboat/Tugboat.cs.meta | 18 + Assets/FishNet/Runtime/Utility.meta | 8 + .../Utility/AdaptiveInterpolationType.cs | 36 + .../Utility/AdaptiveInterpolationType.cs.meta | 18 + .../Utility/AdaptiveLocalTransformSmoother.cs | 629 +++ .../AdaptiveLocalTransformSmoother.cs.meta | 18 + Assets/FishNet/Runtime/Utility/Constants.cs | 15 + .../FishNet/Runtime/Utility/Constants.cs.meta | 18 + Assets/FishNet/Runtime/Utility/Extension.meta | 8 + .../Runtime/Utility/Extension/Networks.cs | 25 + .../Utility/Extension/Networks.cs.meta | 18 + .../Runtime/Utility/Extension/Scenes.cs | 95 + .../Runtime/Utility/Extension/Scenes.cs.meta | 18 + .../Runtime/Utility/Extension/Transforms.cs | 199 + .../Utility/Extension/Transforms.cs.meta | 18 + .../Utility/LocalTransformTickSmoother.cs | 162 + .../LocalTransformTickSmoother.cs.meta | 18 + .../FishNet/Runtime/Utility/Performance.meta | 8 + .../Runtime/Utility/Performance/BasicQueue.cs | 231 + .../Utility/Performance/BasicQueue.cs.meta | 18 + .../Utility/Performance/ByteArrayPool.cs | 53 + .../Utility/Performance/ByteArrayPool.cs.meta | 18 + .../Utility/Performance/DefaultObjectPool.cs | 321 ++ .../Performance/DefaultObjectPool.cs.meta | 18 + .../Runtime/Utility/Performance/ObjectPool.cs | 85 + .../Utility/Performance/ObjectPool.cs.meta | 18 + .../Performance/ObjectPoolRetrieveOption.cs | 25 + .../ObjectPoolRetrieveOption.cs.meta | 18 + .../Runtime/Utility/Performance/Transforms.cs | 28 + .../Utility/Performance/Transforms.cs.meta | 18 + Assets/FishNet/Runtime/Utility/SnappedAxes.cs | 25 + .../Runtime/Utility/SnappedAxes.cs.meta | 18 + Assets/FishNet/Runtime/Utility/Template.meta | 8 + .../Utility/Template/TickNetworkBehaviour.cs | 117 + .../Template/TickNetworkBehaviour.cs.meta | 18 + .../Runtime/Utility/TransformTickSmoother.cs | 691 +++ .../Utility/TransformTickSmoother.cs.meta | 18 + Assets/FishNet/THIRD PARTY NOTICE.md | 113 + Assets/FishNet/THIRD PARTY NOTICE.md.meta | 14 + Assets/FishNet/Upgrading.meta | 8 + Assets/FishNet/Upgrading/EdgegapMenu.cs | 26 + Assets/FishNet/Upgrading/EdgegapMenu.cs.meta | 18 + Assets/FishNet/Upgrading/MirrorUpgrade.cs | 410 ++ .../FishNet/Upgrading/MirrorUpgrade.cs.meta | 18 + .../Upgrading/UpgradeFromMirrorMenu.cs | 75 + .../Upgrading/UpgradeFromMirrorMenu.cs.meta | 18 + Assets/FishNet/package.json | 37 + Assets/FishNet/package.json.meta | 14 + Assets/InputSystem_Actions.inputactions | 1057 ++++ Assets/InputSystem_Actions.inputactions.meta | 14 + Assets/NuGet.config | 18 + Assets/NuGet.config.meta | 28 + Assets/OpusMicRecorder.cs | 147 + Assets/OpusMicRecorder.cs.meta | 2 + Assets/OpusVoiceReceiverWithBuffer.cs | 101 + Assets/OpusVoiceReceiverWithBuffer.cs.meta | 2 + Assets/OpusVoiceSender.cs | 26 + Assets/OpusVoiceSender.cs.meta | 2 + Assets/Packages.meta | 8 + Assets/Packages/Concentus.2.2.2.meta | 8 + .../Packages/Concentus.2.2.2/.signature.p7s | Bin 0 -> 12927 bytes .../Packages/Concentus.2.2.2/Concentus.nuspec | 37 + .../Concentus.2.2.2/Concentus.nuspec.meta | 7 + Assets/Packages/Concentus.2.2.2/LICENSE | 37 + Assets/Packages/Concentus.2.2.2/LICENSE.meta | 7 + Assets/Packages/Concentus.2.2.2/icon.png | Bin 0 -> 3294 bytes Assets/Packages/Concentus.2.2.2/icon.png.meta | 130 + Assets/Packages/Concentus.2.2.2/lib.meta | 8 + .../Concentus.2.2.2/lib/netstandard2.0.meta | 8 + .../lib/netstandard2.0/Concentus.dll | Bin 0 -> 392192 bytes .../lib/netstandard2.0/Concentus.dll.meta | 29 + .../lib/netstandard2.0/Concentus.xml | 4107 ++++++++++++++ .../lib/netstandard2.0/Concentus.xml.meta | 7 + .../Packages/websocketsharp.core.1.0.1.meta | 8 + .../websocketsharp.core.1.0.1/.signature.p7s | Bin 0 -> 12924 bytes .../websocketsharp.core.1.0.1/lib.meta | 8 + .../lib/netstandard2.0.meta | 8 + .../netstandard2.0/websocket-sharp-core.dll | Bin 0 -> 227328 bytes .../websocket-sharp-core.dll.meta | 29 + .../websocketsharp.core.nuspec | 13 + .../websocketsharp.core.nuspec.meta | 7 + Assets/Readme.asset | 34 + Assets/Readme.asset.meta | 8 + Assets/Scenes.meta | 8 + Assets/Scenes/SampleScene.unity | 585 ++ Assets/Scenes/SampleScene.unity.meta | 7 + Assets/Settings.meta | 8 + Assets/Settings/DefaultVolumeProfile.asset | 983 ++++ .../Settings/DefaultVolumeProfile.asset.meta | 8 + Assets/Settings/Mobile_RPAsset.asset | 135 + Assets/Settings/Mobile_RPAsset.asset.meta | 8 + Assets/Settings/Mobile_Renderer.asset | 52 + Assets/Settings/Mobile_Renderer.asset.meta | 8 + Assets/Settings/PC_RPAsset.asset | 136 + Assets/Settings/PC_RPAsset.asset.meta | 8 + Assets/Settings/PC_Renderer.asset | 95 + Assets/Settings/PC_Renderer.asset.meta | 8 + Assets/Settings/SampleSceneProfile.asset | 159 + Assets/Settings/SampleSceneProfile.asset.meta | 8 + ...niversalRenderPipelineGlobalSettings.asset | 367 ++ ...salRenderPipelineGlobalSettings.asset.meta | 8 + Assets/TutorialInfo.meta | 8 + Assets/TutorialInfo/Icons.meta | 9 + Assets/TutorialInfo/Icons/URP.png | Bin 0 -> 24069 bytes Assets/TutorialInfo/Icons/URP.png.meta | 134 + Assets/TutorialInfo/Layout.wlt | 654 +++ Assets/TutorialInfo/Layout.wlt.meta | 8 + Assets/TutorialInfo/Scripts.meta | 9 + Assets/TutorialInfo/Scripts/Editor.meta | 9 + .../Scripts/Editor/ReadmeEditor.cs | 242 + .../Scripts/Editor/ReadmeEditor.cs.meta | 12 + Assets/TutorialInfo/Scripts/Readme.cs | 16 + Assets/TutorialInfo/Scripts/Readme.cs.meta | 12 + Assets/VoicePlayer.cs | 10 + Assets/VoicePlayer.cs.meta | 2 + Assets/packages.config | 5 + Assets/packages.config.meta | 28 + Assets/prefab.meta | 8 + Assets/prefab/player.prefab | 208 + Assets/prefab/player.prefab.meta | 7 + Packages/manifest.json | 49 + Packages/packages-lock.json | 496 ++ ProjectSettings/AudioManager.asset | 19 + ProjectSettings/ClusterInputManager.asset | 6 + ProjectSettings/DynamicsManager.asset | 36 + ProjectSettings/EditorBuildSettings.asset | 13 + ProjectSettings/EditorSettings.asset | 49 + ProjectSettings/GraphicsSettings.asset | 67 + ProjectSettings/InputManager.asset | 487 ++ ProjectSettings/MemorySettings.asset | 35 + ProjectSettings/MultiplayerManager.asset | 7 + ProjectSettings/NavMeshAreas.asset | 91 + ProjectSettings/PackageManagerSettings.asset | 43 + ProjectSettings/Physics2DSettings.asset | 56 + ProjectSettings/PresetManager.asset | 7 + ProjectSettings/ProjectSettings.asset | 932 ++++ ProjectSettings/ProjectVersion.txt | 2 + ProjectSettings/QualitySettings.asset | 134 + ProjectSettings/SceneTemplateSettings.json | 121 + ProjectSettings/ShaderGraphSettings.asset | 18 + ProjectSettings/TagManager.asset | 76 + ProjectSettings/TimeManager.asset | 9 + ProjectSettings/URPProjectSettings.asset | 15 + ProjectSettings/UnityConnectSettings.asset | 36 + ProjectSettings/VFXManager.asset | 12 + ProjectSettings/VersionControlSettings.asset | 8 + ProjectSettings/XRSettings.asset | 10 + UserSettings/EditorUserSettings.asset | 63 + UserSettings/Layouts/default-6000.dwlt | 1413 +++++ UserSettings/Search.index | 13 + UserSettings/Search.settings | 81 + 1842 files changed, 211825 insertions(+) create mode 100644 Assets/DefaultPrefabObjects.asset create mode 100644 Assets/DefaultPrefabObjects.asset.meta create mode 100644 Assets/FishNet.meta create mode 100644 Assets/FishNet/CodeGenerating.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/FN_README.txt create mode 100644 Assets/FishNet/CodeGenerating/FN_README.txt.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs create mode 100644 Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/ILCore.meta create mode 100644 Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs create mode 100644 Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs create mode 100644 Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs create mode 100644 Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs create mode 100644 Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs create mode 100644 Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Prediction.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef create mode 100644 Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs.meta create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs create mode 100644 Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs.meta create mode 100644 Assets/FishNet/DOCUMENTATION.txt create mode 100644 Assets/FishNet/DOCUMENTATION.txt.meta create mode 100644 Assets/FishNet/Demos.meta create mode 100644 Assets/FishNet/Demos/Authenticator.meta create mode 100644 Assets/FishNet/Demos/Authenticator/Scenes.meta create mode 100644 Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity create mode 100644 Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity.meta create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts.meta create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs.meta create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs.meta create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs create mode 100644 Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs.meta create mode 100644 Assets/FishNet/Demos/Benchmarks.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/License.txt create mode 100644 Assets/FishNet/Demos/Benchmarks/License.txt.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs.meta create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs create mode 100644 Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Audio.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Audio/Glock.wav create mode 100644 Assets/FishNet/Demos/ColliderRollback/Audio/Glock.wav.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Ground.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Ground.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Particles.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Particles/Circle Additive.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Particles/Circle Additive.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Rollback Visualization.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Rollback Visualization/ClientPosition.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Rollback Visualization/ClientPosition.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Rollback Visualization/ServerPosition.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Rollback Visualization/ServerPosition.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Target.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Target.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Wall.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Materials/Wall.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Glock.obj create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Glock.obj.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Gray4.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Gray4.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Gray6.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Gray6.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Gray8.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Gray8.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Mesh1_Group2_Group1_ModelMat.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Mesh1_Group2_Group1_ModelMat.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Mesh2_Group3_Group1_ModelMat.mat create mode 100644 Assets/FishNet/Demos/ColliderRollback/Models/Weapons/Materials/Mesh2_Group3_Group1_ModelMat.mat.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Particles.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Particles/Pistol Muzzle Flash.prefab create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Particles/Pistol Muzzle Flash.prefab.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab create mode 100644 Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/ReadMe.txt create mode 100644 Assets/FishNet/Demos/ColliderRollback/ReadMe.txt.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scenes.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs create mode 100644 Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Textures.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png create mode 100644 Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Textures/Particles.meta create mode 100644 Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png create mode 100644 Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs create mode 100644 Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs create mode 100644 Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta create mode 100644 Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs create mode 100644 Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs.meta create mode 100644 Assets/FishNet/Demos/FishNet.Demos.asmdef create mode 100644 Assets/FishNet/Demos/FishNet.Demos.asmdef.meta create mode 100644 Assets/FishNet/Demos/HashGrid.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Prefabs.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab create mode 100644 Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab create mode 100644 Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab.meta create mode 100644 Assets/FishNet/Demos/HashGrid/ReadMe.txt create mode 100644 Assets/FishNet/Demos/HashGrid/ReadMe.txt.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Scenes.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity create mode 100644 Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Scripts.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs create mode 100644 Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs create mode 100644 Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Textures.meta create mode 100644 Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png create mode 100644 Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png.meta create mode 100644 Assets/FishNet/Demos/IntermediateLayer.meta create mode 100644 Assets/FishNet/Demos/IntermediateLayer/Scenes.meta create mode 100644 Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity create mode 100644 Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity.meta create mode 100644 Assets/FishNet/Demos/IntermediateLayer/Scripts.meta create mode 100644 Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs create mode 100644 Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs.meta create mode 100644 Assets/FishNet/Demos/Prediction.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Materials.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Prefabs.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Textures.meta create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png create mode 100644 Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Scripts.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Textures.meta create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png create mode 100644 Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png.meta create mode 100644 Assets/FishNet/Demos/Prefabs.meta create mode 100644 Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab create mode 100644 Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab.meta create mode 100644 Assets/FishNet/Demos/Prefabs/NetworkManager.prefab create mode 100644 Assets/FishNet/Demos/Prefabs/NetworkManager.prefab.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples).meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs create mode 100644 Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Models.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs create mode 100644 Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs.meta create mode 100644 Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png create mode 100644 Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png.meta create mode 100644 Assets/FishNet/Demos/Scripts.meta create mode 100644 Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs create mode 100644 Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs.meta create mode 100644 Assets/FishNet/LICENSE.txt create mode 100644 Assets/FishNet/LICENSE.txt.meta create mode 100644 Assets/FishNet/Runtime.meta create mode 100644 Assets/FishNet/Runtime/Authenticating.meta create mode 100644 Assets/FishNet/Runtime/Authenticating/Authenticator.cs create mode 100644 Assets/FishNet/Runtime/Authenticating/Authenticator.cs.meta create mode 100644 Assets/FishNet/Runtime/Broadcast.meta create mode 100644 Assets/FishNet/Runtime/Broadcast/Helping.meta create mode 100644 Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs create mode 100644 Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs.meta create mode 100644 Assets/FishNet/Runtime/Broadcast/IBroadcast.cs create mode 100644 Assets/FishNet/Runtime/Broadcast/IBroadcast.cs.meta create mode 100644 Assets/FishNet/Runtime/CodeGenerating.meta create mode 100644 Assets/FishNet/Runtime/CodeGenerating/Attributes.cs create mode 100644 Assets/FishNet/Runtime/CodeGenerating/Attributes.cs.meta create mode 100644 Assets/FishNet/Runtime/Config.json create mode 100644 Assets/FishNet/Runtime/Config.json.meta create mode 100644 Assets/FishNet/Runtime/Connection.meta create mode 100644 Assets/FishNet/Runtime/Connection/Buffer.cs create mode 100644 Assets/FishNet/Runtime/Connection/Buffer.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/EstimatedTick.cs create mode 100644 Assets/FishNet/Runtime/Connection/EstimatedTick.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.cs create mode 100644 Assets/FishNet/Runtime/Connection/NetworkConnection.cs.meta create mode 100644 Assets/FishNet/Runtime/Connection/OldTickOption.cs create mode 100644 Assets/FishNet/Runtime/Connection/OldTickOption.cs.meta create mode 100644 Assets/FishNet/Runtime/Documenting.meta create mode 100644 Assets/FishNet/Runtime/Documenting/Attributes.cs create mode 100644 Assets/FishNet/Runtime/Documenting/Attributes.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor.meta create mode 100644 Assets/FishNet/Runtime/Editor/BuildIdentifier.cs create mode 100644 Assets/FishNet/Runtime/Editor/BuildIdentifier.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/CodeStripping.cs create mode 100644 Assets/FishNet/Runtime/Editor/CodeStripping.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs create mode 100644 Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Constants.cs create mode 100644 Assets/FishNet/Runtime/Editor/Constants.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs create mode 100644 Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Finding.cs create mode 100644 Assets/FishNet/Runtime/Editor/Finding.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs create mode 100644 Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/NewNetworkBehaviour.meta create mode 100644 Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs create mode 100644 Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs create mode 100644 Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/PlayModeTracker.cs create mode 100644 Assets/FishNet/Runtime/Editor/PlayModeTracker.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta create mode 100644 Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs create mode 100644 Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs create mode 100644 Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/ScriptingDefines.cs create mode 100644 Assets/FishNet/Runtime/Editor/ScriptingDefines.cs.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/Icon.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png create mode 100644 Assets/FishNet/Runtime/Editor/Textures/Icon/fishnet_light.png.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/Client_Text.png create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/Client_Text.png.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/FishNet_Text.png create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/FishNet_Text.png.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png.meta create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/Server_Text.png create mode 100644 Assets/FishNet/Runtime/Editor/Textures/UI/Server_Text.png.meta create mode 100644 Assets/FishNet/Runtime/Editor/Upgrading.meta create mode 100644 Assets/FishNet/Runtime/Editor/Upgrading/UpgradeFromV3ToV4Menu.cs create mode 100644 Assets/FishNet/Runtime/Editor/Upgrading/UpgradeFromV3ToV4Menu.cs.meta create mode 100644 Assets/FishNet/Runtime/FishNet.Runtime.asmdef create mode 100644 Assets/FishNet/Runtime/FishNet.Runtime.asmdef.meta create mode 100644 Assets/FishNet/Runtime/Generated.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkAnimator.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Spawning.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/Editor.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs create mode 100644 Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs.meta create mode 100644 Assets/FishNet/Runtime/InstanceFinder.cs create mode 100644 Assets/FishNet/Runtime/InstanceFinder.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/ClientManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/ClientManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs create mode 100644 Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Debugging.meta create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs create mode 100644 Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Logging.meta create mode 100644 Assets/FishNet/Runtime/Managing/Logging/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs create mode 100644 Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs create mode 100644 Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs create mode 100644 Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/NetworkManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs create mode 100644 Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Observing.meta create mode 100644 Assets/FishNet/Runtime/Managing/Observing/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Prediction.meta create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs create mode 100644 Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs create mode 100644 Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Broadcast.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs create mode 100644 Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/KickReasons.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/KickReasons.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Server/ServerManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Statistic.meta create mode 100644 Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs create mode 100644 Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs create mode 100644 Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/Editor.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/TickType.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/TickType.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Transporting.meta create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs create mode 100644 Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Managing/Utility.meta create mode 100644 Assets/FishNet/Runtime/Managing/Utility/Utility.cs create mode 100644 Assets/FishNet/Runtime/Managing/Utility/Utility.cs.meta create mode 100644 Assets/FishNet/Runtime/Object.meta create mode 100644 Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs create mode 100644 Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Editor.meta create mode 100644 Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs create mode 100644 Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Helping.meta create mode 100644 Assets/FishNet/Runtime/Object/Helping/RpcLink.cs create mode 100644 Assets/FishNet/Runtime/Object/Helping/RpcLink.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Helping/RpcType.cs create mode 100644 Assets/FishNet/Runtime/Object/Helping/RpcType.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs create mode 100644 Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs create mode 100644 Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/Attributes.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/Attributes.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/Delegates.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/Delegates.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs create mode 100644 Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs create mode 100644 Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/TransformProperties.cs create mode 100644 Assets/FishNet/Runtime/Object/TransformProperties.cs.meta create mode 100644 Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs create mode 100644 Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset.meta create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset create mode 100644 Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset.meta create mode 100644 Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs create mode 100644 Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs create mode 100644 Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/NetworkObserver.cs create mode 100644 Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/ObserverCondition.cs create mode 100644 Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/ObserverConditionType.cs create mode 100644 Assets/FishNet/Runtime/Observing/ObserverConditionType.cs.meta create mode 100644 Assets/FishNet/Runtime/Observing/ObserverStateChange.cs create mode 100644 Assets/FishNet/Runtime/Observing/ObserverStateChange.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins.meta create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis.meta create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll.meta create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.dll.meta create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/LICENSE.txt create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/LICENSE.txt.meta create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/README.txt create mode 100644 Assets/FishNet/Runtime/Plugins/CodeAnalysis/README.txt.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/LICENSE.txt.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs create mode 100644 Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs create mode 100644 Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Yak.cs create mode 100644 Assets/FishNet/Runtime/Plugins/Yak/Yak.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing.meta create mode 100644 Assets/FishNet/Runtime/Serializing/AutoPackType.cs create mode 100644 Assets/FishNet/Runtime/Serializing/AutoPackType.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs create mode 100644 Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/DeltaTypes.cs create mode 100644 Assets/FishNet/Runtime/Serializing/DeltaTypes.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs create mode 100644 Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs create mode 100644 Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/GenericReader.cs create mode 100644 Assets/FishNet/Runtime/Serializing/GenericReader.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/GenericWriter.cs create mode 100644 Assets/FishNet/Runtime/Serializing/GenericWriter.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Reader.Delta.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Reader.Delta.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Reader.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Reader.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs create mode 100644 Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/ReaderPool.cs create mode 100644 Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/ReaderStatics.cs create mode 100644 Assets/FishNet/Runtime/Serializing/ReaderStatics.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/SceneComparer.cs create mode 100644 Assets/FishNet/Runtime/Serializing/SceneComparer.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream.meta create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs create mode 100644 Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/TransformPackingData.cs create mode 100644 Assets/FishNet/Runtime/Serializing/TransformPackingData.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs create mode 100644 Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Writer.Delta.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Writer.Delta.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/Writer.cs create mode 100644 Assets/FishNet/Runtime/Serializing/Writer.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/WriterExtensions.cs create mode 100644 Assets/FishNet/Runtime/Serializing/WriterExtensions.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/WriterPool.cs create mode 100644 Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Serializing/WriterStatics.cs create mode 100644 Assets/FishNet/Runtime/Serializing/WriterStatics.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Channels.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Channels.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/ConnectionStates.cs create mode 100644 Assets/FishNet/Runtime/Transporting/ConnectionStates.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/EventStructures.cs create mode 100644 Assets/FishNet/Runtime/Transporting/EventStructures.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/IPAddressType.cs create mode 100644 Assets/FishNet/Runtime/Transporting/IPAddressType.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs create mode 100644 Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs create mode 100644 Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/PacketId.cs create mode 100644 Assets/FishNet/Runtime/Transporting/PacketId.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transport.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transport.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/TransportConsts.cs create mode 100644 Assets/FishNet/Runtime/Transporting/TransportConsts.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json.meta create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs create mode 100644 Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility.meta create mode 100644 Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs create mode 100644 Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs create mode 100644 Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Constants.cs create mode 100644 Assets/FishNet/Runtime/Utility/Constants.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Extension.meta create mode 100644 Assets/FishNet/Runtime/Utility/Extension/Networks.cs create mode 100644 Assets/FishNet/Runtime/Utility/Extension/Networks.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Extension/Scenes.cs create mode 100644 Assets/FishNet/Runtime/Utility/Extension/Scenes.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Extension/Transforms.cs create mode 100644 Assets/FishNet/Runtime/Utility/Extension/Transforms.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs create mode 100644 Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs create mode 100644 Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs create mode 100644 Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs create mode 100644 Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs create mode 100644 Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Performance/Transforms.cs create mode 100644 Assets/FishNet/Runtime/Utility/Performance/Transforms.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/SnappedAxes.cs create mode 100644 Assets/FishNet/Runtime/Utility/SnappedAxes.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/Template.meta create mode 100644 Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs create mode 100644 Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs.meta create mode 100644 Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs create mode 100644 Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs.meta create mode 100644 Assets/FishNet/THIRD PARTY NOTICE.md create mode 100644 Assets/FishNet/THIRD PARTY NOTICE.md.meta create mode 100644 Assets/FishNet/Upgrading.meta create mode 100644 Assets/FishNet/Upgrading/EdgegapMenu.cs create mode 100644 Assets/FishNet/Upgrading/EdgegapMenu.cs.meta create mode 100644 Assets/FishNet/Upgrading/MirrorUpgrade.cs create mode 100644 Assets/FishNet/Upgrading/MirrorUpgrade.cs.meta create mode 100644 Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs create mode 100644 Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs.meta create mode 100644 Assets/FishNet/package.json create mode 100644 Assets/FishNet/package.json.meta create mode 100644 Assets/InputSystem_Actions.inputactions create mode 100644 Assets/InputSystem_Actions.inputactions.meta create mode 100644 Assets/NuGet.config create mode 100644 Assets/NuGet.config.meta create mode 100644 Assets/OpusMicRecorder.cs create mode 100644 Assets/OpusMicRecorder.cs.meta create mode 100644 Assets/OpusVoiceReceiverWithBuffer.cs create mode 100644 Assets/OpusVoiceReceiverWithBuffer.cs.meta create mode 100644 Assets/OpusVoiceSender.cs create mode 100644 Assets/OpusVoiceSender.cs.meta create mode 100644 Assets/Packages.meta create mode 100644 Assets/Packages/Concentus.2.2.2.meta create mode 100644 Assets/Packages/Concentus.2.2.2/.signature.p7s create mode 100644 Assets/Packages/Concentus.2.2.2/Concentus.nuspec create mode 100644 Assets/Packages/Concentus.2.2.2/Concentus.nuspec.meta create mode 100644 Assets/Packages/Concentus.2.2.2/LICENSE create mode 100644 Assets/Packages/Concentus.2.2.2/LICENSE.meta create mode 100644 Assets/Packages/Concentus.2.2.2/icon.png create mode 100644 Assets/Packages/Concentus.2.2.2/icon.png.meta create mode 100644 Assets/Packages/Concentus.2.2.2/lib.meta create mode 100644 Assets/Packages/Concentus.2.2.2/lib/netstandard2.0.meta create mode 100644 Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.dll create mode 100644 Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.dll.meta create mode 100644 Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.xml create mode 100644 Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.xml.meta create mode 100644 Assets/Packages/websocketsharp.core.1.0.1.meta create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/.signature.p7s create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/lib.meta create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/lib/netstandard2.0.meta create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/lib/netstandard2.0/websocket-sharp-core.dll create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/lib/netstandard2.0/websocket-sharp-core.dll.meta create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec create mode 100644 Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec.meta create mode 100644 Assets/Readme.asset create mode 100644 Assets/Readme.asset.meta create mode 100644 Assets/Scenes.meta create mode 100644 Assets/Scenes/SampleScene.unity create mode 100644 Assets/Scenes/SampleScene.unity.meta create mode 100644 Assets/Settings.meta create mode 100644 Assets/Settings/DefaultVolumeProfile.asset create mode 100644 Assets/Settings/DefaultVolumeProfile.asset.meta create mode 100644 Assets/Settings/Mobile_RPAsset.asset create mode 100644 Assets/Settings/Mobile_RPAsset.asset.meta create mode 100644 Assets/Settings/Mobile_Renderer.asset create mode 100644 Assets/Settings/Mobile_Renderer.asset.meta create mode 100644 Assets/Settings/PC_RPAsset.asset create mode 100644 Assets/Settings/PC_RPAsset.asset.meta create mode 100644 Assets/Settings/PC_Renderer.asset create mode 100644 Assets/Settings/PC_Renderer.asset.meta create mode 100644 Assets/Settings/SampleSceneProfile.asset create mode 100644 Assets/Settings/SampleSceneProfile.asset.meta create mode 100644 Assets/Settings/UniversalRenderPipelineGlobalSettings.asset create mode 100644 Assets/Settings/UniversalRenderPipelineGlobalSettings.asset.meta create mode 100644 Assets/TutorialInfo.meta create mode 100644 Assets/TutorialInfo/Icons.meta create mode 100644 Assets/TutorialInfo/Icons/URP.png create mode 100644 Assets/TutorialInfo/Icons/URP.png.meta create mode 100644 Assets/TutorialInfo/Layout.wlt create mode 100644 Assets/TutorialInfo/Layout.wlt.meta create mode 100644 Assets/TutorialInfo/Scripts.meta create mode 100644 Assets/TutorialInfo/Scripts/Editor.meta create mode 100644 Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs create mode 100644 Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs.meta create mode 100644 Assets/TutorialInfo/Scripts/Readme.cs create mode 100644 Assets/TutorialInfo/Scripts/Readme.cs.meta create mode 100644 Assets/VoicePlayer.cs create mode 100644 Assets/VoicePlayer.cs.meta create mode 100644 Assets/packages.config create mode 100644 Assets/packages.config.meta create mode 100644 Assets/prefab.meta create mode 100644 Assets/prefab/player.prefab create mode 100644 Assets/prefab/player.prefab.meta create mode 100644 Packages/manifest.json create mode 100644 Packages/packages-lock.json create mode 100644 ProjectSettings/AudioManager.asset create mode 100644 ProjectSettings/ClusterInputManager.asset create mode 100644 ProjectSettings/DynamicsManager.asset create mode 100644 ProjectSettings/EditorBuildSettings.asset create mode 100644 ProjectSettings/EditorSettings.asset create mode 100644 ProjectSettings/GraphicsSettings.asset create mode 100644 ProjectSettings/InputManager.asset create mode 100644 ProjectSettings/MemorySettings.asset create mode 100644 ProjectSettings/MultiplayerManager.asset create mode 100644 ProjectSettings/NavMeshAreas.asset create mode 100644 ProjectSettings/PackageManagerSettings.asset create mode 100644 ProjectSettings/Physics2DSettings.asset create mode 100644 ProjectSettings/PresetManager.asset create mode 100644 ProjectSettings/ProjectSettings.asset create mode 100644 ProjectSettings/ProjectVersion.txt create mode 100644 ProjectSettings/QualitySettings.asset create mode 100644 ProjectSettings/SceneTemplateSettings.json create mode 100644 ProjectSettings/ShaderGraphSettings.asset create mode 100644 ProjectSettings/TagManager.asset create mode 100644 ProjectSettings/TimeManager.asset create mode 100644 ProjectSettings/URPProjectSettings.asset create mode 100644 ProjectSettings/UnityConnectSettings.asset create mode 100644 ProjectSettings/VFXManager.asset create mode 100644 ProjectSettings/VersionControlSettings.asset create mode 100644 ProjectSettings/XRSettings.asset create mode 100644 UserSettings/EditorUserSettings.asset create mode 100644 UserSettings/Layouts/default-6000.dwlt create mode 100644 UserSettings/Search.index create mode 100644 UserSettings/Search.settings diff --git a/Assets/DefaultPrefabObjects.asset b/Assets/DefaultPrefabObjects.asset new file mode 100644 index 0000000..d4c8c5c --- /dev/null +++ b/Assets/DefaultPrefabObjects.asset @@ -0,0 +1,28 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3ad70174b079c2f4ebc7931d3dd1af6f, type: 3} + m_Name: DefaultPrefabObjects + m_EditorClassIdentifier: + _prefabs: + - {fileID: 3007804520713623267, guid: e3ebf1e5214cbb941b2da4f7add0060a, type: 3} + - {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3} + - {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3} + - {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3} + - {fileID: 611616139817875448, guid: bf5f023b4017a5e41a9815ec5745df3d, type: 3} + - {fileID: 4512293259955182956, guid: 35639798ad77fc145871588b25d66259, type: 3} + - {fileID: 4512293259955182956, guid: fe2b65b02f0484b41aa8cfa9fbbb0e1d, type: 3} + - {fileID: 4512293259955182956, guid: dafef736ca1ae384e9a19eb672843563, type: 3} + - {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3} + - {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3} + - {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3} + - {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, type: 3} + - {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3} diff --git a/Assets/DefaultPrefabObjects.asset.meta b/Assets/DefaultPrefabObjects.asset.meta new file mode 100644 index 0000000..a71b6d4 --- /dev/null +++ b/Assets/DefaultPrefabObjects.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4c21a388bb04ca646927587df80f819a +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet.meta b/Assets/FishNet.meta new file mode 100644 index 0000000..5e5a228 --- /dev/null +++ b/Assets/FishNet.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f1525dbf8ebd59e438b504fa19c4fd6c +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..e7902b4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a03c3ea914b8ed649a14606e1430e404 +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..20b1a5c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c2e223f21744b544bbb7e93020199efb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs new file mode 100644 index 0000000..14259c8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs @@ -0,0 +1,22 @@ +using MonoFN.Cecil; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class FieldDefinitionExtensions + { + + /// + /// Makes a FieldDefinition generic if it has generic parameters. + /// + public static FieldReference TryMakeGenericInstance(this FieldDefinition fd, CodegenSession session) + { + FieldReference fr = session.ImportReference(fd); + return fr.TryMakeGenericInstance(); + } + + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs.meta new file mode 100644 index 0000000..eaf1af7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: de84fa9739ed55945b58147a773e1740 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Extension/FieldDefinitionExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs new file mode 100644 index 0000000..080bf2c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs @@ -0,0 +1,40 @@ +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); + } + + /// + /// Makes a FieldReference generic if it has generic parameters. + /// + public static FieldReference TryMakeGenericInstance(this FieldReference fr) + { + TypeReference declaringTr = fr.DeclaringType; + + if (declaringTr.HasGenericParameters) + { + GenericInstanceType git = declaringTr.MakeGenericInstanceType(); + FieldReference result = new(fr.Name, fr.FieldType, git); + return result; + } + else + { + return fr; + } + } + + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs.meta b/Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs.meta new file mode 100644 index 0000000..432fb5f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6d983ebd0c9e1b745902030c2f7a8e99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Extension/FieldReferenceExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs new file mode 100644 index 0000000..9d2ecc3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs @@ -0,0 +1,28 @@ +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil; +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); + } + /// + /// Creates a variable type within the body and returns it's VariableDef. + /// + internal static VariableDefinition CreateVariable(this ILProcessor processor, CodegenSession session, TypeReference variableTr) + { + return processor.Body.Method.CreateVariable(variableTr); + } + } + + +} \ 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..9b8492e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 127d8312da53b3e49ba9e3e4c6348857 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Extension/ILProcessorExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs new file mode 100644 index 0000000..9ebd5c3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs @@ -0,0 +1,239 @@ +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class MethodDefinitionExtensions + { + public const MethodAttributes PUBLIC_VIRTUAL_ATTRIBUTES = (MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig); + public const MethodAttributes PROTECTED_VIRTUAL_ATTRIBUTES = (MethodAttributes.Family | MethodAttributes.Virtual | MethodAttributes.HideBySig); + + /// + /// Returns a custom attribute. + /// + public static CustomAttribute GetCustomAttribute(this MethodDefinition md, string attributeFullName) + { + if (md == null) + return null; + + foreach (CustomAttribute item in md.CustomAttributes) + { + if (item.AttributeType.FullName == attributeFullName) + return item; + } + + //Not found. + return null; + } + + /// + /// 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(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)); + } + + /// + /// 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 a parameter and returns added parameters. + /// + public static ParameterDefinition CreateParameter(this MethodDefinition thisMd, CodegenSession session, ParameterAttributes attr, System.Type type) + { + TypeReference parameterTypeRef = session.ImportReference(type); + ParameterDefinition pd = new($"p{thisMd.Parameters.Count}", attr, parameterTypeRef); + thisMd.Parameters.Add(pd); + return pd; + } + + /// + /// Adds otherMd parameters to thisMd and returns added parameters. + /// + public static List CreateParameters(this MethodDefinition thisMd, CodegenSession session, MethodDefinition otherMd) + { + List results = new(); + + foreach (ParameterDefinition pd in otherMd.Parameters) + { + session.ImportReference(pd.ParameterType.CachedResolve(session)); + int currentCount = thisMd.Parameters.Count; + string name = (pd.Name + currentCount); + ParameterDefinition parameterDef = new(name, pd.Attributes, pd.ParameterType); + //Set any default values. + parameterDef.Constant = pd.Constant; + parameterDef.IsReturnValue = pd.IsReturnValue; + parameterDef.IsOut = pd.IsOut; + foreach (CustomAttribute item in pd.CustomAttributes) + parameterDef.CustomAttributes.Add(item); + parameterDef.HasConstant = pd.HasConstant; + parameterDef.HasDefault = pd.HasDefault; + + if (parameterDef == null || thisMd.Parameters == null) + { + session.LogError($"ParameterDefinition or collection is null. Definition null: {parameterDef == null}. Collection null: {thisMd.Parameters == null}."); + } + else + { + thisMd.Parameters.Add(parameterDef); + results.Add(parameterDef); + } + } + + return results; + } + + /// + /// 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(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); + } + + /// + /// Removes ret if it exist at the end of the method. Returns if ret was removed. + /// + internal static bool RemoveEndRet(this MethodDefinition md, CodegenSession session) + { + int count = md.Body.Instructions.Count; + if (count > 0 && md.Body.Instructions[count - 1].OpCode == OpCodes.Ret) + { + md.Body.Instructions.RemoveAt(count - 1); + return true; + } + else + { + return false; + } + } + + + /// + /// 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); + } + + public static MethodDefinition CreateCopy(this MethodDefinition copiedMd, CodegenSession session, string nameOverride = null, MethodAttributes? attributesOverride = null) + { + session.ImportReference(copiedMd.ReturnType); + + MethodAttributes attr = (attributesOverride.HasValue) ? attributesOverride.Value : copiedMd.Attributes; + string name = (nameOverride == null) ? copiedMd.Name : nameOverride; + MethodDefinition result = new(name, attr, copiedMd.ReturnType); + foreach (GenericParameter item in copiedMd.GenericParameters) + result.GenericParameters.Add(item); + + result.CreateParameters(session, copiedMd); + return result; + } + + /// + /// Makes a method definition public. + /// + public static void SetPublicAttributes(this MethodDefinition md) + { + md.Attributes = PUBLIC_VIRTUAL_ATTRIBUTES; + } + public static void SetProtectedAttributes(this MethodDefinition md) + { + md.Attributes = PROTECTED_VIRTUAL_ATTRIBUTES; + } + } + + +} \ 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..1607b59 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 866ed457fe28c3e4b9698d87b9abd709 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Extension/MethodDefinitionExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs new file mode 100644 index 0000000..d0f16df --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs @@ -0,0 +1,314 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Object.Prediction; +using FishNet.Utility.Performance; +using MonoFN.Cecil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class TypeDefinitionExtensions + { + + + /// + /// Returns if a TypeDefinition is nullable. + /// + public static bool IsNullable(this TypeDefinition td) + { + return (td.Name == typeof(System.Nullable<>).Name); + } + + /// + /// Finds the first method by a given name. + /// + /// + /// + /// + internal static MethodDefinition GetMethod(this TypeDefinition typeDef, string methodName) + { + foreach (MethodDefinition md in typeDef.Methods) + { + if (md.Name == methodName) + return md; + } + + return null; + } + + + public static MethodReference GetMethodReferenceInBase(this TypeDefinition td, CodegenSession session, string methodName) + { + MethodDefinition baseMd = td.GetMethodDefinitionInBase(session, methodName); + if (baseMd == null) + return null; + + MethodReference baseMr; + TypeReference baseTr = td.BaseType; + if (baseTr.CachedResolve(session).HasGenericParameters) + { + GenericInstanceType git = (GenericInstanceType)baseTr; + baseMr = new(baseMd.Name, baseMd.ReturnType, git) + { + HasThis = baseMd.HasThis, + CallingConvention = baseMd.CallingConvention, + ExplicitThis = baseMd.ExplicitThis, + }; + foreach (ParameterDefinition pd in baseMd.Parameters) + { + session.ImportReference(pd.ParameterType); + baseMr.Parameters.Add(pd); + } + } + else + { + baseMr = session.ImportReference(baseMd); + } + + return baseMr; + } + /// + /// 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(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(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(methodName, methodTemplate.Attributes, returnType) + { + ExplicitThis = methodTemplate.ExplicitThis, + AggressiveInlining = methodTemplate.AggressiveInlining, + Attributes = methodTemplate.Attributes, + CallingConvention = methodTemplate.CallingConvention, + HasThis = methodTemplate.HasThis, + }; + md.Body.InitLocals = methodTemplate.Body.InitLocals; + + if (copyParameters) + { + foreach (ParameterDefinition pd in methodTemplate.Parameters) + { + session.ImportReference(pd.ParameterType.CachedResolve(session)); + md.Parameters.Add(pd); + } + } + + foreach (GenericParameter item in methodTemplate.GenericParameters) + { + session.ImportReference(item); + md.GenericParameters.Add(item); + } + + td.Methods.Add(md); + created = true; + } + else + { + created = false; + } + + return md; + } + + + + /// + /// 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 a TypeDefintiion found in typeDef or up it's hierarchy. + /// + /// True to check if typeDef equals fullName. + /// + public static TypeDefinition GetTypeDefinitionInBase(this TypeDefinition typeDef, CodegenSession session, string targetFullName, bool checkTypeDef) + { + if (typeDef == null) + return null; + if (!checkTypeDef) + typeDef = typeDef.GetNextBaseTypeDefinition(session); + + while (typeDef != null) + { + if (typeDef.FullName == targetFullName) + return typeDef; + + typeDef = typeDef.GetNextBaseTypeDefinition(session); + } + + return null; + } + + /// + /// 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(declaringType); + foreach (GenericParameter item in declaringType.GenericParameters) + git.GenericArguments.Add(item); + fr = new(fd.Name, fd.FieldType, git); + return fr; + } + //Not generic. + else + { + return session.ImportReference(fd); + } + } + + /// + /// 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(fieldName, attributes, fieldTypeRef); + td.Fields.Add(fd); + return fd.CreateFieldReference(session); + } + + + /// + /// Makes a GenericInstanceType. + /// + public static GenericInstanceType MakeGenericInstanceType(this TypeDefinition self, CodegenSession session) + { + TypeReference tr = session.ImportReference(self); + return tr.MakeGenericInstanceType(); + } + + + } + + +} \ 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..d7b8dad --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c9f00cf3dc8b90b469c3c9cb8b87fc88 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Extension/TypeDefinitionExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs new file mode 100644 index 0000000..e7928b6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs @@ -0,0 +1,52 @@ + +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using MonoFN.Cecil; +using UnityEngine; + +namespace FishNet.CodeGenerating.Extension +{ + + + internal static class TypeReferenceExtensions + { + + /// + /// 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; + } + + + /// + /// Makes a GenericInstanceType. + /// + public static GenericInstanceType MakeGenericInstanceType(this TypeReference self) + { + GenericInstanceType instance = new(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..c4ad754 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 239b1b10db80c594d93b7ba4ee2c1ec5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Extension/TypeReferenceExtensions.cs + uploadId: 762203 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..4845268 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/FN_README.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 9133eb285bd7f3c4f89f4d7a2a079c6b +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/FN_README.txt + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers.meta b/Assets/FishNet/CodeGenerating/Helpers.meta new file mode 100644 index 0000000..b0764dd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d27bb367daea26d46a8fa5f6ca02865e +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..bf2772f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs @@ -0,0 +1,68 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Object; +using FishNet.Object.Helping; +using FishNet.Object.Prediction; +using MonoFN.Cecil; + + +namespace FishNet.CodeGenerating.Helping +{ + internal class AttributeHelper : CodegenBase + { + #region Reflection references. + internal string ReplicateAttribute_FullName; + internal string ReconcileAttribute_FullName; + private string ServerAttribute_FullName; + private string ClientAttribute_FullName; + private string ServerRpcAttribute_FullName; + private string ObserversRpcAttribute_FullName; + private string TargetRpcAttribute_FullName; + #endregion + + public override bool ImportReferences() + { + ServerAttribute_FullName = typeof(ServerAttribute).FullName; + ClientAttribute_FullName = typeof(ClientAttribute).FullName; + ServerRpcAttribute_FullName = typeof(ServerRpcAttribute).FullName; + ObserversRpcAttribute_FullName = typeof(ObserversRpcAttribute).FullName; + TargetRpcAttribute_FullName = typeof(TargetRpcAttribute).FullName; + ReplicateAttribute_FullName = typeof(ReplicateAttribute).FullName; + ReconcileAttribute_FullName = typeof(ReconcileAttribute).FullName; + return true; + } + + /// + /// 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; + } + } + +} diff --git a/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta new file mode 100644 index 0000000..16636b3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d32f3dc23b55598429c5cfe6156e6243 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/AttributeHelper.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs new file mode 100644 index 0000000..0eb0211 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs @@ -0,0 +1,184 @@ +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.ILCore; +using FishNet.CodeGenerating.Processing; +using FishNet.CodeGenerating.Processing.Rpc; +using MonoFN.Cecil; +using System.Collections.Generic; +using System.Linq; +using Unity.CompilationPipeline.Common.Diagnostics; +#if !UNITY_2020_1_OR_NEWER +using UnityEngine; +#endif +using SR = System.Reflection; + + +namespace FishNet.CodeGenerating +{ + + internal class CodegenSession + { + /// + /// 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(); + + + /// + /// CodegenBase classes for processing a module. + /// + private List _bases; + /// + /// Quick lookup of base classes. + /// + private Dictionary _basesCache = new(); + + /// + /// 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(); + + _bases = new() + { + new ReaderImports(), new ReaderProcessor() + ,new WriterImports(), new WriterProcessor() + , new PhysicsHelper(), new TimeManagerHelper(), new AttributeHelper(), new GeneralHelper() + , new ObjectHelper(), new NetworkBehaviourHelper() + , new TransportHelper() + , new NetworkConnectionImports(), new PredictedObjectHelper(), new GeneratorHelper() + , new CustomSerializerProcessor() + , new NetworkBehaviourProcessor() + , new QolAttributeProcessor() + , new RpcProcessor() + , new SyncTypeProcessor() + , new PredictionProcessor() + }; + + //Add all to dictionary first, then import. + foreach (CodegenBase item in _bases) + { + string tName = item.GetType().Name; + _basesCache.Add(tName, item); + } + + //Initialize. + foreach (CodegenBase item in _bases) + { + item.Initialize(this); + if (!item.ImportReferences()) + return false; + } + + return true; + } + + + #region Logging. + /// + /// Logs a warning. + /// + /// + internal void LogWarning(string msg) + { + Diagnostics.AddWarning(msg); + } + /// + /// Logs an error. + /// + /// + internal void LogError(string msg) + { + Diagnostics.AddError(msg); + } + #endregion + + #region ImportReference. + + public MethodReference ImportReference(SR.MethodBase method) + { + return Module.ImportReference(method); + } + + public MethodReference ImportReference(SR.MethodBase method, IGenericParameterProvider context) + { + return Module.ImportReference(method, context); + } + + public TypeReference ImportReference(TypeReference type) + { + return Module.ImportReference(type); + } + + public TypeReference ImportReference(TypeReference type, IGenericParameterProvider context) + { + return Module.ImportReference(type, context); + } + + public FieldReference ImportReference(FieldReference field) + { + return Module.ImportReference(field); + } + + public FieldReference ImportReference(FieldReference field, IGenericParameterProvider context) + { + return Module.ImportReference(field, context); + } + public MethodReference ImportReference(MethodReference method) + { + return Module.ImportReference(method); + } + + public MethodReference ImportReference(MethodReference method, IGenericParameterProvider context) + { + return Module.ImportReference(method, context); + } + public TypeReference ImportReference(System.Type type) + { + return ImportReference(type, null); + } + + + public TypeReference ImportReference(System.Type type, IGenericParameterProvider context) + { + return Module.ImportReference(type, context); + } + + + public FieldReference ImportReference(SR.FieldInfo field) + { + return Module.ImportReference(field); + } + + public FieldReference ImportReference(SR.FieldInfo field, IGenericParameterProvider context) + { + return Module.ImportReference(field, context); + } + + #endregion + + } + + +} \ 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..9bf831f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3e8416eee3308f54fa942003de975420 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/CodegenSession.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs new file mode 100644 index 0000000..cae9beb --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs @@ -0,0 +1,120 @@ +//using FishNet.CodeGenerating.Helping.Extension; +//using FishNet.CodeGenerating.Processing; +//using FishNet.Object.Synchronizing; +//using FishNet.Object.Synchronizing.Internal; +//using MonoFN.Cecil; +//using MonoFN.Cecil.Rocks; +//using System; +//using System.Collections.Generic; + +//namespace FishNet.CodeGenerating.Helping +//{ +// internal class CreatedSyncVarGenerator : CodegenBase +// { +// private readonly Dictionary _createdSyncVars = new Dictionary(); + +// #region Relfection references. +// private TypeReference _syncBase_TypeRef; +// internal TypeReference SyncVar_TypeRef; +// private MethodReference _syncVar_InitializeOnce_MethodRef; +// #endregion + +// #region Const. +// private const string GETVALUE_NAME = "GetValue"; +// private const string SETVALUE_NAME = "SetValue"; +// #endregion + +// /* //feature add and test the dirty boolean changes +// * eg... instead of base.Dirty() +// * do if (!base.Dirty()) return false; +// * See synclist for more info. */ + +// /// +// /// Imports references needed by this helper. +// /// +// /// +// /// +// public override bool ImportReferences() +// { +// Type svType = typeof(SyncVar<>); +// SyncVar_TypeRef = base.ImportReference(svType); +// _syncVar_InitializeOnce_MethodRef = base.ImportReference(svType.GetMethod(nameof(SyncVar.InitializeOnce))); +// Type syncBaseType = typeof(SyncBase); +// _syncBase_TypeRef = base.ImportReference(syncBaseType); + +// return true; +// } + +// /// +// /// Gets and optionally creates data for SyncVar +// /// +// /// +// /// +// internal DeclaredSyncType GetCreatedSyncVar(FieldDefinition originalFd, bool createMissing) +// { +// TypeReference dataTr = originalFd.FieldType; +// TypeDefinition dataTd = dataTr.CachedResolve(base.Session); + +// string typeHash = dataTr.FullName + dataTr.IsArray.ToString(); + +// if (_createdSyncVars.TryGetValue(typeHash, out DeclaredSyncType createdSyncVar)) +// { +// return createdSyncVar; +// } +// else +// { +// if (!createMissing) +// return null; + +// base.ImportReference(dataTd); + +// GenericInstanceType originalFdGit = SyncVar_TypeRef.MakeGenericInstanceType(new TypeReference[] { dataTr }); +// TypeReference genericDataTr = originalFdGit.GenericArguments[0]; + +// //Make sure can serialize. +// bool canSerialize = base.GetClass().HasSerializerAndDeserializer(genericDataTr, true); +// if (!canSerialize) +// { +// base.LogError($"SyncVar {originalFd.Name} data type {genericDataTr.FullName} does not support serialization. Use a supported type or create a custom serializer."); +// return null; +// } + +// //Set needed methods from syncbase. +// MethodReference setSyncIndexMr; +// MethodReference initializeOnceMrGit = _syncVar_InitializeOnce_MethodRef.MakeHostInstanceGeneric(base.Session, originalFdGit); + +// if (!base.GetClass().SetSyncBaseMethods(_syncBase_TypeRef.CachedResolve(base.Session), out setSyncIndexMr, out _)) +// return null; + +// MethodReference setValueMr = null; +// MethodReference getValueMr = null; +// foreach (MethodDefinition md in SyncVar_TypeRef.CachedResolve(base.Session).Methods) +// { +// //GetValue. +// if (md.Name == GETVALUE_NAME) +// { +// MethodReference mr = base.ImportReference(md); +// getValueMr = mr.MakeHostInstanceGeneric(base.Session, originalFdGit); +// } +// //SetValue. +// else if (md.Name == SETVALUE_NAME) +// { +// MethodReference mr = base.ImportReference(md); +// setValueMr = mr.MakeHostInstanceGeneric(base.Session, originalFdGit); +// } +// } + +// if (setValueMr == null || getValueMr == null) +// return null; + +// DeclaredSyncType csv = new DeclaredSyncType(originalFd, originalFdGit, dataTd, initializeOnceMrGit, null); +// _createdSyncVars.Add(typeHash, csv); +// return csv; +// } +// } + + +// } + + +//} \ 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..cb25b6a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 935ec97b96b35b94f8c6880c6908304c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/CreatedSyncVarGenerator.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension.meta b/Assets/FishNet/CodeGenerating/Helpers/Extension.meta new file mode 100644 index 0000000..4e7333c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6fee1744ec071184db72fc2443e77ece +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..c8a7a6d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a66d771ab331fae408142a5c04abd74e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/CustomAttributeExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs new file mode 100644 index 0000000..f549971 --- /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() + { + 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..061d8aa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fb7b65b572b01444cbe3c9d830cd3587 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/Diagnostics.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs new file mode 100644 index 0000000..d258e17 --- /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 GetDefaultConstructor(this TypeReference typeRef, CodegenSession session) + { + return typeRef.CachedResolve(session).GetDefaultConstructor(); + } + /// + /// Gets the first public constructor with no parameters. + /// + /// + public static MethodDefinition GetDefaultConstructor(this TypeDefinition typeDef) + { + foreach (MethodDefinition methodDef in typeDef.Methods) + { + if (methodDef.IsConstructor && methodDef.IsPublic && methodDef.Parameters.Count == 0) + return methodDef; + } + + return null; + } + + /// + /// Gets all constructors on typeDef. + /// + /// + public static List GetConstructors(this TypeDefinition typeDef) + { + List lst = new(); + 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..e5e165f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1a7e03137ca78704e999f3a3dc68b953 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/GetConstructor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs new file mode 100644 index 0000000..1a67a6d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs @@ -0,0 +1,198 @@ +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 List DebugLog(this ILProcessor processor, CodegenSession session, string txt) + { + List insts = new(); + insts.Add(processor.Create(OpCodes.Ldstr, txt)); + insts.Add(processor.Create(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef)); + return insts; + } + /// + /// Creates a debug log for vd without any conditions. + /// + public static List DebugLog(this ILProcessor processor, CodegenSession session, VariableDefinition vd) + { + List insts = new(); + insts.Add(processor.Create(OpCodes.Ldloc, vd)); + insts.Add(processor.Create(OpCodes.Box, vd.VariableType)); + insts.Add(processor.Create(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef)); + return insts; + } + /// + /// Creates a debug log for vd without any conditions. + /// + public static List DebugLog(this ILProcessor processor, CodegenSession session, FieldDefinition fd, bool loadArg0) + { + List insts = new(); + if (loadArg0) + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldfld, fd)); + insts.Add(processor.Create(OpCodes.Box, fd.FieldType)); + insts.Add(processor.Create(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef)); + return insts; + } + /// + /// Creates a debug log for pd without any conditions. + /// + public static List DebugLog(this ILProcessor processor, CodegenSession session, ParameterDefinition pd) + { + List insts = new(); + insts.Add(processor.Create(OpCodes.Ldloc, pd)); + insts.Add(processor.Create(OpCodes.Box, pd.ParameterType)); + insts.Add(processor.Create(OpCodes.Call, session.GetClass().Debug_LogCommon_MethodRef)); + return insts; + } + + /// + /// Removes Ret if the last instruction. + /// + /// True if ret was removed. + public static bool RemoveEndRet(this ILProcessor processor) + { + int instCount = processor.Body.Instructions.Count; + if (instCount == 0) + return false; + + if (processor.Body.Instructions[instCount-1].OpCode == OpCodes.Ret) + { + processor.Body.Instructions.RemoveAt(instCount - 1); + return true; + } + else + { + return false; + } + } + + ///// + ///// 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..78a91f7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 820cf8401d4d71c4196dda444559ef8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ILProcessorExtensions.cs + uploadId: 762203 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..7170a02 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 360667149f16b6c4aba61fd05427cbfb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/InstructionExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs new file mode 100644 index 0000000..44bd974 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs @@ -0,0 +1,174 @@ +using FishNet.CodeGenerating.Extension; +using MonoFN.Cecil; +using MonoFN.Cecil.Rocks; +using System; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class MethodReferenceExtensions + { + + /// + /// Returns a custom attribute. + /// + public static CustomAttribute GetCustomAttribute(this MethodReference mr, string attributeFullName) + { + MethodDefinition md = mr.Resolve(); + return MethodDefinitionExtensions.GetCustomAttribute(md, attributeFullName); + } + + /// + /// Makes a generic method with specified arguments. + /// + /// + /// + /// + public static GenericInstanceMethod MakeGenericMethod(this MethodReference method, params TypeReference[] genericArguments) + { + GenericInstanceMethod result = new(method); + foreach (TypeReference argument in genericArguments) + result.GenericArguments.Add(argument); + return result; + } + + /// + /// Makes a generic method with the same arguments as the original. + /// + /// + /// + public static GenericInstanceMethod MakeGenericMethod(this MethodReference method) + { + GenericInstanceMethod result = new(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); + } + + /// + /// Removes ret if it exist at the end of the method. Returns if ret was removed. + /// + internal static bool RemoveEndRet(this MethodReference mr, CodegenSession session) + { + MethodDefinition md = mr.CachedResolve(session); + return MethodDefinitionExtensions.RemoveEndRet(md, session); + } + /// + /// 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(self.Name, self.ReturnType, instanceType) + { + CallingConvention = self.CallingConvention, + HasThis = self.HasThis, + ExplicitThis = self.ExplicitThis + }; + + foreach (ParameterDefinition parameter in self.Parameters) + reference.Parameters.Add(new(parameter.ParameterType)); + + foreach (GenericParameter generic_parameter in self.GenericParameters) + reference.GenericParameters.Add(new(generic_parameter.Name, reference)); + + return session.ImportReference(reference); + } + /// + /// 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(self.Name, self.ReturnType, git) + { + CallingConvention = self.CallingConvention, + HasThis = self.HasThis, + ExplicitThis = self.ExplicitThis + }; + + foreach (ParameterDefinition parameter in self.Parameters) + reference.Parameters.Add(new(parameter.ParameterType)); + + foreach (GenericParameter generic_parameter in self.GenericParameters) + reference.GenericParameters.Add(new(generic_parameter.Name, reference)); + + return reference; + } + public static bool Is(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..3e9f5bc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ee1c15a06ab386e439ec5aa41e3496f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/MethodReferenceExtensions.cs + uploadId: 762203 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..c76656e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 42648785493390646898f5fa13e1dfd6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ModuleDefinitionExtensions.cs + uploadId: 762203 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..43027d3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 31071055a2e388141b8f11e1ba4e147e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/ParameterDefinitionExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs new file mode 100644 index 0000000..fd9001e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs @@ -0,0 +1,504 @@ +using FishNet.CodeGenerating.Extension; +using MonoFN.Cecil; +using MonoFN.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + + internal static class TypeDefinitionExtensionsOld + { + + /// + /// Creates a GenericInstanceType and adds parameters. + /// + internal static GenericInstanceType CreateGenericInstanceType(this TypeDefinition type, Collection parameters) + { + GenericInstanceType git = new(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.HasExcludeSerializationAttribute(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.HasExcludeSerializationAttribute(pd)) + continue; + if (pd.GetMethod.IsPrivate) + continue; + if (pd.SetMethod.IsPrivate) + continue; + if (pd.GetMethod.ReturnType.IsGenericParameter) + continue; + + yield return pd; + } + + try { typeDef = typeDef.BaseType?.CachedResolve(session); } + catch { break; } + } + } + + /// + /// Returns if typeDef is excluded. + /// + private static bool IsExcluded(TypeDefinition typeDef, System.Type[] excludedBaseTypes = null, string[] excludedAssemblyPrefixes = null) + { + if (typeDef.FullName == typeof(LoadSceneParameters).FullName) + return false; + + if (excludedBaseTypes != null) + { + foreach (System.Type t in excludedBaseTypes) + { + if (typeDef.FullName == t.FullName) + return true; + } + } + if (excludedAssemblyPrefixes != null) + { + foreach (string s in excludedAssemblyPrefixes) + { + int len = s.Length; + string tdAsmName = typeDef.Module.Assembly.FullName; + if (tdAsmName.Length >= len && tdAsmName.Substring(0, len).ToLower() == s.ToLower()) + return true; + } + } + + //Fall through, not excluded. + return false; + } + + + /// + /// 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; + } + + /// + /// 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) + { + if (typeDef.Interfaces == null) + return false; + 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..09a7819 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 645e49fe7eeff3a4e9eb65d77fc6e2ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/TypeDefinitionExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs new file mode 100644 index 0000000..fa3b219 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs @@ -0,0 +1,141 @@ +using MonoFN.Cecil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.CodeGenerating.Helping.Extension +{ + + internal static class TypeReferenceExtensionsOld + { + + /// + /// 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) + { + if (typeRef is ArrayType && typeRef.Name.Contains("[][]")) + return true; + + return false; + } + + + /// + /// 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..a5a1901 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2344f5ab0fda07b498c03fbe0e082c14 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Extension/TypeReferenceExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs new file mode 100644 index 0000000..241fa23 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs @@ -0,0 +1,1454 @@ +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 FishNet.Utility.Performance; +using GameKit.Dependencies.Utilities; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices.ComTypes; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine; +using SR = System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class GeneralHelper : CodegenBase + { + #region Reflection references. + public string ExcludeSerializationAttribute_FullName; + public string NotSerializerAttribute_FullName; + public MethodReference Extension_Attribute_Ctor_MethodRef; + public MethodReference BasicQueue_Clear_MethodRef; + public TypeReference List_TypeRef; + public TypeReference RingBuffer_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 GenericInstanceType ArraySegment_Byte_Git; + public MethodReference InstanceFinder_NetworkManager_MethodRef; + public MethodReference NetworkBehaviour_CanLog_MethodRef; + public MethodReference NetworkBehaviour_NetworkManager_MethodRef; + public MethodReference NetworkManager_Log_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 PublicPropertyComparer_Compare_Set_MethodRef; + public MethodReference PublicPropertyComparer_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; + public TypeReference ObjectCaches_TypeRef; + + private Dictionary _importedTypeReferences = new(); + private Dictionary _importedFieldReferences = new(); + private Dictionary _methodReferenceResolves = new(); + private Dictionary _typeReferenceResolves = new(); + private Dictionary _fieldReferenceResolves = new(); + private Dictionary _comparerDelegates = new(); + private MethodReference _objectCaches_Retrieve_MethodRef; + #endregion + + #region Const. + public const string UNITYENGINE_ASSEMBLY_PREFIX = "UnityEngine."; + #endregion + + public override bool ImportReferences() + { + Type tmpType; + TypeReference tmpTr; + 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]); + + ExcludeSerializationAttribute_FullName = typeof(ExcludeSerializationAttribute).FullName; + //NotSerializerAttribute_FullName = typeof(NotSerializerAttribute).FullName; + + TypeReference _objectCaches_TypeRef = base.ImportReference(typeof(ObjectCaches<>)); + _objectCaches_Retrieve_MethodRef = _objectCaches_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(ObjectCaches.Retrieve)); + + tmpType = typeof(BasicQueue<>); + base.ImportReference(tmpType); + foreach (SR.MethodInfo mi in tmpType.GetMethods()) + { + if (mi.Name == nameof(BasicQueue.Clear)) + BasicQueue_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.GetDefaultConstructor(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 networkManagerExtensionsType = typeof(NetworkManagerExtensions); + foreach (SR.MethodInfo methodInfo in networkManagerExtensionsType.GetMethods()) + { + //These extension methods will have two parameters: the type extension is for, and value. + if (methodInfo.GetParameters().Length == 2) + { + if (methodInfo.Name == nameof(NetworkManagerExtensions.Log)) + NetworkManager_Log_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(NetworkManagerExtensions.LogWarning)) + NetworkManager_LogWarning_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(NetworkManagerExtensions.LogError)) + NetworkManager_LogError_MethodRef = base.ImportReference(methodInfo); + } + } + + //ArraySegment + TypeReference arraySegmentTr = base.ImportReference(typeof(ArraySegment<>)); + ArraySegment_Byte_Git = arraySegmentTr.MakeGenericInstanceType(new TypeReference[] { GetTypeReference(typeof(byte)) }); + + //Lists. + tmpType = typeof(List<>); + List_TypeRef = base.ImportReference(tmpType); + tmpType = typeof(RingBuffer<>); + RingBuffer_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 ppComparerType = typeof(PublicPropertyComparer<>); + GeneratedComparer_TypeRef = base.ImportReference(ppComparerType); + System.Reflection.PropertyInfo pi; + pi = ppComparerType.GetProperty(nameof(PublicPropertyComparer.Compare)); + PublicPropertyComparer_Compare_Set_MethodRef = base.ImportReference(pi.GetSetMethod()); + pi = ppComparerType.GetProperty(nameof(PublicPropertyComparer.IsDefault)); + PublicPropertyComparer_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(Extension_Attribute_Ctor_MethodRef); + md.CustomAttributes.Add(ca); + } + + #region HasExcludeSerializationAttribute + /// + /// Returns if typeDef should be ignored. + /// + /// + /// + public bool HasExcludeSerializationAttribute(TypeDefinition typeDef) + { + foreach (CustomAttribute item in typeDef.CustomAttributes) + { + if (item.AttributeType.FullName == ExcludeSerializationAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool HasExcludeSerializationAttribute(SR.MethodInfo methodInfo) + { + foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes) + { + if (item.AttributeType.FullName == ExcludeSerializationAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool HasExcludeSerializationAttribute(MethodDefinition methodDef) + { + foreach (CustomAttribute item in methodDef.CustomAttributes) + { + if (item.AttributeType.FullName == ExcludeSerializationAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool HasExcludeSerializationAttribute(FieldDefinition fieldDef) + { + foreach (CustomAttribute item in fieldDef.CustomAttributes) + { + if (item.AttributeType.FullName == ExcludeSerializationAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool HasExcludeSerializationAttribute(PropertyDefinition propDef) + { + foreach (CustomAttribute item in propDef.CustomAttributes) + { + if (item.AttributeType.FullName == ExcludeSerializationAttribute_FullName) + return true; + } + + return false; + } + #endregion + + #region NotSerializableAttribute + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool HasNotSerializableAttribute(SR.MethodInfo methodInfo) + { + foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes) + { + if (item.AttributeType.FullName == NotSerializerAttribute_FullName) + return true; + } + + return false; + } + + /// + /// Returns if type uses CodegenExcludeAttribute. + /// + public bool HasNotSerializableAttribute(MethodDefinition methodDef) + { + foreach (CustomAttribute item in methodDef.CustomAttributes) + { + if (item.AttributeType.FullName == NotSerializerAttribute_FullName) + return true; + } + + return false; + } + #endregion + + /// + /// 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 = GetGenericList(dataTr); + MethodReference lstDataRemoveRangeMr = base.GetClass().List_RemoveRange_MethodRef.MakeHostInstanceGeneric(base.Session, dataListGit); + + List insts = new(); + 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. + /// + public GenericInstanceType GetGenericList(TypeReference dataTr) + { + TypeReference typeTr = base.ImportReference(typeof(List<>)); + return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + + /// + /// Outputs generic Dictionary for keyTr and valueTr. + /// + public GenericInstanceType GetGenericDictionary(TypeReference keyTr, TypeReference valueTr) + { + TypeReference typeTr = base.ImportReference(typeof(Dictionary<,>)); + return typeTr.MakeGenericInstanceType(new TypeReference[] { keyTr, valueTr }); + } + + /// + /// Outputs generic RingBuffer for dataTr. + /// + public GenericInstanceType GetGenericRingBuffer(TypeReference dataTr) + { + TypeReference typeTr = base.ImportReference(typeof(RingBuffer<>)); + return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + + /// + /// Gets a generic instance of any type with optional arguments. + /// + public GenericInstanceType GetGenericType(Type type, params TypeReference[] datasTr) + { + TypeReference typeTr = base.ImportReference(type); + return typeTr.MakeGenericInstanceType(datasTr); + } + + /// + /// Outputs generic BasicQueue for dataTr. + /// + public GenericInstanceType GetGenericBasicQueue(TypeReference dataTr) + { + TypeReference typeTr = base.ImportReference(typeof(BasicQueue<>)); + return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + + + /// + /// Copies one method to another while transferring diagnostic paths. + /// + public void CopyIntoMethod(MethodDefinition originalMethodDef, MethodDefinition targetMethodDef) + { + TypeDefinition typeDef = originalMethodDef.DeclaringType; + + + (targetMethodDef.Body, originalMethodDef.Body) = (originalMethodDef.Body, targetMethodDef.Body); + //Move over all the debugging information + foreach (SequencePoint sequencePoint in originalMethodDef.DebugInformation.SequencePoints) + targetMethodDef.DebugInformation.SequencePoints.Add(sequencePoint); + originalMethodDef.DebugInformation.SequencePoints.Clear(); + + foreach (CustomDebugInformation customInfo in originalMethodDef.CustomDebugInformations) + targetMethodDef.CustomDebugInformations.Add(customInfo); + originalMethodDef.CustomDebugInformations.Clear(); + //Swap debuginformation scope. + (originalMethodDef.DebugInformation.Scope, targetMethodDef.DebugInformation.Scope) = (targetMethodDef.DebugInformation.Scope, originalMethodDef.DebugInformation.Scope); + } + + + /// + /// 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) + md.Body.Instructions.Clear(); + + CopyIntoMethod(originalMd, md); + + return md; + } + + /// + /// Copies one method to another while transferring diagnostic paths. + /// + public MethodDefinition CopyMethodSignature(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; + + 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(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(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(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(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(".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. + /// + public List LogMessage(MethodDefinition md, string message, LoggingType loggingType) + { + ILProcessor processor = md.Body.GetILProcessor(); + List instructions = new(); + 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) + { + instructions.Add(processor.Create(OpCodes.Ldnull)); + instructions.Add(processor.Create(OpCodes.Stloc, networkManagerVd)); + } + //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)); + } + + MethodReference methodRef; + if (loggingType == LoggingType.Common) + methodRef = NetworkManager_Log_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; + } + + /// + /// 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(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(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(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 an object or collection of typeDef using cachces. + /// + /// + /// + /// + public void SetVariableDefinitionFromCaches(ILProcessor processor, VariableDefinition variableDef, TypeDefinition typeDef) + { + TypeReference dataTr = variableDef.VariableType; + GenericInstanceType git = ObjectCaches_TypeRef.MakeGenericInstanceType(new TypeReference[] { dataTr }); + + MethodReference genericInstanceMethod = _objectCaches_Retrieve_MethodRef.MakeHostInstanceGeneric(base.Session, git); + processor.Emit(OpCodes.Call, genericInstanceMethod); + processor.Emit(OpCodes.Stloc, variableDef); + } + + /// + /// Initializes variableDef as a new object or collection of typeDef using instantiation. + /// + /// + /// + /// + 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.GetDefaultConstructor(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(); + //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) + { + 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 arrays. 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 falseLdcInst = processor.Create(OpCodes.Ldc_I4_0); + + //Non-value type null check. + if (!dataTr.IsValueType) + { + GeneralHelper gh = base.GetClass(); + + Instruction checkNullAndNotNullInst = Instruction.Create(OpCodes.Nop); + + VariableDefinition isNullV0 = gh.CreateVariable(comparerMd, typeof(bool)); + VariableDefinition isNullV1 = gh.CreateVariable(comparerMd, typeof(bool)); + + //isNull0 = (value0 == null); + processor.Emit(OpCodes.Ldarg, v0Pd); + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Stloc, isNullV0); + //isNull1 = (value1 == null); + processor.Emit(OpCodes.Ldarg, v1Pd); + processor.Emit(OpCodes.Ldnull); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Stloc, isNullV1); + + //If (isNull0 && isNull1) return true; + processor.Emit(OpCodes.Ldloc, isNullV0); + processor.Emit(OpCodes.Ldloc, isNullV1); + processor.Emit(OpCodes.And); + processor.Emit(OpCodes.Brfalse, checkNullAndNotNullInst); + processor.Emit(OpCodes.Ldc_I4_1); + processor.Emit(OpCodes.Ret); + //Skip past ret here. + processor.Append(checkNullAndNotNullInst); + + //bool isNullOpposing = (isNull0 != isNull1); + VariableDefinition isNullOpposingVd = gh.CreateVariable(comparerMd, typeof(bool)); + processor.Emit(OpCodes.Ldloc, isNullV0); + processor.Emit(OpCodes.Ldloc, isNullV1); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Ldc_I4_0); + processor.Emit(OpCodes.Ceq); + processor.Emit(OpCodes.Stloc, isNullOpposingVd); + + + Instruction checkPropertiesInst = Instruction.Create(OpCodes.Nop); + //if (isNullOpposing) return false; + processor.Emit(OpCodes.Ldloc, isNullOpposingVd); + processor.Emit(OpCodes.Brfalse, checkPropertiesInst); + processor.Emit(OpCodes.Ldc_I4_0); + processor.Emit(OpCodes.Ret); + //Skip past ret here. + processor.Append(checkPropertiesInst); + } + + //Fields. + foreach (FieldDefinition fieldDef in dataTr.FindAllSerializableFields(base.Session, null, WriterProcessor.EXCLUDED_ASSEMBLY_PREFIXES)) + { + FieldReference fr = base.ImportReference(fieldDef); + MethodDefinition recursiveMd = CreateEqualityComparer(fieldDef.FieldType); + if (recursiveMd == null) + break; + processor.Append(GetLoadParameterInstruction(comparerMd, v0Pd)); + processor.Emit(OpCodes.Ldfld, fr); + processor.Append(GetLoadParameterInstruction(comparerMd, v1Pd)); + processor.Emit(OpCodes.Ldfld, fr); + 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(falseLdcInst); + 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, falseLdcInst); + } + //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, falseLdcInst); + } + } + } + //Value types do not need to check custom comparers. + else + { + processor.Emit(OpCodes.Bne_Un, falseLdcInst); + } + } + } + + 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(); + 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 = PublicPropertyComparer_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(); + 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 = PublicPropertyComparer_IsDefault_Set_MethodRef.MakeHostInstanceGeneric(base.Session, git); + insts.Add(processor.Create(OpCodes.Call, isDefaultMr)); + processor.InsertFirst(insts); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta new file mode 100644 index 0000000..ee831d4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6139ff104f3c24442b26dbc4e40d5ce5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/GeneralHelper.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs new file mode 100644 index 0000000..0028ea3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs @@ -0,0 +1,471 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing; +using FishNet.Configuring; +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Object.Delegating; +using FishNet.Object.Helping; +using FishNet.Object.Prediction.Delegating; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using MethodAttributes = MonoFN.Cecil.MethodAttributes; + +namespace FishNet.CodeGenerating.Helping +{ + internal class NetworkBehaviourHelper : CodegenBase + { + #region Reflection references. + //Names. + internal string FullName; + //Prediction. + + public MethodReference Replicate_Reader_MethodRef; + public MethodReference Reconcile_Server_MethodRef; + //public FieldReference UsesPrediction_FieldRef; + public MethodReference EmptyReplicatesQueueIntoHistory_Start_MethodRef; + public MethodReference EmptyReplicatesQueueIntoHistory_MethodRef; + public MethodReference Reconcile_Client_Start_MethodRef; + public MethodReference Replicate_Replay_Start_MethodRef; + public MethodReference Replicate_Current_MethodRef; + public MethodReference Reconcile_Client_MethodRef; + public MethodReference Reconcile_Current_MethodRef; + public MethodReference ClearReplicateCache_Internal_MethodRef; + public MethodReference Replicate_Replay_MethodRef; + public MethodReference Reconcile_Reader_MethodRef; + public MethodReference RegisterReplicateRpc_MethodRef; + public MethodReference RegisterReconcileRpc_MethodRef; + public MethodReference ReplicateRpcDelegate_Ctor_MethodRef; + public MethodReference ReconcileRpcDelegate_Ctor_MethodRef; + //public MethodReference Replicate_Server_SendToSpectators_MethodRef; + //RPCs. + public MethodReference SendServerRpc_MethodRef; + public MethodReference SendObserversRpc_MethodRef; + public MethodReference SendTargetRpc_MethodRef; + public MethodReference RegisterServerRpc_MethodRef; + public MethodReference RegisterObserversRpc_MethodRef; + public MethodReference RegisterTargetRpc_MethodRef; + public MethodReference ServerRpcDelegate_Ctor_MethodRef; + public MethodReference ClientRpcDelegate_Ctor_MethodRef; + //Is checks. + public MethodReference IsClientInitialized_MethodRef; + public MethodReference IsOwner_MethodRef; + public MethodReference IsServerInitialized_MethodRef; + public MethodReference IsHost_MethodRef; + public MethodReference IsNetworked_MethodRef; + //Misc. + public TypeReference TypeRef; + public MethodReference OwnerMatches_MethodRef; + public MethodReference LocalConnection_MethodRef; + public MethodReference Owner_MethodRef; + public MethodReference NetworkInitializeIfDisabled_MethodRef; + //TimeManager. + public MethodReference TimeManager_MethodRef; + #endregion + + #region Const. + internal const uint MAX_SYNCTYPE_ALLOWANCE = byte.MaxValue; + internal const uint MAX_RPC_ALLOWANCE = ushort.MaxValue; + internal const uint MAX_PREDICTION_ALLOWANCE = byte.MaxValue; + internal const string AWAKE_METHOD_NAME = "Awake"; + internal const string DISABLE_LOGGING_TEXT = "This message may be disabled by setting the Logging field in your attribute to LoggingType.Off"; + #endregion + + public override bool ImportReferences() + { + Type networkBehaviourType = typeof(NetworkBehaviour); + TypeRef = base.ImportReference(networkBehaviourType); + FullName = networkBehaviourType.FullName; + base.ImportReference(networkBehaviourType); + + //ServerRpcDelegate and ClientRpcDelegate constructors. + ServerRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ServerRpcDelegate).GetConstructors().First()); + ClientRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ClientRpcDelegate).GetConstructors().First()); + //Prediction Rpc delegate constructors. + ReplicateRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ReplicateRpcDelegate).GetConstructors().First()); + ReconcileRpcDelegate_Ctor_MethodRef = base.ImportReference(typeof(ReconcileRpcDelegate).GetConstructors().First()); + + foreach (MethodInfo mi in networkBehaviourType.GetMethods((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))) + { + if (mi.Name == nameof(NetworkBehaviour.GetIsNetworked)) + IsNetworked_MethodRef = base.ImportReference(mi); + //CreateDelegates. + else if (mi.Name == nameof(NetworkBehaviour.RegisterServerRpc)) + RegisterServerRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.RegisterObserversRpc)) + RegisterObserversRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.RegisterTargetRpc)) + RegisterTargetRpc_MethodRef = base.ImportReference(mi); + //Prediction delegates. + else if (mi.Name == nameof(NetworkBehaviour.RegisterReplicateRpc)) + RegisterReplicateRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.RegisterReconcileRpc)) + RegisterReconcileRpc_MethodRef = base.ImportReference(mi); + //SendRpcs. + else if (mi.Name == nameof(NetworkBehaviour.SendServerRpc)) + SendServerRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.SendObserversRpc)) + SendObserversRpc_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.SendTargetRpc)) + SendTargetRpc_MethodRef = base.ImportReference(mi); + //Misc. + else if (mi.Name == nameof(NetworkBehaviour.OwnerMatches)) + OwnerMatches_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.NetworkInitializeIfDisabled)) + NetworkInitializeIfDisabled_MethodRef = base.ImportReference(mi); + //Prediction + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Current)) + Replicate_Current_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Replay_Start)) + Replicate_Replay_Start_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.EmptyReplicatesQueueIntoHistory)) + EmptyReplicatesQueueIntoHistory_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.EmptyReplicatesQueueIntoHistory_Start)) + EmptyReplicatesQueueIntoHistory_Start_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client_Start)) + Reconcile_Client_Start_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Replay)) + Replicate_Replay_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Replicate_Reader)) + Replicate_Reader_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Reader)) + Reconcile_Reader_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Server)) + Reconcile_Server_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Client)) + Reconcile_Client_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.Reconcile_Current)) + Reconcile_Current_MethodRef = base.ImportReference(mi); + else if (mi.Name == nameof(NetworkBehaviour.ClearReplicateCache_Internal)) + ClearReplicateCache_Internal_MethodRef = base.ImportReference(mi); + } + + foreach (PropertyInfo pi in networkBehaviourType.GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))) + { + //Server/Client states. + if (pi.Name == nameof(NetworkBehaviour.IsClientInitialized)) + IsClientInitialized_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.IsServerInitialized)) + IsServerInitialized_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.IsHostStarted)) + IsHost_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.IsOwner)) + IsOwner_MethodRef = base.ImportReference(pi.GetMethod); + //Owner. + else if (pi.Name == nameof(NetworkBehaviour.Owner)) + Owner_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkBehaviour.LocalConnection)) + LocalConnection_MethodRef = base.ImportReference(pi.GetMethod); + //Misc. + else if (pi.Name == nameof(NetworkBehaviour.TimeManager)) + TimeManager_MethodRef = base.ImportReference(pi.GetMethod); + } + + return true; + } + + /// + /// 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(); + 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(); + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldc_I4, (int)methodHash)); + + /* Create delegate and call NetworkBehaviour method. */ + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldftn, readerMethodDef)); + //Server. + if (rpcType == RpcType.Server) + { + insts.Add(processor.Create(OpCodes.Newobj, ServerRpcDelegate_Ctor_MethodRef)); + insts.Add(processor.Create(OpCodes.Call, RegisterServerRpc_MethodRef)); + } + //Observers. + else if (rpcType == RpcType.Observers) + { + insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegate_Ctor_MethodRef)); + insts.Add(processor.Create(OpCodes.Call, RegisterObserversRpc_MethodRef)); + } + //Target + else if (rpcType == RpcType.Target) + { + insts.Add(processor.Create(OpCodes.Newobj, ClientRpcDelegate_Ctor_MethodRef)); + insts.Add(processor.Create(OpCodes.Call, RegisterTargetRpc_MethodRef)); + } + + /* Has to be done last. This allows the NetworkBehaviour to + * initialize it's fields first. */ + processor.InsertLast(insts); + } + + /// + /// 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(); + /* 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. + /// + /// When true InstanceFinder.IsClient is used, when false base.IsClientInitialized is used. + internal void CreateIsClientCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked) + { + /* This is placed after the if check. + * Should the if check pass then code + * jumps to this instruction. */ + ILProcessor processor = methodDef.Body.GetILProcessor(); + Instruction endIf = processor.Create(OpCodes.Nop); + + List instructions = new(); + + if (checkIsNetworked) + instructions.AddRange(CreateIsNetworkedCheck(methodDef, endIf)); + + //Checking against the NetworkObject. + if (!useStatic) + { + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this + //If (!base.IsClient) + instructions.Add(processor.Create(OpCodes.Call, IsClientInitialized_MethodRef)); + } + //Checking instanceFinder. + else + { + instructions.Add(processor.Create(OpCodes.Call, base.GetClass().InstanceFinder_IsClient_MethodRef)); + } + instructions.Add(processor.Create(OpCodes.Brtrue, endIf)); + //If warning then also append warning text. + if (loggingType != LoggingType.Off) + { + string msg = $"Cannot complete action because client is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component."; + instructions.AddRange(base.GetClass().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. + /// + /// When true InstanceFinder.IsServer is used, when false base.IsServerInitialized is used. + internal void CreateIsServerCheck(MethodDefinition methodDef, LoggingType loggingType, bool useStatic, bool insertFirst, bool checkIsNetworked) + { + /* This is placed after the if check. + * Should the if check pass then code + * jumps to this instruction. */ + ILProcessor processor = methodDef.Body.GetILProcessor(); + Instruction endIf = processor.Create(OpCodes.Nop); + + List instructions = new(); + + if (checkIsNetworked) + instructions.AddRange(CreateIsNetworkedCheck(methodDef, endIf)); + + if (!useStatic) + { + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //argument: this + //If (!base.IsServer) + instructions.Add(processor.Create(OpCodes.Call, IsServerInitialized_MethodRef)); + } + //Checking instanceFinder. + else + { + instructions.Add(processor.Create(OpCodes.Call, base.GetClass().InstanceFinder_IsServer_MethodRef)); + } + instructions.Add(processor.Create(OpCodes.Brtrue, endIf)); + //If warning then also append warning text. + if (loggingType != LoggingType.Off) + { + string msg = $"Cannot complete action because server is not active. This may also occur if the object is not yet initialized, has deinitialized, or if it does not contain a NetworkObject component."; + instructions.AddRange(base.GetClass().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 call to base.IsNetworked and returns instructions. + /// + private List CreateIsNetworkedCheck(MethodDefinition methodDef, Instruction endIfInst) + { + List insts = new(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Call, base.GetClass().IsNetworked_MethodRef)); + insts.Add(processor.Create(OpCodes.Brfalse, endIfInst)); + + return insts; + } + + /// + /// Creates a return using the ReturnType for methodDef. + /// + /// + /// + /// + public List CreateRetDefault(MethodDefinition methodDef, ModuleDefinition importReturnModule = null) + { + ILProcessor processor = methodDef.Body.GetILProcessor(); + List instructions = new(); + //If requires a value return. + if (methodDef.ReturnType != methodDef.Module.TypeSystem.Void) + { + //Import type first. + methodDef.Module.ImportReference(methodDef.ReturnType); + if (importReturnModule != null) + importReturnModule.ImportReference(methodDef.ReturnType); + VariableDefinition vd = base.GetClass().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..07d134b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0c42e06349d6890459a155a2afe17d41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/NetworkBehaviourHelper.cs + uploadId: 762203 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..ae1b8b1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6d061dda8ed87ed48a08020942ad63f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/NetworkConnectionImports.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs new file mode 100644 index 0000000..cdad964 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs @@ -0,0 +1,99 @@ +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Connection; +using FishNet.Object; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using MonoFN.Cecil; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class ObjectHelper : CodegenBase + { + #region Reflection references. + //Fullnames. + public string SyncVar_Name { get; private set; } + public string SyncList_Name { get; private set; } + public string SyncDictionary_Name { get; private set; } + public string SyncHashSet_Name { get; private set; } + //Is checks. + public MethodReference InstanceFinder_IsServer_MethodRef { get; private set; } + public MethodReference InstanceFinder_IsClient_MethodRef { get; private set; } + //Misc. + public TypeReference NetworkBehaviour_TypeRef { get; private set; } + public MethodReference NetworkConnection_IsValid_MethodRef { get; private set; } + public MethodReference NetworkConnection_IsActive_MethodRef { get; private set; } + public MethodReference Dictionary_Add_UShort_SyncBase_MethodRef { get; private set; } + public MethodReference NetworkConnection_GetIsLocalClient_MethodRef { get; private set; } + #endregion + + public override bool ImportReferences() + { + Type tmpType; + /* SyncType names */ + //SyncVar. + tmpType = typeof(SyncVar<>); + base.ImportReference(tmpType); + SyncVar_Name = tmpType.Name; + //SyncList. + tmpType = typeof(SyncList<>); + base.ImportReference(tmpType); + SyncList_Name = tmpType.Name; + //SyncDictionary. + tmpType = typeof(SyncDictionary<,>); + base.ImportReference(tmpType); + SyncDictionary_Name = tmpType.Name; + //SyncHashSet. + tmpType = typeof(SyncHashSet<>); + base.ImportReference(tmpType); + SyncHashSet_Name = tmpType.Name; + + NetworkBehaviour_TypeRef = base.ImportReference(typeof(NetworkBehaviour)); + + tmpType = typeof(NetworkConnection); + TypeReference networkConnectionTr = base.ImportReference(tmpType); + foreach (PropertyDefinition item in networkConnectionTr.CachedResolve(base.Session).Properties) + { + if (item.Name == nameof(NetworkConnection.IsLocalClient)) + NetworkConnection_GetIsLocalClient_MethodRef = base.ImportReference(item.GetMethod); + } + + //Dictionary.Add(ushort, SyncBase). + Type dictType = typeof(Dictionary); + 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.IsClientStarted)) + InstanceFinder_IsClient_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(InstanceFinder.IsServerStarted)) + InstanceFinder_IsServer_MethodRef = base.ImportReference(pi.GetMethod); + } + + //NetworkConnection. + foreach (PropertyInfo pi in typeof(NetworkConnection).GetProperties((BindingFlags.Static | BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic))) + { + if (pi.Name == nameof(NetworkConnection.IsValid)) + NetworkConnection_IsValid_MethodRef = base.ImportReference(pi.GetMethod); + else if (pi.Name == nameof(NetworkConnection.IsActive)) + NetworkConnection_IsActive_MethodRef = base.ImportReference(pi.GetMethod); + } + + return true; + } + + } +} \ 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..0012737 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 033da35314653aa4689b4582e7929295 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/ObjectHelper.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs new file mode 100644 index 0000000..fcc89c0 --- /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(); + + //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..bfbe9d3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 28ae27f7bc8e89547a606262508fdd36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/PhysicsHelper.cs + uploadId: 762203 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..af8537a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e9a06c812bf785a44a38a5852ff866d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/PredictedObjectHelper.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs new file mode 100644 index 0000000..6138987 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs @@ -0,0 +1,63 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Connection; +using FishNet.Serializing; +using MonoFN.Cecil; +using System; +using System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class ReaderImports : CodegenBase + { + #region Reflection references. + public TypeReference PooledReader_TypeRef; + public TypeReference Reader_TypeRef; + public TypeReference NetworkConnection_TypeRef; + public MethodReference PooledReader_ReadNetworkBehaviour_MethodRef; + public MethodReference Reader_ReadPackedWhole_MethodRef; + public MethodReference Reader_ReadDictionary_MethodRef; + public MethodReference Reader_ReadList_MethodRef; + public MethodReference Reader_ReadArray_MethodRef; + public TypeReference GenericReader_TypeRef; + + public MethodReference GenericReader_Read_MethodRef; + #endregion + + /// + /// 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)); + GenericReader_TypeRef = base.ImportReference(typeof(GenericReader<>)); + + TypeDefinition genericWriterTd = GenericReader_TypeRef.CachedResolve(base.Session); + GenericReader_Read_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericReader.SetRead))); + + Type pooledReaderType = typeof(PooledReader); + foreach (MethodInfo methodInfo in pooledReaderType.GetMethods()) + { + int parameterCount = methodInfo.GetParameters().Length; + /* Special methods. */ + if (methodInfo.Name == nameof(PooledReader.ReadUnsignedPackedWhole)) + Reader_ReadPackedWhole_MethodRef = base.ImportReference(methodInfo); + //Relay readers. + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadDictionary)) + Reader_ReadDictionary_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadList)) + Reader_ReadList_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 0 && methodInfo.Name == nameof(PooledReader.ReadArrayAllocated)) + Reader_ReadArray_MethodRef = base.ImportReference(methodInfo); + } + + return true; + } + } +} \ 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..0c55592 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 350861dcbcbabc447acd37e4310b0697 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/ReaderImports.cs + uploadId: 762203 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..0d973f6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 11dbcc0798e079e4a85fe98dfc9fe23a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/TimeManagerHelper.cs + uploadId: 762203 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..80b8345 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6ced44bfdb3068f4cb7513c9be85729a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/TransportHelper.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed.meta b/Assets/FishNet/CodeGenerating/Helpers/Typed.meta new file mode 100644 index 0000000..e2860e4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c480d34b00248444d91a61e015b14e2a +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..92a4cb4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2b30600f0fdb27c4fb86c310b08f43b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/Comparers.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs new file mode 100644 index 0000000..b5f6e72 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs @@ -0,0 +1,186 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Object; +using FishNet.Serializing.Helping; +using FishNet.Utility.Performance; +using MonoFN.Cecil; +using System.Collections.Generic; + +namespace FishNet.CodeGenerating.Helping +{ + + + internal class GeneratorHelper : CodegenBase + { + /// + /// 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(ExcludeSerializationAttribute))) + return SerializerType.Invalid; + } + } + + //By reference. + if (objectTr.IsByReference) + { + base.LogError($"{errorPrefix}Cannot pass {objectTr.Name} by reference."); + return SerializerType.Invalid; + } + /* Arrays have to be processed first because it's possible for them to meet other conditions + * below and be processed wrong. */ + else if (objectTr.IsArray) + { + if (objectTr.IsMultidimensionalArray()) + { + base.LogError($"{errorPrefix}{objectTr.Name} is an unsupported type. Multidimensional arrays are not supported."); + return SerializerType.Invalid; + } + else + { + return SerializerType.Array; + } + } + //Enum. + else if (objectTd.IsEnum) + { + return SerializerType.Enum; + } + else if (objectTd.Is(typeof(Dictionary<,>))) + { + return SerializerType.Dictionary; + } + else if (objectTd.Is(typeof(List<>))) + { + return SerializerType.List; + } + else if (objectTd.InheritsFrom(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..9aa36b6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9b1882eac63e3d94aad8f41915bc1ab8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/GeneratorHelper.cs + uploadId: 762203 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..9976019 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 357a22940018b8e49976e13272c5b2ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/QOLAttributeType.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs new file mode 100644 index 0000000..787d647 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs @@ -0,0 +1,19 @@ +namespace FishNet.CodeGenerating.Helping +{ + + internal enum SerializerType + { + Invalid, + Enum, + Array, + List, + 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..ef94778 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e7f1bbf5c398c3e4e92e53ec3e49d5e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SerializatierType.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs new file mode 100644 index 0000000..eb78e46 --- /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(); + } + + +} \ 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..5e1d501 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 55f2e751e4788464b8394f6b8bdb548a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SyncIndexData.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs new file mode 100644 index 0000000..11592b6 --- /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, + Unhandled, + } + +} \ 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..472a586 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 44c753d6ac0c7864c9962d91703b2afe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/Typed/SyncType.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs new file mode 100644 index 0000000..ef32ce0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs @@ -0,0 +1,100 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Object; +using FishNet.Serializing; +using MonoFN.Cecil; +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace FishNet.CodeGenerating.Helping +{ + internal class WriterImports : CodegenBase + { + #region Reflection references. + public MethodReference WriterPool_GetWriter_MethodRef; + public MethodReference WriterPool_GetWriterLength_MethodRef; + public MethodReference Writer_WritePackedWhole_Signed_MethodRef; + public MethodReference Writer_WritePackedWhole_Unsigned_MethodRef; + public TypeReference PooledWriter_TypeRef; + public TypeReference Writer_TypeRef; + public MethodReference PooledWriter_Dispose_MethodRef; + public MethodReference Writer_WriteDictionary_MethodRef; + public MethodReference Writer_WriteList_MethodRef; + public MethodReference Writer_WriteArray_MethodRef; + public TypeReference AutoPackTypeRef; + + public TypeReference GenericWriter_TypeRef; + public MethodReference GenericWriter_Write_MethodRef; + public MethodReference Writer_Write_MethodRef; + #endregion + + /// + /// 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)); + GenericWriter_TypeRef = base.ImportReference(typeof(GenericWriter<>)); + Writer_Write_MethodRef = Writer_TypeRef.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(Writer.Write)); + + + TypeDefinition genericWriterTd = GenericWriter_TypeRef.CachedResolve(base.Session); + GenericWriter_Write_MethodRef = base.ImportReference(genericWriterTd.GetMethod(nameof(GenericWriter.SetWrite))); + + //WriterPool.GetWriter + Type writerPoolType = typeof(WriterPool); + base.ImportReference(writerPoolType); + foreach (var methodInfo in writerPoolType.GetMethods()) + { + if (methodInfo.Name == nameof(WriterPool.Retrieve)) + { + //GetWriter(). + if (methodInfo.GetParameters().Length == 0) + { + WriterPool_GetWriter_MethodRef = base.ImportReference(methodInfo); + } + //GetWriter(?). + else if (methodInfo.GetParameters().Length == 1) + { + ParameterInfo pi = methodInfo.GetParameters()[0]; + //GetWriter(int). + if (pi.ParameterType == typeof(int)) + WriterPool_GetWriterLength_MethodRef = base.ImportReference(methodInfo); + } + } + } + + WriterProcessor gwh = base.GetClass(); + Type pooledWriterType = typeof(PooledWriter); + foreach (MethodInfo methodInfo in pooledWriterType.GetMethods()) + { + int parameterCount = methodInfo.GetParameters().Length; + + if (methodInfo.Name == nameof(PooledWriter.Store)) + PooledWriter_Dispose_MethodRef = base.ImportReference(methodInfo); + else if (methodInfo.Name == nameof(PooledWriter.WriteUnsignedPackedWhole)) + { + //todo: check if signed or not and set to signed/unsigned variable. + //do the same changes for methods which call these. + //Writer_WritePackedWhole_MethodRef = base.ImportReference(methodInfo); + } + //Relay writers. + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteDictionary)) + Writer_WriteDictionary_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteList)) + Writer_WriteList_MethodRef = base.ImportReference(methodInfo); + else if (parameterCount == 1 && methodInfo.Name == nameof(PooledWriter.WriteArray)) + Writer_WriteArray_MethodRef = base.ImportReference(methodInfo); + } + + return true; + } + + } + +} \ 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..a527322 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 425638e29bab6f1488e8865c9e3f8b57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Helpers/WriterImports.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/ILCore.meta b/Assets/FishNet/CodeGenerating/ILCore.meta new file mode 100644 index 0000000..584749a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b1808ca0399d069499d3ecb4e031c3e2 +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..67a9a2f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs @@ -0,0 +1,425 @@ +using FishNet.Broadcast; +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing; +using FishNet.CodeGenerating.Processing.Rpc; +using FishNet.Configuring; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Unity.CompilationPipeline.Common.ILPostProcessing; + +namespace FishNet.CodeGenerating.ILCore +{ + public class FishNetILPP : ILPostProcessor + { + #region Const. + internal const string RUNTIME_ASSEMBLY_NAME = "FishNet.Runtime"; + #endregion + + public override bool WillProcess(ICompiledAssembly compiledAssembly) + { + if (compiledAssembly.Name.StartsWith("Unity.")) + return false; + if (compiledAssembly.Name.StartsWith("UnityEngine.")) + return false; + if (compiledAssembly.Name.StartsWith("UnityEditor.")) + return false; + if (compiledAssembly.Name.Contains("Editor")) + return false; + + /* This line contradicts the one below where referencesFishNet + * becomes true if the assembly is FishNetAssembly. This is here + * intentionally to stop codegen from running on the runtime + * fishnet assembly, but the option below is for debugging. I would + * comment out this check if I wanted to compile fishnet runtime. */ + //if (CODEGEN_THIS_NAMESPACE.Length == 0) + //{ + // if (compiledAssembly.Name == RUNTIME_ASSEMBLY_NAME) + // return false; + //} + bool referencesFishNet = FishNetILPP.IsFishNetAssembly(compiledAssembly) || compiledAssembly.References.Any(filePath => Path.GetFileNameWithoutExtension(filePath) == RUNTIME_ASSEMBLY_NAME); + return referencesFishNet; + } + + public override ILPostProcessor GetInstance() => this; + + public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) + { + AssemblyDefinition assemblyDef = ILCoreHelper.GetAssemblyDefinition(compiledAssembly); + if (assemblyDef == null) + return null; + + //Check WillProcess again; somehow certain editor scripts skip the WillProcess check. + if (!WillProcess(compiledAssembly)) + return null; + + CodegenSession session = new(); + if (!session.Initialize(assemblyDef.MainModule)) + return null; + + + + bool modified = false; + + bool fnAssembly = IsFishNetAssembly(compiledAssembly); + if (fnAssembly) + modified |= ModifyMakePublicMethods(session); + /* If one or more scripts use RPCs but don't inherit NetworkBehaviours + * then don't bother processing the rest. */ + if (session.GetClass().NonNetworkBehaviourHasInvalidAttributes(session.Module.Types)) + return new(null, session.Diagnostics); + + modified |= session.GetClass().Process(); + modified |= session.GetClass().Process(); + modified |= CreateDeclaredSerializerDelegates(session); + modified |= CreateDeclaredSerializers(session); + modified |= CreateDeclaredComparerDelegates(session); + modified |= CreateIncludeSerializationSerializers(session); + modified |= CreateIBroadcast(session); +#if !DISABLE_QOL_ATTRIBUTES + modified |= CreateQOLAttributes(session); +#endif + modified |= CreateNetworkBehaviours(session); + modified |= CreateSerializerInitializeDelegates(session); + + if (fnAssembly) + { + AssemblyNameReference anr = session.Module.AssemblyReferences.FirstOrDefault(x => x.FullName == session.Module.Assembly.FullName); + if (anr != null) + session.Module.AssemblyReferences.Remove(anr); + } + + /* If there are warnings about SyncVars being in different assemblies. + * This is awful ... codegen would need to be reworked to save + * syncvars across all assemblies so that scripts referencing them from + * another assembly can have it's instructions changed. This however is an immense + * amount of work so it will have to be put on hold, for... a long.. long while. */ + if (session.DifferentAssemblySyncVars.Count > 0) + { + StringBuilder sb = new(); + sb.AppendLine($"Assembly {session.Module.Name} has inherited access to SyncVars in different assemblies. When accessing SyncVars across assemblies be sure to use Get/Set methods withinin the inherited assembly script to change SyncVars. Accessible fields are:"); + + foreach (FieldDefinition item in session.DifferentAssemblySyncVars) + sb.AppendLine($"Field {item.Name} within {item.DeclaringType.FullName} in assembly {item.Module.Name}."); + + session.LogWarning("v------- IMPORTANT -------v"); + session.LogWarning(sb.ToString()); + session.DifferentAssemblySyncVars.Clear(); + } + + //session.LogWarning($"Assembly {compiledAssembly.Name} took {stopwatch.ElapsedMilliseconds}."); + if (!modified) + { + return null; + } + else + { + MemoryStream pe = new(); + MemoryStream pdb = new(); + WriterParameters writerParameters = new() + { + SymbolWriterProvider = new PortablePdbWriterProvider(), + SymbolStream = pdb, + WriteSymbols = true + }; + assemblyDef.Write(pe, writerParameters); + return new(new(pe.ToArray(), pdb.ToArray()), session.Diagnostics); + } + } + + + /// + /// Makees methods public scope which use CodegenMakePublic attribute. + /// + /// + private bool ModifyMakePublicMethods(CodegenSession session) + { + string makePublicTypeFullName = typeof(MakePublicAttribute).FullName; + foreach (TypeDefinition td in session.Module.Types) + { + foreach (CustomAttribute tdCustomAttribute in td.CustomAttributes) + { + if (tdCustomAttribute.AttributeType.FullName == makePublicTypeFullName) + { + td.Attributes &= ~TypeAttributes.NotPublic; + td.Attributes |= TypeAttributes.Public; + } + } + foreach (MethodDefinition md in td.Methods) + { + foreach (CustomAttribute ca in md.CustomAttributes) + { + if (ca.AttributeType.FullName == makePublicTypeFullName) + { + md.Attributes &= ~MethodAttributes.Assembly; + md.Attributes |= MethodAttributes.Public; + } + } + } + } + + //There is always at least one modified. + return true; + } + + /// + /// Creates delegates for user declared serializers. + /// + internal bool CreateDeclaredSerializerDelegates(CodegenSession session) + { + bool modified = false; + + List allTypeDefs = session.Module.Types.ToList(); + foreach (TypeDefinition td in allTypeDefs) + { + if (session.GetClass().HasExcludeSerializationAttribute(td)) + continue; + + if (td.Attributes.HasFlag(WriterProcessor.CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES)) + 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; + + List allTypeDefs = session.Module.Types.ToList(); + foreach (TypeDefinition td in allTypeDefs) + { + if (session.GetClass().HasExcludeSerializationAttribute(td)) + continue; + + if (td.Attributes.HasFlag(WriterProcessor.CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES)) + 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().HasExcludeSerializationAttribute(td)) + continue; + + modified |= session.GetClass().CreateComparerDelegates(td); + } + + return modified; + } + + /// + /// Creates serializers for types that use IncludeSerialization attribute. + /// + private bool CreateIncludeSerializationSerializers(CodegenSession session) + { + string attributeName = typeof(IncludeSerializationAttribute).FullName; + WriterProcessor wp = session.GetClass(); + ReaderProcessor rp = session.GetClass(); + + bool modified = false; + List allTypeDefs = session.Module.Types.ToList(); + foreach (TypeDefinition td in allTypeDefs) + { + if (!CanSerialize()) + continue; + + TypeReference tr = session.ImportReference(td); + if (wp.CreateWriter(tr) != null && rp.CreateReader(tr) != null) + modified = true; + else + session.LogError($"Failed to create serializers for {td.FullName}."); + + bool CanSerialize() + { + foreach (CustomAttribute item in td.CustomAttributes) + { + if (item.AttributeType.FullName == attributeName) + return true; + } + + return false; + } + } + + return modified; + } + + /// + /// Creaters serializers and calls for IBroadcast. + /// + /// + /// + private bool CreateIBroadcast(CodegenSession session) + { + bool modified = false; + + string networkBehaviourFullName = session.GetClass().FullName; + HashSet typeDefs = new(); + foreach (TypeDefinition td in session.Module.Types) + { + TypeDefinition climbTd = td; + do + { + //Reached NetworkBehaviour class. + if (climbTd.FullName == networkBehaviourFullName) + break; + + ///* Check initial class as well all types within + // * the class. Then check all of it's base classes. */ + if (climbTd.ImplementsInterface()) + 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().HasExcludeSerializationAttribute(td)) + continue; + + modified |= session.GetClass().Process(td, codeStripping); + } + + + + return modified; + } + + /// + /// Creates NetworkBehaviour changes. + /// + private bool CreateNetworkBehaviours(CodegenSession session) + { + //Get all network behaviours to process. + List networkBehaviourTypeDefs = session.Module.Types.Where(td => td.IsSubclassOf(session, session.GetClass().FullName)).ToList(); + + /* Remove types which are inherited. This gets the child most networkbehaviours. + * Since processing iterates upward from each child there is no reason + * to include any inherited NBs. */ + RemoveInheritedTypeDefinitions(networkBehaviourTypeDefs); + + foreach (TypeDefinition typeDef in networkBehaviourTypeDefs) + { + session.ImportReference(typeDef); + session.GetClass().ProcessLocal(typeDef); + } + + //Call base methods on RPCs. + foreach (TypeDefinition td in session.Module.Types) + 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(); + /* Remove any networkbehaviour typedefs which are inherited by + * another networkbehaviour typedef. */ + for (int i = 0; i < tds.Count; i++) + { + /* Iterates all base types and + * adds them to inheritedTds so long + * as the base type is not a NetworkBehaviour. */ + TypeDefinition copyTd = tds[i].GetNextBaseTypeDefinition(session); + while (copyTd != null) + { + //Class is NB. + if (copyTd.FullName == session.GetClass().FullName) + break; + + inheritedTds.Add(copyTd); + copyTd = copyTd.GetNextBaseTypeDefinition(session); + } + } + + //Remove all inherited types. + foreach (TypeDefinition item in inheritedTds) + tds.Remove(item); + } + + //Moment a NetworkBehaviour exist the assembly is considered modified. + bool modified = (networkBehaviourTypeDefs.Count > 0); + return modified; + } + + /// + /// Creates generic delegates for all read and write methods. + /// + /// + /// + private bool CreateSerializerInitializeDelegates(CodegenSession session) + { + session.GetClass().CreateInitializeDelegates(); + session.GetClass().CreateInitializeDelegates(); + + return true; + } + + internal static bool IsFishNetAssembly(ICompiledAssembly assembly) => (assembly.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME); + internal static bool IsFishNetAssembly(CodegenSession session) => (session.Module.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME); + internal static bool IsFishNetAssembly(ModuleDefinition moduleDef) => (moduleDef.Assembly.Name.Name == FishNetILPP.RUNTIME_ASSEMBLY_NAME); + } +} \ 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..cc2b128 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f03d76b376c1d5b4591039af6fd4c9e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/ILCore/FishNetILPP.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs b/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs new file mode 100644 index 0000000..63776cc --- /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(compiledAssembly); + ReaderParameters readerParameters = new() + { + SymbolStream = new MemoryStream(compiledAssembly.InMemoryAssembly.PdbData), + SymbolReaderProvider = new PortablePdbReaderProvider(), + AssemblyResolver = assemblyResolver, + ReflectionImporterProvider = new PostProcessorReflectionImporterProvider(), + ReadingMode = ReadingMode.Immediate + }; + + AssemblyDefinition assemblyDefinition = AssemblyDefinition.ReadAssembly(new MemoryStream(compiledAssembly.InMemoryAssembly.PeData), readerParameters); + //Allows us to resolve inside FishNet assembly, such as for components. + assemblyResolver.AddAssemblyDefinitionBeingOperatedOn(assemblyDefinition); + + return assemblyDefinition; + } + + + } + +} \ 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..f8e7bc3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dfcfb917dd9268744962ae61aa0115b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/ILCore/ILCoreHelper.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs new file mode 100644 index 0000000..fcb4c99 --- /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(); + private readonly ICompiledAssembly m_CompiledAssembly; + private AssemblyDefinition m_SelfAssembly; + + public PostProcessorAssemblyResolver(ICompiledAssembly compiledAssembly) + { + m_CompiledAssembly = compiledAssembly; + m_AssemblyReferences = compiledAssembly.References; + } + + public void Dispose() { } + + public AssemblyDefinition Resolve(AssemblyNameReference name) => Resolve(name, new(ReadingMode.Deferred)); + + public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters) + { + lock (m_AssemblyCache) + { + if (name.Name == m_CompiledAssembly.Name) + { + return m_SelfAssembly; + } + + var fileName = FindFile(name); + if (fileName == null) + { + return null; + } + + var lastWriteTime = File.GetLastWriteTime(fileName); + var cacheKey = $"{fileName}{lastWriteTime}"; + if (m_AssemblyCache.TryGetValue(cacheKey, out var result)) + { + return result; + } + + parameters.AssemblyResolver = this; + + var ms = MemoryStreamFor(fileName); + var pdb = $"{fileName}.pdb"; + if (File.Exists(pdb)) + { + parameters.SymbolStream = MemoryStreamFor(pdb); + } + + var assemblyDefinition = AssemblyDefinition.ReadAssembly(ms, parameters); + m_AssemblyCache.Add(cacheKey, assemblyDefinition); + + return assemblyDefinition; + } + } + + private string FindFile(AssemblyNameReference name) + { + var fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.dll"); + if (fileName != null) + { + return fileName; + } + + // perhaps the type comes from an exe instead + fileName = m_AssemblyReferences.FirstOrDefault(r => Path.GetFileName(r) == $"{name.Name}.exe"); + if (fileName != null) + { + return fileName; + } + + //Unfortunately the current ICompiledAssembly API only provides direct references. + //It is very much possible that a postprocessor ends up investigating a type in a directly + //referenced assembly, that contains a field that is not in a directly referenced assembly. + //if we don't do anything special for that situation, it will fail to resolve. We should fix this + //in the ILPostProcessing API. As a workaround, we rely on the fact here that the indirect references + //are always located next to direct references, so we search in all directories of direct references we + //got passed, and if we find the file in there, we resolve to it. + return m_AssemblyReferences + .Select(Path.GetDirectoryName) + .Distinct() + .Select(parentDir => Path.Combine(parentDir, $"{name.Name}.dll")) + .FirstOrDefault(File.Exists); + } + + private static MemoryStream MemoryStreamFor(string fileName) + { + return Retry(10, TimeSpan.FromSeconds(1), () => + { + byte[] byteArray; + using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + byteArray = new byte[fs.Length]; + var readLength = fs.Read(byteArray, 0, (int)fs.Length); + if (readLength != fs.Length) + { + throw new InvalidOperationException("File read length is not full length of file."); + } + } + + return new(byteArray); + }); + } + + private static MemoryStream Retry(int retryCount, TimeSpan waitTime, Func 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..a1c0638 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2c247f4266b2864eb96e6a9ae6557d31 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorAssemblyResolver.cs + uploadId: 762203 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..179c7e4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 484e8ad8c4dde382ea67036b32935ef1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporter.cs + uploadId: 762203 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..ef7aa91 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f9273a5dad109ab0783891e36c983080 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/ILCore/PostProcessorReflectionImporterProvider.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing.meta b/Assets/FishNet/CodeGenerating/Processing.meta new file mode 100644 index 0000000..361cc40 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15ff4c71a3c972440810ac633b69764d +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..382208d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8462034e5255191499a018bd8fbbf751 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/CodegenBase.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs new file mode 100644 index 0000000..51ddaf0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs @@ -0,0 +1,341 @@ + +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.CodeGenerating.Processing +{ + internal class CustomSerializerProcessor : CodegenBase + { + + #region Types. + internal enum ExtensionType + { + None, + Write, + Read + } + + #endregion + + internal bool CreateSerializerDelegates(TypeDefinition typeDef, bool replace) + { + bool modified = false; + /* Find all declared methods and register delegates to them. + * After they are all registered create any custom writers + * needed to complete the declared methods. It's important to + * make generated writers after so that a generated method + * isn't made for a type when the user has already made a declared one. */ + foreach (MethodDefinition methodDef in typeDef.Methods) + { + ExtensionType extensionType = GetExtensionType(methodDef); + if (extensionType == ExtensionType.None) + continue; + if (base.GetClass().HasNotSerializableAttribute(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(); + /* 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().HasNotSerializableAttribute(methodDef)) + continue; + + declaredMethods.Add((methodDef, extensionType)); + modified = true; + } + //Now that all declared are loaded see if any of them need generated serializers. + foreach ((MethodDefinition methodDef, ExtensionType extensionType) in declaredMethods) + CreateSerializers(extensionType, methodDef); + + return modified; + } + + + /// + /// 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.HasNotSerializableAttribute(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) + { + ReaderProcessor rp = base.GetClass(); + WriterProcessor wp = base.GetClass(); + ////If parameterType has user declared do nothing. + //if (wp.IsGlobalSerializer(parameterType)) + // return; + + if (!parameterType.IsGenericParameter && parameterType.CanBeResolved(base.Session)) + { + TypeDefinition typeDefinition = parameterType.CachedResolve(base.Session); + //If class and not value type check for accessible constructor. + if (typeDefinition.IsClass && !typeDefinition.IsValueType) + { + MethodDefinition constructor = typeDefinition.GetDefaultConstructor(base.Session); + //Constructor is inaccessible, cannot create serializer for type. + if (constructor != null && !constructor.IsPublic) + { + base.LogError($"Unable to generator serializers for {typeDefinition.FullName} because it's constructor is not public."); + return; + } + } + + ILProcessor processor = methodDef.Body.GetILProcessor(); + + //Find already existing read or write method. + MethodReference createdMethodRef = (extensionType == ExtensionType.Write) ? + wp.GetWriteMethodReference(parameterType) : + rp.GetReadMethodReference(parameterType); + + //If a created method already exist nothing further is required. + if (createdMethodRef != null) + { + //Replace call to generic with already made serializer. + Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef); + methodDef.Body.Instructions[instructionIndex] = newInstruction; + return; + } + else + { + createdMethodRef = (extensionType == ExtensionType.Write) ? + wp.CreateWriter(parameterType) : + rp.CreateReader(parameterType); + } + + //If method was created. + if (createdMethodRef != null) + { + //Set new instruction. + Instruction newInstruction = processor.Create(OpCodes.Call, createdMethodRef); + methodDef.Body.Instructions[instructionIndex] = newInstruction; + } + } + + } + + /// + /// 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.CUSTOM_WRITER_PREFIX : ReaderProcessor.CUSTOM_READER_PREFIX; + + //Does not contain prefix. + if (methodDef.Name.Length < prefix.Length || methodDef.Name.Substring(0, prefix.Length) != prefix) + return ExtensionType.None; + + //Make sure first parameter is right. + if (methodDef.Parameters.Count >= 1) + { + TypeReference tr = methodDef.Parameters[0].ParameterType; + if (tr.FullName != base.GetClass().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..d810cbc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9269fd8a62199e24c965b4c99b641244 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/CustomSerializerProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs new file mode 100644 index 0000000..0622e14 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs @@ -0,0 +1,503 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing.Rpc; +using FishNet.Configuring; +using FishNet.Object; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Collections.Generic; +using System.Collections.Generic; +using System.Linq; + +namespace FishNet.CodeGenerating.Processing +{ + internal class NetworkBehaviourProcessor : CodegenBase + { + #region Private. + /// + /// Classes which have been processed for all NetworkBehaviour features. + /// + private HashSet _processedClasses = new(); + #endregion + + #region Const. + internal const string NETWORKINITIALIZE_EARLY_INTERNAL_NAME = "NetworkInitialize___Early"; + internal const string NETWORKINITIALIZE_LATE_INTERNAL_NAME = "NetworkInitialize___Late"; + private readonly string[] _networkInitializeMethodNames = new string[] { NETWORKINITIALIZE_EARLY_INTERNAL_NAME, NETWORKINITIALIZE_LATE_INTERNAL_NAME }; + #endregion + + internal bool ProcessLocal(TypeDefinition typeDef) + { + bool modified = false; + TypeDefinition copyTypeDef = typeDef; + + //TypeDefs which are using prediction. + List _usesPredictionTypeDefs = new(); + + //Make collection of NBs to processor. + List typeDefs = new(); + do + { + if (!HasClassBeenProcessed(copyTypeDef)) + { + //Disallow nested network behaviours. + ICollection nestedTds = copyTypeDef.NestedTypes; + foreach (TypeDefinition item in nestedTds) + { + if (item.InheritsNetworkBehaviour(base.Session)) + { + base.LogError($"{copyTypeDef.FullName} contains nested NetworkBehaviours. These are not supported."); + return modified; + } + } + + typeDefs.Add(copyTypeDef); + } + copyTypeDef = TypeDefinitionExtensionsOld.GetNextBaseClassToProcess(copyTypeDef, base.Session); + } while (copyTypeDef != null); + + /* Reverse type definitions so that the parent + * is first. This counts indexes up as we go further + * down the children. By doing so we do not have to + * rebuild rpc or synctype indexes when a parent is inherited + * multiple times. EG: with this solution if parent had 1 sync type + * and childA had 2 the parent would be index 0, and childA would have 1 and 2. + * But also, if childB inherited childA it would have 3+. + * + * Going in reverse also gaurantees the awake method will already be created + * or modified in any class a child inherits. This lets us call it appropriately + * as well error if the awake does not exist, such as could not be created. */ + typeDefs.Reverse(); + + foreach (TypeDefinition td in typeDefs) + { + /* Create NetworkInitialize before-hand so the other procesors + * can use it. */ + MethodDefinition networkInitializeIfDisabledMd; + CreateNetworkInitializeMethods(td, out networkInitializeIfDisabledMd); + CallNetworkInitialize(networkInitializeIfDisabledMd); + + + + /* Prediction. */ + /* Run prediction first since prediction will modify + * user data passed into prediction methods. Because of this + * other RPCs should use the modified version and reader/writers + * made for prediction. */ + if (base.GetClass().Process(td)) + { + _usesPredictionTypeDefs.Add(td); + modified = true; + } + //25ms + + /* RPCs. */ + modified |= base.GetClass().ProcessLocal(td); + //30ms + /* //perf rpcCounts can be optimized by having different counts + * for target, observers, server, replicate, and reoncile rpcs. Since + * each registers to their own delegates this is possible. */ + + /* SyncTypes. */ + modified |= base.GetClass().ProcessLocal(td); + + //Call base networkinitialize early/late. + CallBaseOnNetworkInitializeMethods(td); + //Add networkinitialize executed check to early/late. + AddNetworkInitializeExecutedChecks(td); + + //Copy user logic from awake into a new method. + CopyAwakeUserLogic(td); + /* Create awake method or if already exist make + * it public virtual. */ + if (!ModifyAwakeMethod(td, out bool awakeCreated)) + { + //This is a hard fail and will break the solution so throw here. + base.LogError($"{td.FullName} has an Awake method which could not be modified, or could not be found. This often occurs when a child class is in an assembly different from the parent, and the parent does not implement Awake. To resolve this make an Awake in {td.Name} public virtual."); + return modified; + } + + //Calls NetworkInitializeEarly from awake. + CallMethodFromAwake(td, NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + //Only call base if awake was created. Otherwise let the users implementation handle base calling. + if (awakeCreated) + CallBaseAwake(td); + //Call logic user may have put in awake. + CallAwakeUserLogic(td); + //NetworkInitializeLate from awake. + CallMethodFromAwake(td, NETWORKINITIALIZE_LATE_INTERNAL_NAME); + //Since awake methods are erased ret has to be added at the end. + AddReturnToAwake(td); + + //70ms + _processedClasses.Add(td); + } + + /* If here then all inerited classes for firstTypeDef have + * been processed. */ + + return modified; + } + + /// + /// Gets the name to use for user awake logic method. + /// + internal string GetAwakeUserLogicMethodDefinition(TypeDefinition td) => $"Awake_UserLogic_{td.FullName}_{base.Module.Name}"; + + /// + /// 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) + { + SyncTypeProcessor stProcessor = base.GetClass(); + RpcProcessor rpcProcessor = base.GetClass(); + + foreach (TypeDefinition typeDef in typeDefs) + { + //Inherits, don't need to check. + if (typeDef.InheritsNetworkBehaviour(base.Session)) + continue; + + //Check each method for attribute. + foreach (MethodDefinition md in typeDef.Methods) + { + //Has RPC attribute but doesn't inherit from NB. + if (rpcProcessor.Attributes.HasRpcAttributes(md)) + { + base.LogError($"{typeDef.FullName} has one or more RPC attributes but does not inherit from NetworkBehaviour."); + return true; + } + } + //Check fields for attribute. + foreach (FieldDefinition fd in typeDef.Fields) + { + if (stProcessor.IsSyncType(fd)) + { + base.LogError($"{typeDef.FullName} implements one or more SyncTypes but does not inherit from NetworkBehaviour."); + return true; + } + } + } + + //Fallthrough / pass. + return false; + } + + + + /// + /// Calls the next awake method if the nested awake was created by codegen. + /// + /// + private void CallBaseAwake(TypeDefinition td) + { + /* If base is not a class which can be processed then there + * is no need to continue. */ + if (!td.CanProcessBaseType(base.Session)) + return; + + //Base Awake. + MethodReference baseAwakeMr = td.GetMethodReferenceInBase(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME); + //This Awake. + MethodDefinition tdAwakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + + ILProcessor processor = tdAwakeMd.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg_0); //base. + processor.Emit(OpCodes.Call, baseAwakeMr); + } + + /// + /// Calls the next awake method if the nested awake was created by codegen. + /// + /// + private void CallAwakeUserLogic(TypeDefinition td) + { + //UserLogic. + MethodDefinition userLogicMd = td.GetMethod(GetAwakeUserLogicMethodDefinition(td)); + /* Userlogic may be null if Awake was created. + * If so, there's no need to proceed. */ + if (userLogicMd == null) + return; + + //This Awake. + MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + //Call logic. + base.GetClass().CallCopiedMethod(awakeMd, userLogicMd); + } + + /// + /// Adds a check to NetworkInitialize to see if it has already run. + /// + /// + private void AddNetworkInitializeExecutedChecks(TypeDefinition typeDef) + { + AddCheck(NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + AddCheck(NETWORKINITIALIZE_LATE_INTERNAL_NAME); + + void AddCheck(string methodName) + { + string fieldName = $"{methodName}{typeDef.FullName}{typeDef.Module.Name}_Excuted"; + MethodDefinition md = typeDef.GetMethod(methodName); + if (md == null) + return; + + TypeReference boolTr = base.GetClass().GetTypeReference(typeof(bool)); + FieldReference fr = typeDef.GetOrCreateFieldReference(base.Session, fieldName, FieldAttributes.Private, boolTr, out bool created); + + if (created) + { + List insts = new(); + ILProcessor processor = md.Body.GetILProcessor(); + //Add check if already called. + //if (alreadyInitialized) return; + Instruction skipFirstRetInst = processor.Create(OpCodes.Nop); + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldfld, fr)); + insts.Add(processor.Create(OpCodes.Brfalse_S, skipFirstRetInst)); + insts.Add(processor.Create(OpCodes.Ret)); + insts.Add(skipFirstRetInst); + //Set field to true. + insts.Add(processor.Create(OpCodes.Ldarg_0)); + insts.Add(processor.Create(OpCodes.Ldc_I4_1)); + insts.Add(processor.Create(OpCodes.Stfld, fr)); + processor.InsertFirst(insts); + } + } + } + + /// + /// Calls base for NetworkInitializeEarly/Late on a TypeDefinition. + /// + private void CallBaseOnNetworkInitializeMethods(TypeDefinition typeDef) + { + //If base is null cannot call base. + if (typeDef.BaseType == null) + return; + + foreach (string mdName in _networkInitializeMethodNames) + { + MethodReference baseMr = typeDef.GetMethodReferenceInBase(base.Session, mdName); + //Not found in base. + if (baseMr == null) + continue; + + MethodDefinition baseMd = baseMr.CachedResolve(base.Session); + + MethodDefinition thisMd = typeDef.GetMethod(mdName); + ILProcessor processor = thisMd.Body.GetILProcessor(); + + bool alreadyHasBaseCall = false; + //Check if already calls baseAwake. + foreach (Instruction item in thisMd.Body.Instructions) + { + //If a call or call virt. Although, callvirt should never occur. + if (item.OpCode == OpCodes.Call || item.OpCode == OpCodes.Callvirt) + { + if (item.Operand != null && item.Operand.GetType().Name == nameof(MethodDefinition)) + { + MethodDefinition md = (MethodDefinition)item.Operand; + if (md == baseMd) + { + alreadyHasBaseCall = true; + break; + } + } + } + } + + if (!alreadyHasBaseCall) + { + //Create instructions for base call. + List instructions = new() + { + processor.Create(OpCodes.Ldarg_0), //this. + processor.Create(OpCodes.Call, baseMr) + }; + processor.InsertFirst(instructions); + } + + } + } + + /// + /// Adds returns awake method definitions within awakeDatas. + /// + private void AddReturnToAwake(TypeDefinition td) + { + //This Awake. + MethodDefinition awakeMd = td.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + ILProcessor processor = awakeMd.Body.GetILProcessor(); + //If no instructions or the last instruction isnt ret. + if (processor.Body.Instructions.Count == 0 || processor.Body.Instructions[processor.Body.Instructions.Count - 1].OpCode != OpCodes.Ret) + { + processor.Emit(OpCodes.Ret); + } + } + + /// + /// Calls a method by name from awake. + /// + private void CallMethodFromAwake(TypeDefinition typeDef, string methodName) + { + //Will never be null because we added it previously. + MethodDefinition awakeMethodDef = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + MethodReference networkInitMr = typeDef.GetMethodReference(base.Session, methodName); + + ILProcessor processor = awakeMethodDef.Body.GetILProcessor(); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(networkInitMr.GetCallOpCode(base.Session), networkInitMr); + } + + /// + /// 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); + networkInitializeIfDisabledMd = CreateMethod(nameof(NetworkBehaviour.NetworkInitializeIfDisabled)); + + MethodDefinition CreateMethod(string name, MethodDefinition copied = null) + { + bool created; + MethodDefinition md = typeDef.GetOrCreateMethodDefinition(base.Session, name, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created); + + if (created) + { + //Emit ret into new method. + ILProcessor processor = md.Body.GetILProcessor(); + //End of method return. + processor.Emit(OpCodes.Ret); + } + + return md; + } + } + + /// + /// Creates an 'NetworkInitialize' method which is called by the childmost class to initialize scripts on Awake. + /// + private void CallNetworkInitialize(MethodDefinition networkIntializeMd) + { + ILProcessor processor = networkIntializeMd.Body.GetILProcessor(); + + networkIntializeMd.Body.Instructions.Clear(); + CallMethod(NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + CallMethod(NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor.Emit(OpCodes.Ret); + + void CallMethod(string name) + { + MethodReference initIfDisabledMr = networkIntializeMd.DeclaringType.GetMethodReference(base.Session, name); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(initIfDisabledMr.GetCallOpCode(base.Session), initIfDisabledMr); + } + } + + /// + /// Copies logic from users Awake if present, to a new method. + /// + private void CopyAwakeUserLogic(TypeDefinition typeDef) + { + MethodDefinition awakeMd = typeDef.GetMethod(NetworkBehaviourHelper.AWAKE_METHOD_NAME); + //If found copy. + if (awakeMd != null) + base.GetClass().CopyIntoNewMethod(awakeMd, GetAwakeUserLogicMethodDefinition(typeDef), out _); + } + + /// + /// Erases content in awake if it already exist, otherwise makes a new Awake. + /// Makes Awake public and virtual. + /// + /// True if successful. + private bool ModifyAwakeMethod(TypeDefinition typeDef, out bool created) + { + MethodDefinition awakeMd = typeDef.GetOrCreateMethodDefinition(base.Session, NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void, out created); + + //Awake is found. Check for invalid return type. + if (!created) + { + if (awakeMd.ReturnType != typeDef.Module.TypeSystem.Void) + { + base.LogError($"IEnumerator Awake methods are not supported within NetworkBehaviours."); + return false; + } + //Make public if good. + awakeMd.SetPublicAttributes(); + } + //Already was made. + else + { + ILProcessor processor = awakeMd.Body.GetILProcessor(); + processor.Emit(OpCodes.Ret); + } + + //Clear original awake. + awakeMd.Body.Instructions.Clear(); + + return true; + } + + /// + /// 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(NetworkBehaviourHelper.AWAKE_METHOD_NAME, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES, typeDef.Module.TypeSystem.Void); + thisAwakeMethodDef.Body.InitLocals = true; + typeDef.Methods.Add(thisAwakeMethodDef); + + processor = thisAwakeMethodDef.Body.GetILProcessor(); + processor.Emit(OpCodes.Ret); + } + + //MethodRefs for networkinitialize and awake. + MethodReference networkInitializeMethodRef = typeDef.Module.ImportReference(firstNetworkInitializeMethodDef); + + processor = thisAwakeMethodDef.Body.GetILProcessor(); + //Create instructions for base call. + List instructions = new() + { + processor.Create(OpCodes.Ldarg_0), //this. + processor.Create(OpCodes.Call, networkInitializeMethodRef) + }; + + /* If awake was created then make a call to the users + * first awake. There's no reason to do this if awake + * already existed because the user would have control + * over making that call. */ + if (created && firstUserAwakeMethodDef != null) + { + MethodReference baseAwakeMethodRef = typeDef.Module.ImportReference(firstUserAwakeMethodDef); + instructions.Add(processor.Create(OpCodes.Ldarg_0)); //this. + instructions.Add(processor.Create(OpCodes.Call, baseAwakeMethodRef)); + } + + processor.InsertFirst(instructions); + } + } +} \ 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..2cb4bc1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 23866e4d620216745a837fa99e801164 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/NetworkBehaviourProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/Prediction.meta b/Assets/FishNet/CodeGenerating/Processing/Prediction.meta new file mode 100644 index 0000000..6f54468 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 98f6937bb72a7254b92b4656f28b7333 +folderAsset: yes +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..f034226 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs @@ -0,0 +1,1092 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.Processing.Rpc; +using FishNet.Connection; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Object.Prediction.Delegating; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Performance; +using GameKit.Dependencies.Utilities; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using System.Collections.Generic; +using System.Linq; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine; +using SR = System.Reflection; + +namespace FishNet.CodeGenerating.Processing +{ + internal class PredictionProcessor : CodegenBase + { + #region Types. + private class PredictionAttributedMethods + { + public MethodDefinition ReplicateMethod; + public MethodDefinition ReconcileMethod; + + public PredictionAttributedMethods(MethodDefinition replicateMethod, MethodDefinition reconcileMethod) + { + ReplicateMethod = replicateMethod; + ReconcileMethod = reconcileMethod; + } + } + + private enum InsertType + { + First, + Last, + Current + } + + private class CreatedPredictionFields + { + /// + /// TypeReference of replicate data. + /// + public TypeReference ReplicateDataTypeRef; + /// + /// Typereference of reconcile data. + /// + public TypeReference ReconcileDataTypeRef; + + /// + /// Delegate for calling replicate user logic. + /// + public FieldDefinition ReplicateUserLogicDelegate; + /// + /// Delegate for calling replicate user logic. + /// + public FieldDefinition ReconcileUserLogicDelegate; + + /// + /// Replicate data which has not run yet and is in queue to do so. + /// + public FieldDefinition ReplicatesQueue; + /// + /// Replicate data which has already run and is used to reconcile/replay. + /// + public FieldDefinition ReplicatesHistory; + /// + /// Reconcile data cached locally from the local client. + /// + public FieldDefinition LocalReconciles; + + /// + /// Last replicate read. This is used for reading delta replicates. + /// + public FieldDefinition LastReadReplicate; + /// + /// Last reconcile read. This is used for reading delta reconciles. + /// + public FieldDefinition LastReadReconcile; + } + + private class PredictionReaders + { + public readonly MethodReference ReplicateReader; + public readonly 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 IReconcileData_GetTick_MethodRef; + public MethodReference ReplicateData_Ctor_MethodRef; + #endregion + + #region Const. + public const string REPLICATE_READER_PREFIX = "Reader_Replicate___"; + public const string RECONCILE_READER_PREFIX = "Reader_Reconcile___"; + #endregion + + public override bool ImportReferences() + { + System.Type locType; + //SR.MethodInfo locMi; + + base.ImportReference(typeof(BasicQueue<>)); + ReplicateULDelegate_TypeRef = base.ImportReference(typeof(ReplicateUserLogicDelegate<>)); + ReconcileULDelegate_TypeRef = base.ImportReference(typeof(ReconcileUserLogicDelegate<>)); + + TypeDefinition replicateDataTd = base.ImportReference(typeof(ReplicateDataContainer<>)).CachedResolve(base.Session); + ReplicateData_Ctor_MethodRef = base.ImportReference(replicateDataTd.GetConstructor(parameterCount: 2)); + + //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); + break; + } + } + + locType = typeof(IReconcileData); + foreach (SR.MethodInfo mi in locType.GetMethods()) + { + if (mi.Name == nameof(IReconcileData.GetTick)) + { + IReconcileData_GetTick_MethodRef = base.ImportReference(mi); + break; + } + } + + return true; + } + + #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; + } + + /// + /// Gets number of predictions by checking for prediction attributes in typeDef parents, excluding typerDef. + /// + /// + /// + internal uint GetPredictionCountInParents(TypeDefinition typeDef) + { + uint count = 0; + do + { + typeDef = typeDef.GetNextBaseClassToProcess(base.Session); + if (typeDef != null) + count += GetPredictionCount(typeDef); + } while (typeDef != null); + + return count; + } + + /// + /// 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; + + string replicateAttributeFullName = base.GetClass().ReplicateAttribute_FullName; + string reconcileAttributeFullName = base.GetClass().ReconcileAttribute_FullName; + + bool error = false; + foreach (MethodDefinition methodDef in typeDef.Methods) + { + foreach (CustomAttribute customAttribute in methodDef.CustomAttributes) + { + if (customAttribute.Is(replicateAttributeFullName)) + { + if (!IsMethodPrivate(methodDef) || IsPredictionMethodAlreadyFound(replicateMd)) + error = true; + else + replicateMd = methodDef; + } + else if (customAttribute.Is(reconcileAttributeFullName)) + { + if (!IsMethodPrivate(methodDef) || IsPredictionMethodAlreadyFound(reconcileMd)) + { + error = true; + } + else + { + reconcileMd = methodDef; + if (!CheckCreateReconcile(reconcileMd)) + error = true; + } + } + + if (error) + break; + } + + if (error) + break; + } + + //Checks to make sure the CreateReconcile method exist and calls reconcile. + bool CheckCreateReconcile(MethodDefinition reconcileMd) + { + string crName = nameof(NetworkBehaviour.CreateReconcile); + MethodDefinition createReconcileMd = reconcileMd.DeclaringType.GetMethod(crName); + //Does not exist. + if (createReconcileMd == null) + { + base.LogError($"{reconcileMd.DeclaringType.Name} does not implement method {crName}. Override method {crName} and use it to create your reconcile data, and call your reconcile method {reconcileMd.Name}. Call "); + return false; + } + //Exists, check for call. + else + { + //Check for call instructions. + foreach (Instruction inst in createReconcileMd.Body.Instructions) + { + if (inst.OpCode == OpCodes.Call || inst.OpCode == OpCodes.Callvirt) + { + if (inst.Operand is MethodReference mr) + { + if (mr.Name == reconcileMd.Name) + return true; + } + } + } + + base.LogError($"{reconcileMd.DeclaringType.Name} implements {crName} but does not call reconcile method {reconcileMd.Name}. If you are calling CreateReconcile from another type please make a new method to call in {reconcileMd.DeclaringType.Name}, which in return calls CreateReconcile."); + //Fallthrough. + return false; + } + } + + /* Forcing a method to private is not necessarily needed + * but it adds a safe-guard against users calling base.Reconcile/Replicate + * from another replicate. Doing this would cause the replicate to run twice + * for the same script hierarchy, which would create unpredictable behavior. */ + bool IsMethodPrivate(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 IsPredictionMethodAlreadyFound(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 + + internal bool Process(TypeDefinition typeDef) + { + //Set prediction count in parents here. Increase count after each predictionAttributeMethods iteration. + //Do a for each loop on predictionAttributedMethods. + /* NOTES: get all prediction attributed methods up front and store them inside predictionAttributedMethods. + * To find the proper reconciles for replicates add an attribute field allowing users to assign Ids. EG ReplicateV2.Id = 1. Default + * value will be 0. */ + + MethodDefinition replicateMd; + MethodDefinition reconcileMd; + //Not using prediction methods. + if (!GetPredictionMethods(typeDef, out replicateMd, out reconcileMd)) + return false; + + RpcProcessor rp = base.GetClass(); + uint predictionRpcCount = GetPredictionCountInParents(typeDef) + rp.GetRpcCountInParents(typeDef); + + //If replication methods found but this hierarchy already has max. + if (predictionRpcCount >= NetworkBehaviourHelper.MAX_RPC_ALLOWANCE) + { + base.LogError($"{typeDef.FullName} and inherited types exceed {NetworkBehaviourHelper.MAX_PREDICTION_ALLOWANCE} replicated methods. Only {NetworkBehaviourHelper.MAX_PREDICTION_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, predictionRpcCount, out predictionReaders, out replicateULMd, out reconcileULMd); + InitializeCollections(typeDef, replicateMd, reconcileMd, predictionFields); + InitializeULDelegates(typeDef, predictionFields, replicateMd, reconcileMd, replicateULMd, reconcileULMd); + RegisterPredictionRpcs(typeDef, predictionRpcCount, predictionReaders); + + 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.GetMethodDefinitionInAnyBase(base.Session, 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 or protected field of uint type and return it's value within {getMd.Name}."); + return false; + } + + //Make sure the field is correct accessibility + FieldDefinition fd = (FieldDefinition)ldFldInst.Operand; + if (!fd.Attributes.HasFlag(FieldAttributes.Private) && !fd.Attributes.HasFlag(FieldAttributes.Family)) + { + base.LogError($"{dataTd.FullName} method {getMd.Name} returns a tick field it does not have the correct accessibility. Make the field {fd.Name} private or protected."); + 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 RegisterPredictionRpcs(TypeDefinition typeDef, uint hash, PredictionReaders readers) + { + MethodDefinition injectionMethodDef = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + ILProcessor processor = injectionMethodDef.Body.GetILProcessor(); + List insts = new(); + + 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().ReplicateRpcDelegate_Ctor_MethodRef; + callMr = base.GetClass().RegisterReplicateRpc_MethodRef; + } + else + { + ctorMr = base.GetClass().ReconcileRpcDelegate_Ctor_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, MethodDefinition reconcileMd, CreatedPredictionFields predictionFields) + { + GeneralHelper gh = base.GetClass(); + 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(); + + GenericInstanceType git; + + //ReplicateQueue. + git = gh.GetGenericType(typeof(ReplicateDataContainer<>), replicateDataTr); + Generate(predictionFields.ReplicatesQueue, gh.GetGenericBasicQueue(git), isRingBuffer: false); + + //ReplicatesHistory buffer. + git = gh.GetGenericType(typeof(ReplicateDataContainer<>), replicateDataTr); + Generate(predictionFields.ReplicatesHistory, gh.GetGenericRingBuffer(git), isRingBuffer: true); + + //LocalReconcile buffer. + git = gh.GetGenericType(typeof(LocalReconcile<>), reconcileDataTr); + Generate(predictionFields.LocalReconciles, gh.GetGenericRingBuffer(git), isRingBuffer: true); + + void Generate(FieldReference fr, GenericInstanceType lGit, bool isRingBuffer) + { + MethodDefinition ctorMd; + if (isRingBuffer) + //ctorMd = base.GetClass().RingBuffer_TypeRef.CachedResolve(base.Session).GetDefaultConstructor(base.Session); + ctorMd = base.GetClass().RingBuffer_TypeRef.CachedResolve(base.Session).GetConstructor(base.Session, 1); + else + ctorMd = base.GetClass().List_TypeRef.CachedResolve(base.Session).GetDefaultConstructor(base.Session); + + MethodReference ctorMr = ctorMd.MakeHostInstanceGeneric(base.Session, lGit); + + List insts = new(); + + insts.Add(processor.Create(OpCodes.Ldarg_0)); + if (isRingBuffer) + insts.Add(processor.Create(OpCodes.Ldc_I4, RingBuffer.DEFAULT_CAPACITY)); + 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(); + + Generate(replicateULMd, replicateDataTr, predictionFields.ReplicateUserLogicDelegate, typeof(ReplicateUserLogicDelegate<>), ReplicateULDelegate_TypeRef); + Generate(reconcileULMd, reconcileDataTr, predictionFields.ReconcileUserLogicDelegate, 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 reconcileDataTr = reconcileMd.Parameters[0].ParameterType; + + GenericInstanceType git; + + //User logic delegate for replicates. + GetGenericULDelegate(replicateDataTr, typeof(ReplicateUserLogicDelegate<>), out git); + FieldDefinition replicateUserLogicDelegateFd = new($"_replicateULDelegate___{replicateMd.Name}", FieldAttributes.Private, git); + typeDef.Fields.Add(replicateUserLogicDelegateFd); + + //User logic delegate for reconciles. + GetGenericULDelegate(reconcileDataTr, typeof(ReconcileUserLogicDelegate<>), out git); + FieldDefinition reconcileUserLogicDelegateFd = new($"_reconcileULDelegate___{reconcileMd.Name}", FieldAttributes.Private, git); + typeDef.Fields.Add(reconcileUserLogicDelegateFd); + + //Replicates history. + git = gh.GetGenericType(typeof(ReplicateDataContainer<>), replicateDataTr); + FieldDefinition replicatesHistoryFd = new($"_replicatesHistory___{replicateMd.Name}", FieldAttributes.Private, gh.GetGenericRingBuffer(git)); + typeDef.Fields.Add(replicatesHistoryFd); + + //Replicates queue. + git = gh.GetGenericType(typeof(ReplicateDataContainer<>), replicateDataTr); + FieldDefinition replicatesQueueFd = new($"_replicatesQueue___{replicateMd.Name}", FieldAttributes.Private, gh.GetGenericBasicQueue(git)); + typeDef.Fields.Add(replicatesQueueFd); + + //Local reconciles. + git = gh.GetGenericType(typeof(LocalReconcile<>), reconcileDataTr); + FieldDefinition localReconcilesFd = new($"_reconcilesHistory___{reconcileMd.Name}", FieldAttributes.Private, gh.GetGenericRingBuffer(git)); + typeDef.Fields.Add(localReconcilesFd); + + //Used for delta reconcile. + FieldDefinition lastReconcileDataFd = new($"_lastReadReconcile___{reconcileMd.Name}", FieldAttributes.Private, reconcileDataTr); + typeDef.Fields.Add(lastReconcileDataFd); + + //Used for delta replicates. + git = gh.GetGenericType(typeof(ReplicateDataContainer<>), replicateDataTr); + FieldDefinition lastReadReplicateFd = new($"_lastReadReplicate___{replicateMd.Name}", FieldAttributes.Private, git); + typeDef.Fields.Add(lastReadReplicateFd); + + predictionFields = new() + { + ReplicateDataTypeRef = replicateDataTr, + ReconcileDataTypeRef = reconcileDataTr, + + ReplicateUserLogicDelegate = replicateUserLogicDelegateFd, + ReconcileUserLogicDelegate = reconcileUserLogicDelegateFd, + + ReplicatesQueue = replicatesQueueFd, + ReplicatesHistory = replicatesHistoryFd, + LocalReconciles = localReconcilesFd, + + LastReadReplicate = lastReadReplicateFd, + LastReadReconcile = lastReconcileDataFd, + }; + } + + /// + /// 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, state, channel. + //Reconcile: data, asServer, channel. + int count = (replicateMethod) ? 3 : 2; + + //Check parameter count. + if (methodDef.Parameters.Count != count) + { + PrintParameterExpectations(); + return true; + } + + string expectedName; + //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; + } + + expectedName = (replicateMethod) ? typeof(ReplicateState).Name : typeof(Channel).Name; + if (methodDef.Parameters[1].ParameterType.Name != expectedName) + { + PrintParameterExpectations(); + return true; + } + + //Only replicate uses more than 2 parameters. + if (replicateMethod) + { + //Channel. + if (methodDef.Parameters[2].ParameterType.Name != typeof(Channel).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, state = ReplicateState.Invalid, channel = Channel.Unreliable"); + else + base.LogError($"Reconcile method {methodDef.Name} within {typeDef.Name} requires exactly {count} parameters. In order: reconcile data, 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; + + uint startingRpcCount = rpcCount; + 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(); + + TypeReference replicateDataTr = replicateMd.Parameters[0].ParameterType; + TypeReference reconcileDataTr = reconcileMd.Parameters[0].ParameterType; + + MethodDefinition replicateReader; + MethodDefinition reconcileReader; + + if (!CreateReplicate()) + return false; + if (!CreateReconcile()) + return false; + if (!CreateEmptyReplicatesQueueIntoHistoryStart()) + return false; + if (!CreateReconcileStart()) + return false; + if (!CreateReplicateReplayStart()) + return false; + + CreateClearReplicateCacheMethod(typeDef, replicateDataTr, reconcileDataTr, predictionFields); + CreateReplicateReader(typeDef, startingRpcCount, replicateMd, predictionFields, out replicateReader); + CreateReconcileReader(typeDef, reconcileMd, predictionFields, out reconcileReader); + predictionReaders = new(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 channelPd = replicateMd.Parameters[2]; + + GenericInstanceMethod replicateGim = base.GetClass().Replicate_Current_MethodRef.MakeGenericMethod(new TypeReference[] { replicateDataTr }); + + /* ReplicateUserLogicDelegate del + * uint methodHash + * BasicQueue> replicatesQueue + * RingBuffer> replicatesHistory + * ReplicateData data) + * where T : IReplicateData + */ + processor.Emit(OpCodes.Ldarg_0); + //User logic delegate. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateUserLogicDelegate); + //Rpc hash. + processor.Emit(OpCodes.Ldc_I4, (int)rpcCount); + //Replicates queue. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesQueue); + //Replicates history. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesHistory); + + /* Data being called into the method. */ + //Generate the ReplicateData + //new ReplicateData(data, channel) + processor.Emit(OpCodes.Ldarg, replicateDataPd); + processor.Emit(OpCodes.Ldarg, channelPd); + GenericInstanceType git = GetGenericReplicateDataContainer(replicateDataTr); + MethodReference ctorMr = ReplicateData_Ctor_MethodRef.MakeHostInstanceGeneric(base.Session, git); + processor.Emit(OpCodes.Newobj, ctorMr); + + //Call nb.Replicate_Current. + processor.Emit(OpCodes.Call, replicateGim); + + processor.Emit(OpCodes.Ret); + + return true; + } + + + bool CreateReconcile() + { + ILProcessor processor = reconcileMd.Body.GetILProcessor(); + ServerCreateReconcile(reconcileMd, predictionFields, ref rpcCount); + processor.Emit(OpCodes.Ret); + return true; + } + + bool CreateEmptyReplicatesQueueIntoHistoryStart() + { + MethodDefinition newMethodDef = nbh.EmptyReplicatesQueueIntoHistory_Start_MethodRef.CachedResolve(base.Session).CreateCopy(base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES); + typeDef.Methods.Add(newMethodDef); + + ILProcessor processor = newMethodDef.Body.GetILProcessor(); + + MethodReference baseMethodGim = nbh.EmptyReplicatesQueueIntoHistory_MethodRef.GetMethodReference(base.Session, new TypeReference[] { predictionFields.ReplicateDataTypeRef }); + + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesQueue); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesHistory); + processor.Emit(OpCodes.Call, baseMethodGim); + processor.Emit(OpCodes.Ret); + + return true; + } + + //Overrides reconcile start to call reconcile_client_internal. + bool CreateReconcileStart() + { + MethodDefinition newMethodDef = nbh.Reconcile_Client_Start_MethodRef.CachedResolve(base.Session).CreateCopy(base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES); + typeDef.Methods.Add(newMethodDef); + + ILProcessor processor = newMethodDef.Body.GetILProcessor(); + + Call_Reconcile_Client(); + + void Call_Reconcile_Client() + { + MethodReference baseMethodGim = nbh.Reconcile_Client_MethodRef.GetMethodReference(base.Session, new TypeReference[] { predictionFields.LastReadReconcile.FieldType, predictionFields.ReplicateDataTypeRef }); + + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReconcileUserLogicDelegate); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesHistory); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.LocalReconciles); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.LastReadReconcile); + processor.Emit(OpCodes.Call, baseMethodGim); + processor.Emit(OpCodes.Ret); + } + + return true; + } + + bool CreateReplicateReplayStart() + { + MethodDefinition newMethodDef = nbh.Replicate_Replay_Start_MethodRef.CachedResolve(base.Session).CreateCopy(base.Session, null, MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES); + typeDef.Methods.Add(newMethodDef); + + ParameterDefinition replayTickPd = newMethodDef.Parameters[0]; + ILProcessor processor = newMethodDef.Body.GetILProcessor(); + + MethodReference baseMethodGim = nbh.Replicate_Replay_MethodRef.GetMethodReference(base.Session, new TypeReference[] { predictionFields.ReplicateDataTypeRef }); + + //uint replicateTick, ReplicateUserLogicDelegate del, List replicates, Channel channel) where T : IReplicateData + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldarg, replayTickPd); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicateUserLogicDelegate); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesHistory); + //processor.Emit(OpCodes.Ldc_I4, (int)Channel.Unreliable); //Channel does not really matter when replaying. At least not until someone needs it. + processor.Emit(OpCodes.Call, baseMethodGim); + 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 replicateDataTr, TypeReference reconcileDataTr, CreatedPredictionFields predictionFields) + { + GeneralHelper gh = base.GetClass(); + NetworkBehaviourHelper nbh = base.GetClass(); + + string methodName = nameof(NetworkBehaviour.ClearReplicateCache); + MethodDefinition baseClearMd = typeDef.GetMethodDefinitionInAnyBase(base.Session, methodName); + MethodDefinition clearMd = typeDef.GetOrCreateMethodDefinition(base.Session, methodName, baseClearMd, true, out bool created); + clearMd.Attributes = MethodDefinitionExtensions.PUBLIC_VIRTUAL_ATTRIBUTES; + //This class already has the method created when it should not. + if (baseClearMd.DeclaringType == typeDef) + { + base.LogError($"{typeDef.Name} overrides method {methodName} when it should not."); + return; + } + + ILProcessor processor = clearMd.Body.GetILProcessor(); + //Call the base class first. + processor.Emit(OpCodes.Ldarg_0); + MethodReference baseClearMr = base.ImportReference(baseClearMd); + processor.Emit(OpCodes.Call, baseClearMr); + + //Call the actual clear method. + GenericInstanceMethod internalClearGim = nbh.ClearReplicateCache_Internal_MethodRef.MakeGenericMethod(new[] { replicateDataTr, reconcileDataTr }); + + processor.Emit(OpCodes.Ldarg_0); //Base. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesQueue); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesHistory); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.LocalReconciles); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldflda, predictionFields.LastReadReplicate); + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldflda, predictionFields.LastReadReconcile); + processor.Emit(OpCodes.Call, internalClearGim); + 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(); + 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(); + 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(); + 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 a reader for replicate data received from clients. + /// + private bool CreateReplicateReader(TypeDefinition typeDef, uint hash, MethodDefinition replicateMd, CreatedPredictionFields predictionFields, out MethodDefinition result) + { + string methodName = $"{REPLICATE_READER_PREFIX}{replicateMd.Name}"; + MethodDefinition createdMd = new(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); + processor.Emit(OpCodes.Ldc_I4, (int)hash); + //Reader, NetworkConnection. + processor.Emit(OpCodes.Ldarg, readerPd); + processor.Emit(OpCodes.Ldarg, networkConnectionPd); + //lastFirstReadReplicate. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldflda, predictionFields.LastReadReplicate); + //Replicates queue. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesQueue); + //Replicates history. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.ReplicatesHistory); + //Channel. + processor.Emit(OpCodes.Ldarg, channelPd); + processor.Emit(OpCodes.Call, replicateReaderGim); + + 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[1]; + ILProcessor processor = reconcileMd.Body.GetILProcessor(); + + NetworkBehaviourHelper nbh = base.GetClass(); + GenericInstanceMethod methodGim; + + /* Reconcile_Current. */ + methodGim = nbh.Reconcile_Current_MethodRef.MakeGenericMethod(new TypeReference[] { reconcileDataPd.ParameterType }); + + //Hash. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldc_I4, (int)rpcCount); + //Last reconcile data. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldflda, predictionFields.LastReadReconcile); + //Reconciles history (local reconciles). + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldfld, predictionFields.LocalReconciles); + //Data. + processor.Emit(OpCodes.Ldarg, reconcileDataPd); + //Channel. + processor.Emit(OpCodes.Ldarg, channelPd); + + processor.Emit(OpCodes.Call, methodGim); + + rpcCount++; + } + #endregion + + #region Client side. + /// + /// 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(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); + + /* nb.Reconcile_Reader(readerPd, ref lastReadReconcile); */ + processor.Emit(OpCodes.Ldarg, readerPd); + //Data to assign read value to. + processor.Emit(OpCodes.Ldarg_0); + processor.Emit(OpCodes.Ldflda, predictionFields.LastReadReconcile); + + processor.Emit(OpCodes.Call, methodGim); + + //Add end of method. + processor.Emit(OpCodes.Ret); + + result = createdMd; + return true; + } + + /// + /// Outputs generic RingBuffer for dataTr. + /// + public GenericInstanceType GetGenericReplicateDataContainer(TypeReference dataTr) + { + TypeReference typeTr = base.ImportReference(typeof(ReplicateDataContainer<>)); + return typeTr.MakeGenericInstanceType(new TypeReference[] { dataTr }); + } + #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..3d50e1b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d0663606e86b0b34bae85df164747e72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/Prediction/PredictionProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs new file mode 100644 index 0000000..155b959 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs @@ -0,0 +1,166 @@ +using FishNet.CodeGenerating.Extension; +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); + //Check to force using static. + if (!useStatic && qolAttribute.GetField("UseIsStarted", false)) + useStatic = true; + + 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, !useStatic); + } + } + else if (qolType == QolAttributeType.Server) + { + if (!StripMethod(methodDef)) + { + LoggingType logging = qolAttribute.GetField("Logging", LoggingType.Warning); + base.GetClass().CreateIsServerCheck(methodDef, logging, useStatic, true, !useStatic); + } + } + + bool StripMethod(MethodDefinition md) + { + + + //Fall through. + return false; + } + } + + + } +} \ No newline at end of file diff --git a/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta new file mode 100644 index 0000000..faf0e86 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5080d7597ffca904b9a9fd5926e4e5a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/QOLAttributeProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs new file mode 100644 index 0000000..5135739 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs @@ -0,0 +1,1062 @@ +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; +using FishNet.Utility; +using GameKit.Dependencies.Utilities; + +namespace FishNet.CodeGenerating.Helping +{ + + internal class ReaderProcessor : CodegenBase + { + + #region Reflection references. + public TypeDefinition GeneratedReader_TypeDef; + public MethodDefinition GeneratedReader_OnLoad_MethodDef; + public readonly Dictionary InstancedReaderMethods = new(); + public readonly Dictionary StaticReaderMethods = new(); + #endregion + + #region Misc. + /// + /// TypeReferences which have already had delegates made for. + /// + private HashSet _delegatedTypes = new(); + #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; + /// + /// Attribute fullname which indicates a default reader. + /// + public string DEFAULT_READER_ATTRIBUTE_FULLNAME => typeof(DefaultReaderAttribute).FullName; + /// + /// Prefix used which all instanced and user created serializers should start with. + /// + internal const string CUSTOM_READER_PREFIX = "Read"; + /// + /// Class name to use for generated readers. + /// + internal const string GENERATED_READERS_CLASS_NAME = "GeneratedReaders___Internal"; + /// + /// 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; + /// + /// MethodReference for Write. + /// + private MethodReference _readMethodRef; + #endregion + + public override bool ImportReferences() + { + TypeReference readerTr = base.ImportReference(typeof(Reader)); + _readMethodRef = readerTr.CachedResolve(base.Session).GetMethodReference(base.Session, nameof(Reader.Read)); + + return true; + } + + public bool Process() + { + GeneralHelper gh = base.GetClass(); + + CreateGeneratedReadersClass(); + FindInstancedReaders(); + CreateInstancedReaderExtensions(); + + void CreateGeneratedReadersClass() + { + GeneratedReader_TypeDef = 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. */ + GeneratedReader_OnLoad_MethodDef = gh.GetOrCreateMethod(GeneratedReader_TypeDef, out _, INITIALIZEONCE_METHOD_ATTRIBUTES, INITIALIZEONCE_METHOD_NAME, base.Module.TypeSystem.Void); + gh.CreateRuntimeInitializeOnLoadMethodAttribute(GeneratedReader_OnLoad_MethodDef); + + ILProcessor ppp = GeneratedReader_OnLoad_MethodDef.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 (!HasDefaultSerializerAttribute()) + 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); + + bool HasDefaultSerializerAttribute() + { + foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes) + { + if (item.AttributeType.FullName == DEFAULT_READER_ATTRIBUTE_FULLNAME) + return true; + } + + return false; + } + + } + } + + return true; + } + + + /// + /// 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) + { + if (dict.ContainsKey(fullName)) + base.LogError($"Key {fullName} already exists. First method is {dict[fullName].Name}, new method is {methodRef.Name}."); + else + 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 CreateInitializeDelegate(MethodReference readMr) + { + GeneralHelper gh = base.GetClass(); + ReaderImports ri = base.GetClass(); + WriterProcessor wp = base.GetClass(); + + /* If a global serializer is declared for the type + * and the method is not the declared serializer then + * exit early. */ + if (wp.DeclaresUseGlobalCustomSerializer(readMr.ReturnType) && readMr.Name.StartsWith(UtilityConstants.GeneratedReaderPrefix)) + return; + + GeneratedReader_OnLoad_MethodDef.RemoveEndRet(base.Session); + + //Check if already exist. + ILProcessor processor = GeneratedReader_OnLoad_MethodDef.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; + + functionGenericInstance = gh.FunctionT2TypeRef.MakeGenericInstanceType(ri.Reader_TypeRef, dataTypeRef); + functionConstructorInstanceMethodRef = gh.FunctionT2ConstructorMethodRef.MakeHostInstanceGeneric(base.Session, functionGenericInstance); + processor.Emit(OpCodes.Newobj, functionConstructorInstanceMethodRef); + + //Call delegate to GeneratedReader.Read + GenericInstanceType genericInstance = ri.GenericReader_TypeRef.MakeGenericInstanceType(dataTypeRef); + MethodReference genericReadMethodRef = ri.GenericReader_Read_MethodRef.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($"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.CachedResolve(base.Session)); + gh.MakeExtensionMethod(md); + // + gwh.GeneratedReader_TypeDef.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(); + MethodReference readMr = GetOrCreateReadMethodReference(readTypeRef); + + if (readMr != null) + { + TypeReference dataTr = readMr.ReturnType; + bool isGlobalSerializer = base.GetClass().DeclaresUseGlobalCustomSerializer(dataTr); + + //Make a local variable. + createdVariableDef = base.GetClass().CreateVariable(methodDef, readTypeRef); + //pooledReader.ReadBool(); + insts.Add(processor.Create(OpCodes.Ldarg, readerParameterDef)); + + 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); + } + + if (isGlobalSerializer) + { + //Switch out to use Read instead. + TypeReference genericTr = base.ImportReference(readTypeRef); + readMr = _readMethodRef.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 encasingValueVd, FieldReference memberValueFr) + { + if (readMr != null) + { + WriterProcessor wp = base.GetClass(); + bool isGlobalSerializer = (wp.DeclaresUseGlobalCustomSerializer(encasingValueVd.VariableType) || wp.DeclaresUseGlobalCustomSerializer(memberValueFr.FieldType)); + + 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 = (encasingValueVd.VariableType.IsValueType) ? + OpCodes.Ldloca : OpCodes.Ldloc; + + /* If generic then find write class for + * data type. Currently we only support one generic + * for this. */ + if (memberValueFr.FieldType.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)memberValueFr.FieldType; + TypeReference genericTr = git.GenericArguments[0]; + readMr = readMr.GetMethodReference(base.Session, genericTr); + } + + processor.Emit(loadOpCode, encasingValueVd); + //reader. + processor.Emit(OpCodes.Ldarg, readerPd); + + if (isGlobalSerializer) + { + //Switch out to use Read instead. + TypeReference genericTr = base.ImportReference(memberValueFr.FieldType); + readMr = _readMethodRef.GetMethodReference(base.Session, genericTr); + } + //reader.ReadXXXX(). + processor.Emit(OpCodes.Call, readMr); + //obj.Field = result / reader.ReadXXXX(). + processor.Emit(OpCodes.Stfld, memberValueFr); + + return true; + } + else + { + base.LogError($"Reader not found for {memberValueFr.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); + //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 CreateInitializeDelegates() + { + foreach (KeyValuePair item in StaticReaderMethods) + base.GetClass().CreateInitializeDelegate(item.Value); + } + + + /// + /// Returns if typeRef has a deserializer. + /// + /// + /// + /// + internal bool HasDeserializer(TypeReference typeRef, bool createMissing) + { + bool result = (GetInstancedReadMethodReference(typeRef) != null) || + (GetStaticReadMethodReference(typeRef) != null) || base.GetClass().DeclaresUseGlobalCustomSerializer(typeRef); + + if (!result && createMissing) + { + if (!base.GetClass().HasNonSerializableAttribute(typeRef.CachedResolve(base.Session))) + { + MethodReference methodRef = CreateReader(typeRef); + result = (methodRef != null); + } + } + + return result; + } + + /// + /// 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 parameters. + 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) + 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); + 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(); + //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 + 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); + 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. */ + //See if to use non-alloc reads. + if (objectTr.CachedResolve(base.Session).HasCustomAttribute()) + { + //Make a new instance of object type and set to objectVariableDef. + base.GetClass().SetVariableDefinitionFromCaches(processor, objectVariableDef, objectTypeDef); + } + else + { + //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 encasingValueVd, 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, encasingValueVd, 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, encasingValueVd, 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 = $"{UtilityConstants.GeneratedReaderPrefix}{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..ceaccf5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0f9d3654f5816c4409b88fa0602c6df4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/ReaderProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc.meta b/Assets/FishNet/CodeGenerating/Processing/Rpc.meta new file mode 100644 index 0000000..a4a26cd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 810757384532c3a4b9e4681f869acf1e +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..8ee537c --- /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(); + 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..35e08ee --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 91f84e00db3d1ad448fb6a760afb6927 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/Rpc/AttributeData.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs b/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs new file mode 100644 index 0000000..37fa2e1 --- /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(); + 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(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(); + } + //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(); + } + } + + //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(); + } + + 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..52a7540 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 974ebf09757267941a86f92e5072258c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/Rpc/Attributes.cs + uploadId: 762203 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..ef9b048 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c2176b6bfcc49934d8f36fba3df74d0c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/Rpc/CreatedRpc.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs new file mode 100644 index 0000000..6c4b031 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs @@ -0,0 +1,1154 @@ +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 GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using FishNet.Object; +using MonoFN.Collections.Generic; +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(); + #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 = nameof(ServerRpcAttribute.RequireOwnership); + private const string RUNLOCALLY_NAME = nameof(RpcAttribute.RunLocally); + private const string EXCLUDEOWNER_NAME = nameof(ObserversRpcAttribute.ExcludeOwner); + private const string EXCLUDESERVER_NAME = nameof(TargetRpcAttribute.ExcludeServer); + private const string BUFFERLAST_NAME = nameof(ObserversRpcAttribute.BufferLast); + private const string DATALENGTH_NAME = nameof(RpcAttribute.DataLength); + private const string VALIDATETARGET_NAME = nameof(TargetRpcAttribute.ValidateTarget); + private const string DATAORDERTYPE_NAME = nameof(RpcAttribute.OrderType); + private const string LOGGING_NAME = nameof(ServerRpcAttribute.Logging); + #endregion + + public override bool ImportReferences() + { + Attributes.Initialize(base.Session); + return base.ImportReferences(); + } + + internal bool ProcessLocal(TypeDefinition typeDef) + { + bool modified = false; + + PredictionProcessor pp = base.GetClass(); + uint rpcCount = GetRpcCountInParents(typeDef) + pp.GetPredictionCountInParents(typeDef) + pp.GetPredictionCount(typeDef); + //All createdRpcs for typeDef. + List typeDefCeatedRpcs = new(); + 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 attributeDatas = Attributes.GetRpcAttributes(md); + bool success = true; + foreach (AttributeData ad in attributeDatas) + { + CreatedRpc cr = new(); + 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)}"; + } + + /// + /// Gets RPCcount count in all of typeDefs parents, excluding typeDef itself. + /// + internal uint GetRpcCountInParents(TypeDefinition typeDef) + { + uint count = 0; + do + { + typeDef = typeDef.GetNextBaseClassToProcess(base.Session); + if (typeDef != null) + count += GetRpcCount(typeDef); + } while (typeDef != null); + + return count; + } + + /// + /// Returns the method name with parameter types included within the name. + /// + public static string GetMethodNameAsParameters(MethodDefinition methodDef) + { + StringBuilder sb = new(); + foreach (ParameterDefinition pd in methodDef.Parameters) + sb.Append(pd.ParameterType.FullName); + + return $"{methodDef.Name}_{sb.ToString().GetStableHashU32()}"; + } + + /// + /// 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++; + } + } + + 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(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(); + /* Parameters which won't be serialized, such as channel. + * It's safe to add parameters which are null or + * not used. */ + HashSet nonserializedParameters = new(); + + //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 non-serialized 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, cr.Attribute); + + VariableDefinition channelVariableDef = CreateAndPopulateChannelVariable(writerMd, channelParameterDef); + /* Create a local PooledWriter variable. */ + //Default value for data length. + 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, cr.Attribute)); + //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(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, 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. + MethodReference logicMr = logicMd.GetMethodReference(base.Session); + processor.Emit(OpCodes.Call, logicMr); + 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, 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. + //MethodReference logicMr = cr.LogicMethodDef.GetMethodReference(base.Session); + 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(); + 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(); + + //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) + LoggingType loggingType = rpcAttribute.GetField(LOGGING_NAME, LoggingType.Warning); + base.GetClass().CreateIsClientCheck(methodDef, loggingType, false, true, false); + } + + /// + /// 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, CustomAttribute rpcAttribute) + { + LoggingType loggingType = rpcAttribute.GetField(LOGGING_NAME, LoggingType.Warning); + //If (!base.IsServer) + base.GetClass().CreateIsServerCheck(methodDef, loggingType, false, 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; + + 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)}"; + + /* Check if method exists first. If it does already exist then return found method. + * This can happen if the logic method was already made when using multiple Rpc attributes + * such as TargetRpc/ObserversRpc. */ + MethodDefinition createdMd = typeDef.GetMethod(methodName); + if (createdMd != null) + return createdMd; + + //If here logic method does not exist yet. + createdMd = new(methodName, cr.OriginalMethodDef.Attributes, base.ImportReference(typeof(void))); + typeDef.Methods.Add(createdMd); + createdMd.Body.InitLocals = true; + + foreach (ParameterDefinition pd in originalMd.Parameters) + createdMd.Parameters.Add(new(base.ImportReference(pd.ParameterType))); + + base.GetClass().CopyIntoMethod(cr.OriginalMethodDef, createdMd); + + /* This is a partial fix for Unity 2021 IL2CPP builds. The issue appears to be resolved in Unity 2022. + * In Unity 2021 when calling a generated method in a generic class the codegen must + * strip the calls to the method of its generics. This is of course improper code but + * that some reason is the resolution, because Unity. However, even with this fix if the + * developer makes use of the generic properties of the class from the offending method + * there is a fair chance the application will crash. */ +#if !UNITY_2022_3_OR_NEWER + /* If the declaring type has a generic then we need to see if any + * logic instructions call methods in another or same generic class. */ + ILProcessor processor = createdMd.Body.GetILProcessor(); + List inserter = new(); + + //base.LogWarning($"Created {createdMd.Name}. Original {cr.OriginalMethodDef.Name}"); + Collection instructions = createdMd.Body.Instructions; + for (int i = 0; i < instructions.Count; i++) + { + inserter.Clear(); + + Instruction v = instructions[i]; + if (v.OpCode == OpCodes.Callvirt || v.OpCode == OpCodes.Call) + { + bool print = cr.OriginalMethodDef.Name.Contains("ClientDetectFigure"); + if (print) + base.LogWarning($" Operand is {v.Operand.GetType().Name}"); + + MethodDefinition calledMd = null; + + if (v.Operand is MethodDefinition md) + { + calledMd = md; + } + else if (v.Operand is MethodReference mr) + { + bool hasGenerics = mr.DeclaringType.ContainsGenericParameter; + + //If methodReference declaring type has generics. + if (mr.DeclaringType.ContainsGenericParameter) + calledMd = mr.CachedResolve(base.Session); + + if (print) + base.LogWarning($"Instruction # {i}. HasGenerics {hasGenerics}. MethodName {mr.Name}. call"); + } + + //If need to make a new call then remove old and insert. + if (calledMd != null) + { + instructions.RemoveAt(i); + inserter.Add(processor.Create(OpCodes.Call, calledMd)); + processor.InsertAt(i, inserter); + } + } + // } + } +#endif + + return createdMd; + } + + /// + /// 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 cr) + { + //Runlocally. + if (cr.RunLocally) + { + processor.Emit(OpCodes.Ldarg_0); //this. + //Parameters. + foreach (ParameterDefinition pd in originalMd.Parameters) + processor.Emit(OpCodes.Ldarg, pd); + + MethodReference logicMr = cr.LogicMethodDef.GetMethodReference(base.Session); + processor.Emit(OpCodes.Call, logicMr); + } + } + + processor.Emit(OpCodes.Ret); + } + + #region CreateSend + /// + /// Creates a call to SendServerRpc on NetworkBehaviour. + /// + /// + /// + private List CreateSendServerRpc(MethodDefinition methodDef, uint methodHash, VariableDefinition writerVariableDef, VariableDefinition channelVariableDef, CustomAttribute rpcAttribute) + { + List insts = new(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + insts.AddRange(CreateSendRpcCommon(processor, methodHash, writerVariableDef, channelVariableDef, rpcAttribute)); + //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(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + insts.AddRange(CreateSendRpcCommon(processor, methodHash, writerVariableDef, channelVariableDef, rpcAttribute)); + //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(); + 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, rpcAttribute)); + insts.Add(processor.Create(OpCodes.Ldarg, targetConnectionParameterDef)); + insts.Add(processor.Create(OpCodes.Ldc_I4, excludeServer.ToInt())); + 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, CustomAttribute rpcAttribute) + { + List insts = new(); + + 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)); + + int orderType = (int)rpcAttribute.GetField(DATAORDERTYPE_NAME, DataOrderType.Default); + insts.Add(processor.Create(OpCodes.Ldc_I4, orderType)); + + 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..685ab93 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4d4adb5891ee44f4397cd07ac2df0ce0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/Rpc/RpcProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs new file mode 100644 index 0000000..bc7b179 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs @@ -0,0 +1,565 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.Object.Delegating; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using GameKit.Dependencies.Utilities; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Collections.Generic; +using System.Collections.Generic; +using System.Linq; + +namespace FishNet.CodeGenerating.Processing +{ + internal class SyncTypeProcessor : CodegenBase + { + #region Reflection references. + private TypeReference _syncBase_TypeRef; + private TypeReference _syncVar_TypeRef; + +#pragma warning disable CS0618 // Type or member is obsolete + private string _syncVarAttribute_FullName = typeof(SyncVarAttribute).FullName; + private string _syncObjectAttribute_FullName = typeof(SyncObjectAttribute).FullName; +#pragma warning restore CS0618 // Type or member is obsolete + #endregion + + #region Const. + private const string INITIALIZELATE_METHOD_NAME = nameof(SyncBase.InitializeLate); + private const string INITIALIZEEARLY_METHOD_NAME = nameof(SyncBase.InitializeEarly); + private const string GETSERIALIZEDTYPE_METHOD_NAME = nameof(ICustomSync.GetSerializedType); + #endregion + + public override bool ImportReferences() + { + System.Type svType = typeof(SyncVar<>); + _syncVar_TypeRef = base.ImportReference(svType); + + System.Type syncBaseType = typeof(SyncBase); + _syncBase_TypeRef = base.ImportReference(syncBaseType).Resolve(); + + return true; + } + + /// + /// Processes SyncVars and Objects. + /// + /// Number of SyncTypes implemented in this typeDef and those inherited of. + internal bool ProcessLocal(TypeDefinition typeDef) + { + bool modified = false; + + ValidateVersion3ToVersion4SyncVars(typeDef); + + uint startingHash = GetSyncTypeCountInParents(typeDef); + uint totalSyncTypes = (startingHash + GetSyncTypeCount(typeDef)); + if (totalSyncTypes > NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE) + { + base.LogError($"Found {totalSyncTypes} SyncTypes within {typeDef.FullName} and inherited classes. The maximum number of allowed SyncTypes within type and inherited types is {NetworkBehaviourHelper.MAX_SYNCTYPE_ALLOWANCE}. Remove SyncTypes or condense them using data containers, or a custom SyncObject."); + return false; + } + + FieldDefinition[] fieldDefs = typeDef.Fields.ToArray(); + foreach (FieldDefinition fd in fieldDefs) + { + //Check if uses old attributes first. + if (HasSyncTypeAttributeUnchecked(fd)) + { + base.LogError($"SyncType {fd.Name} on type {fd.DeclaringType.FullName} implements [SyncVar] or [SyncObject]. These attributes are no longer supported as of version 4. Please see Break Solutions within the documentation to resolve these errors."); + continue; + } + SyncType st = GetSyncType(fd); + //Not a sync type field. + if (st == SyncType.Unset) + continue; + //Needs to be addressed if this ever occurs. + if (st == SyncType.Unhandled) + { + base.LogError($"Field {fd.Name} in type {fd.DeclaringType.FullName} is unhandled."); + return false; + } + //Errors occurred while checking the synctype field. + if (!IsSyncTypeFieldValid(fd, true)) + return false; + + bool isSyncObject = (st != SyncType.Variable); + bool isGeneric = fd.FieldType.IsGenericInstance; + if (isGeneric) + { + if (TryCreateGenericSyncType(startingHash, fd, isSyncObject)) + startingHash++; + } + else + { + if (TryCreateNonGenericSyncType(startingHash, fd, isSyncObject)) + startingHash++; + } + + 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 (IsSyncType(fd)) + count++; + } + + return count; + } + + /// + /// Gets SyncType count in all of typeDefs parents, excluding typeDef itself. + /// + internal uint GetSyncTypeCountInParents(TypeDefinition typeDef) + { + uint count = 0; + while (true) + { + typeDef = typeDef.GetNextBaseClassToProcess(base.Session); + if (typeDef != null) + count += GetSyncTypeCount(typeDef); + else + break; + } + + return count; + } + + /// + /// Returns if fieldDef is a syncType. + /// + internal bool IsSyncType(FieldReference fieldRef) + { + FieldDefinition fd = fieldRef.CachedResolve(base.Session); + return IsSyncType(fd); + } + + /// + /// Returns if fieldDef is a syncType. + /// + internal bool IsSyncType(FieldDefinition fieldDef) + { + TypeDefinition ftTypeDef = fieldDef.FieldType.CachedResolve(base.Session); + /* TypeDef may be null for certain generated types, + * as well for some normal types such as queue because Unity broke + * them in 2022+ and decided the issue was not worth resolving. */ + if (ftTypeDef == null) + return false; + + return ftTypeDef.InheritsFrom(base.Session); + } + + + + /// + /// Throws an error on any SyncVars which are comparing null on the SyncVar field, or trying to set the field null. + /// + private void ValidateVersion3ToVersion4SyncVars(TypeDefinition td) + { +#if !FISHNET_DISABLE_V3TOV4_HELPERS + /* In version3 since the user could reference the field directly like a value these actions were allowed. Doing so now however would cause unintended behavior. + * For example... + * [SyncVar] + * private Player _myPlayer; + * + * void SomeMethod() + * { + * if (_myPlayer == null) doStuff(); + * } + * The above context would be valid in version 3. + * + * But if the user converted without paying close attention, which is reasonable to miss, this would not work as intended. + * For example... + * private readonly SyncVar _myPlayer = new(); + * + * void SomeMethod() + * { + * if (_myPlayer == null) doStuff(); + * } + * The above is not the same behavior as the field _myPlayer would never be null. Instead the code should look like this... + * + * void SomeMethod() + * { + * if (_myPlayer.Value == null) doStuff(); + * } + * The checks below will catch this scenarios. + */ + + foreach (MethodDefinition methodDef in td.Methods) + { + //Ignore constructors. + if (methodDef.IsConstructor) + continue; + if (methodDef.IsAbstract) + continue; + 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.OpCode == OpCodes.Ldflda) && 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); + + if (IsSyncType(resolvedOpField)) + { + //Check next opcode for a brfalse/true. + //If there are more instructions to check, which there should always be. + if (i < (methodDef.Body.Instructions.Count - 1)) + { + Instruction nextInst = methodDef.Body.Instructions[i + 1]; + if (nextInst.OpCode == OpCodes.Brfalse || nextInst.OpCode == OpCodes.Brfalse_S || + nextInst.OpCode == OpCodes.Brtrue || nextInst.OpCode == OpCodes.Brtrue_S) + base.LogError($"Method {methodDef.Name} in class {td.Name} is comparing null for the SyncType field {resolvedOpField.Name}. SyncType fields should never be null; did you intend to compare the SyncType.Value instead?"); + } + } + } + /* 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); + + if (IsSyncType(resolvedOpField)) + base.LogError($"Method {methodDef.Name} in class {td.Name} is setting value to the SyncType field {resolvedOpField.Name}. This will result in the SyncType not functioning."); + } + } + + } +#endif + } + + /// + /// Error checks a SyncType field. This assumes the field is a SyncType. + /// + private bool IsSyncTypeFieldValid(FieldDefinition fieldDef, bool outputError) + { + //Static. + if (fieldDef.IsStatic) + { + if (outputError) + base.LogError($"{fieldDef.Name} SyncType in type {fieldDef.DeclaringType.FullName} cannot be static."); + return false; + } + //Generic. + if (fieldDef.FieldType.IsGenericParameter) + { + if (outputError) + base.LogError($"{fieldDef.Name} SyncType in type {fieldDef.DeclaringType.FullName} cannot be be generic."); + return false; + } + + //todo checking if field is initialized would be good. + + /* Forces readonly unless user allows for serialization. + * If within this logic then the field is readonly. */ + if (!fieldDef.Attributes.HasFlag(FieldAttributes.InitOnly)) + { + bool ignoreRequirement = false; + string attributeFullName = typeof(AllowMutableSyncTypeAttribute).FullName; + //Check if has attribute to bypass readonly check. + foreach (CustomAttribute item in fieldDef.CustomAttributes) + { + if (item.AttributeType.FullName == attributeFullName) + { + ignoreRequirement = true; + break; + } + } + + if (!ignoreRequirement) + { + if (outputError) + base.LogError($"{fieldDef.Name} SyncType in type {fieldDef.DeclaringType.FullName} must be readonly or decorated with the {nameof(AllowMutableSyncTypeAttribute)} attribute. If allowing muteable do not ever re-assign the field at runtime."); + return false; + } + } + + //Fall through/pass. + return true; + } + + /// + /// Returns SyncType on a field while error checking. + /// + internal SyncType GetSyncType(FieldDefinition fieldDef) + { + if (!IsSyncType(fieldDef)) + return SyncType.Unset; + + TypeDefinition fieldTypeDef = fieldDef.FieldType.CachedResolve(base.Session); + + ObjectHelper oh = base.GetClass(); + string fdName = fieldDef.FieldType.Name; + if (fdName == oh.SyncVar_Name || fieldTypeDef.ImplementsInterfaceRecursive(base.Session)) + return SyncType.Variable; + else if (fdName == oh.SyncList_Name) + return SyncType.List; + else if (fdName == oh.SyncDictionary_Name) + return SyncType.Dictionary; + else if (fdName == oh.SyncHashSet_Name) + return SyncType.HashSet; + //Custom types must also implement ICustomSync. + else if (fieldTypeDef.ImplementsInterfaceRecursive(base.Session)) + return SyncType.Custom; + else + return SyncType.Unhandled; + } + + + /// + /// Tries to create a SyncType that does not use generics. + /// + private bool TryCreateNonGenericSyncType(uint hash, FieldDefinition originalFieldDef, bool isSyncObject) + { + TypeDefinition fieldTypeTd = originalFieldDef.FieldType.CachedResolve(base.Session); + if (!fieldTypeTd.ImplementsInterface()) + { + base.LogError($"SyncType field {originalFieldDef.Name} in type {originalFieldDef.DeclaringType.FullName} does not implement {nameof(ICustomSync)}. Non-generic SyncTypes must implement {nameof(ICustomSync)}. See documentation on Custom SyncTypes for more information."); + return false; + } + //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 checkForSerializer = true; + TypeReference serializedDataTypeRef = null; + /* If the user is returning null then + * they are indicating a built-in serializer + * already exists. */ + if (instructions.Count == 2 && instructions[0].OpCode == OpCodes.Ldnull && instructions[1].OpCode == OpCodes.Ret) + { + checkForSerializer = false; + } + //If not returning null then make a serializer for return type. + else + { + foreach (Instruction item in instructions) + { + if (item.OpCode == OpCodes.Ldnull) + { + checkForSerializer = false; + break; + } + + //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; + } + } + } + + TypeReference[] typeRefs; + //If need to check for serialization. + if (checkForSerializer) + { + if (serializedDataTypeRef == null) + { + base.LogError($"SyncType field {originalFieldDef.Name} in type {originalFieldDef.DeclaringType.FullName} does not indicate which data type it needs to serialize. Review documentation for custom SyncTypes to view how to implement this feature."); + return false; + } + + //If here then check. + typeRefs = new TypeReference[] + { + serializedDataTypeRef, + }; + if (!CanSerialize(originalFieldDef, typeRefs)) + return false; + } + else + { + typeRefs = null; + } + + if (!InitializeSyncType(hash, originalFieldDef, typeRefs, isSyncObject)) + return false; + + return true; + } + + /// + /// Tries to create a SyncType that uses generics. + /// + private bool TryCreateGenericSyncType(uint hash, FieldDefinition originalFieldDef, bool isSyncObject) + { + GenericInstanceType tmpGenerinstanceType = originalFieldDef.FieldType as GenericInstanceType; + TypeReference[] typeRefs = new TypeReference[tmpGenerinstanceType.GenericArguments.Count]; + for (int i = 0; i < typeRefs.Length; i++) + typeRefs[i] = base.ImportReference(tmpGenerinstanceType.GenericArguments[i]); + if (!CanSerialize(originalFieldDef, typeRefs)) + return false; + + if (!InitializeSyncType(hash, originalFieldDef, typeRefs, isSyncObject)) + return false; + + return true; + } + + /// + /// Checks if type references can be serialized. + /// + /// Field definition specifying types. This is only used for debug output. + private bool CanSerialize(FieldDefinition fd, TypeReference[] typeRefs) + { + if (typeRefs == null) + return true; + + GeneralHelper gh = base.GetClass(); + foreach (TypeReference item in typeRefs) + { + bool canSerialize = gh.HasSerializerAndDeserializer(item, true); + if (!canSerialize) + { + base.LogError($"SyncType name {fd.Name} in type {fd.DeclaringType.FullName} data type {item.FullName} does not support serialization and one could not be created automatically. Use a supported type or create a custom serializer."); + return false; + } + } + + //Fall through/pass. + return true; + } + + + /// + /// Returns if attribute if a SyncVarAttribute. + /// + /// + /// + private bool IsSyncVariableAttribute(string attributeFullName) + { + return (attributeFullName == _syncVarAttribute_FullName); + } + /// + /// Returns if attribute if a SyncObjectAttribute. + /// + /// + /// + private bool IsSyncObjectAttribute(string attributeFullName) + { + return (attributeFullName == _syncObjectAttribute_FullName); + } + + /// + /// Returns if fieldDef has a SyncType attribute. No error checking is performed. + /// + /// + /// + private bool HasSyncTypeAttributeUnchecked(FieldDefinition fieldDef) + { + foreach (CustomAttribute customAttribute in fieldDef.CustomAttributes) + { + if (IsSyncObjectAttribute(customAttribute.AttributeType.FullName) || IsSyncVariableAttribute(customAttribute.AttributeType.FullName)) + return true; + } + + return false; + } + + /// + /// Sets methods used from SyncBase for typeDef. + /// + /// + internal bool SetSyncBaseInitializeMethods(FieldDefinition syncTypeFieldDef, TypeReference[] variableTypeRefs, out MethodReference initializeEarlyMr, out MethodReference initializeLateMr) + { + initializeEarlyMr = null; + initializeLateMr = null; + //Find the SyncBase class. + TypeDefinition fieldTd = syncTypeFieldDef.FieldType.CachedResolve(base.Session); + TypeDefinition syncBaseTd = fieldTd.GetClassInInheritance(base.Session, typeof(SyncBase).FullName); + //If SyncBase isn't found. + if (syncBaseTd == null) + { + base.LogError($"Could not find SyncBase within type {fieldTd.FullName}."); + return false; + } + else + { + //Early + initializeEarlyMr = syncBaseTd.GetMethodReference(base.Session, INITIALIZEEARLY_METHOD_NAME); + // if (variableTypeRefs != null) + // initializeEarlyMr = initializeEarlyMr.MakeGenericMethod(variableTypeRefs); + //Late + initializeLateMr = syncBaseTd.GetMethodReference(base.Session, INITIALIZELATE_METHOD_NAME); + // if (variableTypeRefs != null) + // initializeLateMr = initializeLateMr.MakeGenericMethod(variableTypeRefs); + + return true; + } + + } + /// + /// Initializes a SyncType using default settings. + /// + private bool InitializeSyncType(uint hash, FieldDefinition originalFieldDef, TypeReference[] variableTypeRefs, bool isSyncObject) + { + //Set needed methods from syncbase. + MethodReference initializeLateMr; + MethodReference initializeEarlyMr; + if (!SetSyncBaseInitializeMethods(originalFieldDef, variableTypeRefs, out initializeEarlyMr, out initializeLateMr)) + return false; + + //Make user field public. + originalFieldDef.Attributes &= ~FieldAttributes.Private; + originalFieldDef.Attributes |= FieldAttributes.Public; + + TypeDefinition typeDef = originalFieldDef.DeclaringType; + List insts; + ILProcessor processor; + MethodDefinition injectionMd; + + //InitializeEarly. + injectionMd = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_EARLY_INTERNAL_NAME); + processor = injectionMd.Body.GetILProcessor(); + insts = new() + { + processor.Create(OpCodes.Ldarg_0), //this. + processor.Create(OpCodes.Ldfld, originalFieldDef), + processor.Create(OpCodes.Ldarg_0), //this again for NetworkBehaviour. + processor.Create(OpCodes.Ldc_I4, (int)hash), + processor.Create(OpCodes.Ldc_I4, isSyncObject.ToInt()), + processor.Create(OpCodes.Call, initializeEarlyMr), + }; + processor.InsertFirst(insts); + + //InitializeLate. + injectionMd = typeDef.GetMethod(NetworkBehaviourProcessor.NETWORKINITIALIZE_LATE_INTERNAL_NAME); + processor = injectionMd.Body.GetILProcessor(); + insts = new() + { + processor.Create(OpCodes.Ldarg_0), //this. + processor.Create(OpCodes.Ldfld, originalFieldDef), + processor.Create(initializeLateMr.GetCallOpCode(base.Session), initializeLateMr), + }; + processor.InsertFirst(insts); + + return true; + + } + } +} diff --git a/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs.meta b/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs.meta new file mode 100644 index 0000000..030e7f3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ec95af37f78b9e340b5eaa199c1af94a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/SyncTypeProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs new file mode 100644 index 0000000..477b8c7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs @@ -0,0 +1,1096 @@ +using FishNet.CodeGenerating.Extension; +using FishNet.CodeGenerating.Helping.Extension; +using FishNet.CodeGenerating.ILCore; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Utility; +using FishNet.Utility.Performance; +using MonoFN.Cecil; +using MonoFN.Cecil.Cil; +using MonoFN.Cecil.Rocks; +using System; +using System.Collections.Generic; +using GameKit.Dependencies.Utilities; +using SR = System.Reflection; +using UnityDebug = UnityEngine.Debug; + +namespace FishNet.CodeGenerating.Helping +{ + internal class WriterProcessor : CodegenBase + { + #region Reflection references. + + public readonly Dictionary InstancedWriterMethods = new(); + public readonly Dictionary StaticWriterMethods = new(); + + public TypeDefinition GeneratedWriterClassTypeDef; + public MethodDefinition GeneratedWriterOnLoadMethodDef; + + #endregion + + #region Misc. + + /// + /// TypeReferences which have already had delegates made for. + /// + private HashSet _delegatedTypes = new(); + + #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); + + /// + /// Attributes required for custom serializer classes. + /// + public const TypeAttributes CUSTOM_SERIALIZER_TYPEDEF_ATTRIBUTES = (TypeAttributes.Sealed | TypeAttributes.Abstract); + + /// + /// Prefix all built-in and user created write methods should begin with. + /// + internal const string CUSTOM_WRITER_PREFIX = "Write"; + /// + /// Attribute fullname which indicates a default writer. + /// + public string DEFAULT_WRITER_ATTRIBUTE_FULLNAME => typeof(DefaultWriterAttribute).FullName; + + /// + /// 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.", + "Unity.Mathmatics", + }; + + #endregion + + public override bool ImportReferences() => true; + + /// + /// Processes data. To be used after everything else has called ImportReferences. + /// + /// + public bool Process() + { + GeneralHelper gh = base.GetClass(); + + CreateGeneratedWritersClass(); + FindInstancedWriters(); + CreateInstancedWriterExtensions(); + + //Creates class for generated writers, and init on load method. + void CreateGeneratedWritersClass() + { + 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 (!HasDefaultSerializerAttribute()) + 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); + + bool HasDefaultSerializerAttribute() + { + foreach (SR.CustomAttributeData item in methodInfo.CustomAttributes) + { + if (item.AttributeType.FullName == DEFAULT_WRITER_ATTRIBUTE_FULLNAME) + return true; + } + + return false; + } + } + } + + return true; + } + + /// + /// Creates writer extension methods for built-in writers. + /// + private void CreateInstancedWriterExtensions() + { + 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($"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.CachedResolve(base.Session)); + 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) + { + if (dict.ContainsKey(fullName)) + base.LogError($"Key {fullName} already exists. First method is {dict[fullName].Name}, new method is {methodRef.Name}."); + else + 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; + string fullName = typeRef.GetFullnameWithoutBrackets(); + dict.Remove(fullName); + } + + + /// + /// Creates Write delegates for known static methods. + /// + public void CreateInitializeDelegates() + { + foreach (KeyValuePair item in StaticWriterMethods) + base.GetClass().CreateInitializeDelegate(item.Value); + } + + /// + /// Creates a Write delegate for writeMethodRef and places it within the generated reader/writer constructor. + /// + /// + private void CreateInitializeDelegate(MethodReference writeMr) + { + GeneralHelper gh = base.GetClass(); + WriterImports wi = base.GetClass(); + + /* If a global serializer is declared for the type + * and the method is not the declared serializer then + * exit early. */ + if (DeclaresUseGlobalCustomSerializer(writeMr.Parameters[1].ParameterType) && writeMr.Name.StartsWith(UtilityConstants.GeneratedWriterPrefix)) + return; + + //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; + + actionGenericInstance = gh.ActionT2_TypeRef.MakeGenericInstanceType(wi.Writer_TypeRef, dataTypeRef); + actionConstructorInstanceMethodRef = gh.ActionT2Constructor_MethodRef.MakeHostInstanceGeneric(base.Session, actionGenericInstance); + + processor.Emit(OpCodes.Newobj, actionConstructorInstanceMethodRef); + //Call delegate to GenericWriter.Write + GenericInstanceType genericInstance = wi.GenericWriter_TypeRef.MakeGenericInstanceType(dataTypeRef); + MethodReference genericWriteMethodRef = wi.GenericWriter_Write_MethodRef.MakeHostInstanceGeneric(base.Session, genericInstance); + processor.Emit(OpCodes.Call, genericWriteMethodRef); + + 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) || DeclaresUseGlobalCustomSerializer(typeRef); + + 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) + { + //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.RetrieveWriter(); + /// + 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.RetrieveWriter(); + /// + /// + /// + /// + internal List CreatePooledWriter(MethodDefinition methodDef, int length, out VariableDefinition resultVd) + { + WriterImports wi = base.GetClass(); + + List insts = new(); + 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(); + 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) + { + Instruction endIf = processor.Create(OpCodes.Nop); + //If (value) jmp to endIf. + processor.Emit(OpCodes.Ldarg, checkedParameterDef); + processor.Emit(OpCodes.Brtrue, endIf); + //writer.WriteBool + CreateWriteBool(processor, writerParameterDef, true); + //Exit method. + processor.Emit(OpCodes.Ret); + //End of if check. + processor.Append(endIf); + } + + /// + /// 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); + } + + /// + /// Returns if a type should use a declared/custom serializer globally. + /// + public bool DeclaresUseGlobalCustomSerializer(TypeReference dataTypeRef) + { + return dataTypeRef.CachedResolve(base.Session).HasCustomAttribute(); + } + + /// + /// 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(); + ILProcessor processor = methodDef.Body.GetILProcessor(); + + if (writeMr != null) + { + bool declaresUseGlobalSerializer = DeclaresUseGlobalCustomSerializer(valueParameterDef.ParameterType); + + 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(); + } + + insts.Add(processor.Create(OpCodes.Ldarg, valueParameterDef)); + + 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); + } + + if (declaresUseGlobalSerializer) + { + //Switch out to use WriteUnpacked instead. + writeMr = base.GetClass().Writer_Write_MethodRef.GetMethodReference(base.Session, valueTr); + } + + insts.Add(processor.Create(OpCodes.Call, writeMr)); + return insts; + } + else + { + base.LogError($"Writer not found for {valueParameterDef.ParameterType.FullName}."); + return new(); + } + } + + /// + /// 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 encasingValuePd, FieldDefinition memberValueFd, MethodReference writeMr) + { + if (writeMr != null) + { + bool declaresUseGlobalSerializer = (DeclaresUseGlobalCustomSerializer(memberValueFd.FieldType) || DeclaresUseGlobalCustomSerializer(encasingValuePd.ParameterType)); + + 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 (memberValueFd.FieldType.IsGenericInstance) + { + GenericInstanceType git = (GenericInstanceType)memberValueFd.FieldType; + TypeReference genericTr = git.GenericArguments[0]; + writeMr = writeMr.GetMethodReference(base.Session, genericTr); + } + + FieldReference fieldRef = base.GetClass().GetFieldReference(memberValueFd); + processor.Emit(OpCodes.Ldarg, writerPd); + processor.Emit(OpCodes.Ldarg, encasingValuePd); + processor.Emit(OpCodes.Ldfld, fieldRef); + + /* If a generated write then instead of calling the + * generated write directly call writer.Write of + * the type. + * + * This will reroute to the generic writer, which does add an + * extra step, but this also allows us to decide what writer + * to use. In GenericWriter we can check if a method being set + * as the writer is generated, and if so while another method + * had already been set then favor the other method. + * + * This will favor built-in and user created serializers. This has to be + * done because we cannot check if a user created serializer exist + * across assemblies, but at runtime we can make sure to favor the + * created one as described above. */ + //True if has Write prefix for generated writers. + if (declaresUseGlobalSerializer) + { + //Switch out to use WriteUnpacked instead. + TypeReference genericTr = base.ImportReference(memberValueFd.FieldType); + writeMr = base.GetClass().Writer_Write_MethodRef.GetMethodReference(base.Session, genericTr); + } + + processor.Emit(OpCodes.Call, writeMr); + } + else + { + base.LogError($"Writer not found for {memberValueFd.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); + 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) + 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); + 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]); + //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 encasingValuePd, 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, encasingValuePd, 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, encasingValuePd, 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); + + //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(); + //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 + 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 = $"{UtilityConstants.GeneratedWriterPrefix}{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..9eb8f21 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2a4021bd44dc40f47abb494e0a4326f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Processing/WriterProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef new file mode 100644 index 0000000..9aa9b97 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef @@ -0,0 +1,20 @@ +{ + "name": "Unity.FishNet.Codegen", + "rootNamespace": "", + "references": [ + "FishNet.Runtime", + "FishNet.Codegen.Cecil", + "GameKit.Dependencies" + ], + "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..abb3ea2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 9341dc36b33c3984e97b22dac619ca50 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/Unity.FishNet.CodeGen.asmdef + uploadId: 762203 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..c4b0e13 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d0397844cfd0974f8ee136097d11380 +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..fa6b0a9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: c3a066bef0608d24987201601e20a905 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Directory.Build.props + uploadId: 762203 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..890ebf8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 65323af257ddec3409ed36503b853604 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/LICENSE.txt + uploadId: 762203 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..47afd8d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 23bfd4374c8423f4ebf6716985b8c720 +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..627a205 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2619210c5ef352b4aac70d8e5fab7a43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Code.cs + uploadId: 762203 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..9f86de8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 451f6a2407c53554f9a16eeb62d806ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeReader.cs + uploadId: 762203 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..17daa93 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 278f89c983a1bc6429c65a1d49dff091 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/CodeWriter.cs + uploadId: 762203 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..051438c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 86b3083301304a341b6059ea8c29be7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Document.cs + uploadId: 762203 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..c9f3886 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eb17bcae1a67ec344b65d6ed78ffb704 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ExceptionHandler.cs + uploadId: 762203 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..48c7fd1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 26902ea42064c624d82727d9ef9a8f5e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/ILProcessor.cs + uploadId: 762203 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..8b7de28 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2d1536adbd2ca174abbd624473e722c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Instruction.cs + uploadId: 762203 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..207465b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4be4045de2c6a0e428e5910f4e76cfac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/MethodBody.cs + uploadId: 762203 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..d697a8f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5d8b234c106529441912c10b87502175 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCode.cs + uploadId: 762203 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..3fa60b5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 52c19a62321778c438d2f6b483f18e73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/OpCodes.cs + uploadId: 762203 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..da66039 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 34e2f5d4b9b9ea542bec15f2d9acc3ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/PortablePdb.cs + uploadId: 762203 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..caaca9a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 612e6675dca63a84b9e8aaeb3e3b9ec6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/SequencePoint.cs + uploadId: 762203 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..d0fecc3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: de9ae158807f471449a81d9b8b3e3c4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/Symbols.cs + uploadId: 762203 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..3249a29 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1346fa5ba8ac9e8418cc20d2d7d0ad21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableDefinition.cs + uploadId: 762203 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..a3ce184 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b5dca1eaac23bfd4ba8e881302a72e33 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Cil/VariableReference.cs + uploadId: 762203 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..6bee4a2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cf18653914b5e6f429d4566a7897264c +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..7841c19 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 33eef67f4a74e1c40a6bdb3da9d22d00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/BlobHeap.cs + uploadId: 762203 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..7cd92fc --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4b1d0a81b66e78341ab77acdd3b15234 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Buffers.cs + uploadId: 762203 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..73115b0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dd50ffa8272dac444831e33b1b9d7f56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/CodedIndex.cs + uploadId: 762203 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..84e8b9d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 94c4cb498ef60034d83bb60d3f743832 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/ElementType.cs + uploadId: 762203 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..f7be9d3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4bb39ab6484eb114b91a85d4b53f0f28 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/GuidHeap.cs + uploadId: 762203 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..0aad0b7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c343466f40d68574499155a2acd05e6b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Heap.cs + uploadId: 762203 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..264dc39 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 755d1954236c374458be77d93493faf8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/MetadataToken.cs + uploadId: 762203 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..6af73fe --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c64dd6e20a2b11c44ad424d29380c14c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/PdbHeap.cs + uploadId: 762203 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..90c5620 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 89fd184bcc2b97840a2c0dfeec671be2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Row.cs + uploadId: 762203 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..338b4a1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e0fc64863ca825b4e994640b0be9a938 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/StringHeap.cs + uploadId: 762203 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..beedfcf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 978b1609b2ddc0c4baf628cc608456a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TableHeap.cs + uploadId: 762203 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..f72f3c0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dba0af6138c6a084b8787e88b20b142a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/TokenType.cs + uploadId: 762203 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..55ad4db --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2d89100b382ef8e4ea67917917d04db6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/UserStringHeap.cs + uploadId: 762203 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..34961b6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4785957c0c546de4680e1196a57f66d2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Metadata/Utilities.cs + uploadId: 762203 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..1ebbe55 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a1a250fa69b80b34c9ca9b9086296b97 +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..9583ede --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7492ed3a048237443b99d7a25e806ce6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamReader.cs + uploadId: 762203 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..e3ca842 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 26d384e0dd0e44549a9faf09adbd0a41 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/BinaryStreamWriter.cs + uploadId: 762203 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..b5b759f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3752816249ea16e4aba70adb03f01673 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBuffer.cs + uploadId: 762203 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..09c228f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0db5f2f7f9a349d4d89e2329ffd563b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ByteBufferEqualityComparer.cs + uploadId: 762203 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..762b5aa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7cd272a7ff953734bbacab398dfc9fe8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/DataDirectory.cs + uploadId: 762203 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..8e41f97 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: be7f3ca6a9f5ad34db68702bd99778fd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Image.cs + uploadId: 762203 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..fe54856 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ba04d4e3389423e47b35aaccee205576 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageReader.cs + uploadId: 762203 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..65ae2e1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 88ebae078ed1c8346be0a945ce3b03b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/ImageWriter.cs + uploadId: 762203 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..4861ae6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dc33915bdaaf42a428f8a4694e9611a8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/Section.cs + uploadId: 762203 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..764d726 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1d36ca0589eb8014fa28bc58a88ae85f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.PE/TextMap.cs + uploadId: 762203 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..71e35a1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 4304d9110a6c73049a70f3d001e1ac37 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.Tests.props + uploadId: 762203 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..ccdbf3d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 08f20128864a55d45abd8a30563f2f5c +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..d0c6246 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 70249fc3714c2ba43bb69eeceaf02171 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nunit + uploadId: 762203 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..9f31a98 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 9ab5a3af6caf6d14da0bad821a809a27 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.nuspec + uploadId: 762203 diff --git a/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln new file mode 100644 index 0000000..7b3ff98 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln @@ -0,0 +1,67 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28516.95 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil", "Mono.Cecil.csproj", "{16C3FA32-4775-497F-8794-DD5AF13CFE22}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Pdb", "symbols\pdb\Mono.Cecil.Pdb.csproj", "{4D22D51C-4230-46AF-8657-4FD757D9C8BC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Mdb", "symbols\mdb\Mono.Cecil.Mdb.csproj", "{5A5F84B1-DD1A-4134-932C-C3AF5BDAD391}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Tests", "Test\Mono.Cecil.Tests.csproj", "{EA7ADB7D-9FC1-4B4C-BBE9-359DD5B2E345}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Mdb.Tests", "symbols\mdb\Test\Mono.Cecil.Mdb.Tests.csproj", "{901E005D-CD64-4DC5-8CD0-4A49A7B0AF71}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Pdb.Tests", "symbols\pdb\Test\Mono.Cecil.Pdb.Tests.csproj", "{50FC1815-A653-48D0-95E2-DB48CB01F4E1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Rocks", "rocks\Mono.Cecil.Rocks.csproj", "{70E05599-64EE-4C11-A2F8-EE4113309039}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Cecil.Rocks.Tests", "rocks\Test\Mono.Cecil.Rocks.Tests.csproj", "{EF768F7A-3C08-45EE-8A7E-BB5A81BADB7B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {16C3FA32-4775-497F-8794-DD5AF13CFE22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {16C3FA32-4775-497F-8794-DD5AF13CFE22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {16C3FA32-4775-497F-8794-DD5AF13CFE22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {16C3FA32-4775-497F-8794-DD5AF13CFE22}.Release|Any CPU.Build.0 = Release|Any CPU + {4D22D51C-4230-46AF-8657-4FD757D9C8BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D22D51C-4230-46AF-8657-4FD757D9C8BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D22D51C-4230-46AF-8657-4FD757D9C8BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D22D51C-4230-46AF-8657-4FD757D9C8BC}.Release|Any CPU.Build.0 = Release|Any CPU + {5A5F84B1-DD1A-4134-932C-C3AF5BDAD391}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A5F84B1-DD1A-4134-932C-C3AF5BDAD391}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A5F84B1-DD1A-4134-932C-C3AF5BDAD391}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A5F84B1-DD1A-4134-932C-C3AF5BDAD391}.Release|Any CPU.Build.0 = Release|Any CPU + {EA7ADB7D-9FC1-4B4C-BBE9-359DD5B2E345}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA7ADB7D-9FC1-4B4C-BBE9-359DD5B2E345}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA7ADB7D-9FC1-4B4C-BBE9-359DD5B2E345}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA7ADB7D-9FC1-4B4C-BBE9-359DD5B2E345}.Release|Any CPU.Build.0 = Release|Any CPU + {901E005D-CD64-4DC5-8CD0-4A49A7B0AF71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {901E005D-CD64-4DC5-8CD0-4A49A7B0AF71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {901E005D-CD64-4DC5-8CD0-4A49A7B0AF71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {901E005D-CD64-4DC5-8CD0-4A49A7B0AF71}.Release|Any CPU.Build.0 = Release|Any CPU + {50FC1815-A653-48D0-95E2-DB48CB01F4E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50FC1815-A653-48D0-95E2-DB48CB01F4E1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50FC1815-A653-48D0-95E2-DB48CB01F4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50FC1815-A653-48D0-95E2-DB48CB01F4E1}.Release|Any CPU.Build.0 = Release|Any CPU + {70E05599-64EE-4C11-A2F8-EE4113309039}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70E05599-64EE-4C11-A2F8-EE4113309039}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70E05599-64EE-4C11-A2F8-EE4113309039}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70E05599-64EE-4C11-A2F8-EE4113309039}.Release|Any CPU.Build.0 = Release|Any CPU + {EF768F7A-3C08-45EE-8A7E-BB5A81BADB7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF768F7A-3C08-45EE-8A7E-BB5A81BADB7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF768F7A-3C08-45EE-8A7E-BB5A81BADB7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF768F7A-3C08-45EE-8A7E-BB5A81BADB7B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {97A7013A-E1DE-4D11-93C5-212D0A7E85C9} + EndGlobalSection +EndGlobal 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..b64d41c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 246f31a0e00fea74a93125fec6d80da8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil.sln + uploadId: 762203 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..654d3cf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7add098db82a032428c139b59f0878be +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ArrayType.cs + uploadId: 762203 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..3743538 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1c1ac0fc48f2d424f9c0d9fa74b16b07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyDefinition.cs + uploadId: 762203 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..cc88db1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 257c8151138cda34dafbe0ca56ebedf4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyFlags.cs + uploadId: 762203 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..7ef6c99 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b200bdec09584af41bd3f656e091cccf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyHashAlgorithm.cs + uploadId: 762203 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..94dc9b7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.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 MonoFN.Cecil; +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..56fbec0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ff23d7231ddfa574b816532360874834 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyInfo.cs + uploadId: 762203 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..c50137e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c3d5a207fc55ac5419e42c86cef15db0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyLinkedResource.cs + uploadId: 762203 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..e83592e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 68b90f4f023a67e4db7ddb52802ca21e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameDefinition.cs + uploadId: 762203 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..4ef59d4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fdaf7563058f3f54ba3e6b32c888eb3c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyNameReference.cs + uploadId: 762203 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..84dfbaa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d8fb74d16029713429e831cb3b5b0861 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyReader.cs + uploadId: 762203 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..6aff61e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9a88ce645df13da4aa9eca43f013f6ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/AssemblyWriter.cs + uploadId: 762203 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..7a08af1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4ab1d53794ef7444c81e276b5a3a5c2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/BaseAssemblyResolver.cs + uploadId: 762203 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..73b548d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 087823dce9623d348927f193f95a1807 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CallSite.cs + uploadId: 762203 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..e19f2c8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs @@ -0,0 +1,10 @@ +namespace MonoFN.Cecil +{ + + 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..4a06c31 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: da413e0e98056364a9512beb26e9aea5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Consts.cs + uploadId: 762203 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..395ac04 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 91db4bc8250b4b949bf262a196c9c0c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/CustomAttribute.cs + uploadId: 762203 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..43af780 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5390b4f18cb83c046baaaa937fec06a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/DefaultAssemblyResolver.cs + uploadId: 762203 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..7d82aa3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b5874acd8fee1b4499a3249dd4648428 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EmbeddedResource.cs + uploadId: 762203 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..b7901b3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 20c18be3d45bbf84d82732fd16751df3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventAttributes.cs + uploadId: 762203 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..0b26baf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4b201911ff1f925438edbe5b91275268 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventDefinition.cs + uploadId: 762203 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..f718b6d --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 94d9b58b6c4033343b975f6730127514 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/EventReference.cs + uploadId: 762203 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..1abf818 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b7fa6f0bdd43c0d44a63cd789a765eeb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ExportedType.cs + uploadId: 762203 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..d35f511 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8e9187082e8fc1446a8c2aa12d8f3fa4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldAttributes.cs + uploadId: 762203 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..bbe0bc5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 00dcb95135f39ec4c923f7923bcc93ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldDefinition.cs + uploadId: 762203 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..317cd8c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 990be2becde06b4468f79bd17646a47a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FieldReference.cs + uploadId: 762203 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..a937942 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 56e662d580c0894489669cc8058a2783 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FileAttributes.cs + uploadId: 762203 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..8c8037c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 91d84cd146416f945acbfce1b1dbcacb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/FunctionPointerType.cs + uploadId: 762203 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..65bcb65 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fd61b82dc3c653d42a1fc0f4e4c003f8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceMethod.cs + uploadId: 762203 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..1088685 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 536e7e359f0fa6c449a352476e8af197 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericInstanceType.cs + uploadId: 762203 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..6245613 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a20d39eab5fe7884c8cdf55fde143904 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameter.cs + uploadId: 762203 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..c57896f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 406369465fc07a643b3923d53fb1e941 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterAttributes.cs + uploadId: 762203 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..47d7573 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4151464170fe40440a031e1f4f2ecf53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/GenericParameterResolver.cs + uploadId: 762203 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..98a76c8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 58315b928b9e49540b9d1591523c92c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IConstantProvider.cs + uploadId: 762203 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..19c9733 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1585603aa3fe4f1409c42694378a557c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ICustomAttributeProvider.cs + uploadId: 762203 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..7704496 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 22b3a8ddd25a989449e5e9ffc632bc00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericInstance.cs + uploadId: 762203 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..08d3a2a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c81842a9d1ff34f4b94b047da954469f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IGenericParameterProvider.cs + uploadId: 762203 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..5b3dce8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a73829f4e35521e4892ef4e0cce2e4f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMarshalInfoProvider.cs + uploadId: 762203 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..6b8da93 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 482577ba8693d3f438504e54d6edb971 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMemberDefinition.cs + uploadId: 762203 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..1a0c565 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: da1ce103d170a414c8c8c6849a8856a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataScope.cs + uploadId: 762203 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..6dfd9d6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c01133d3a99e95542b153987746696d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMetadataTokenProvider.cs + uploadId: 762203 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..d83c54f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8fdcc55987f0a4f4b93358d994704a98 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/IMethodSignature.cs + uploadId: 762203 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..1ffa204 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fa57d261f1a90c746abaef3d59b4c655 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Import.cs + uploadId: 762203 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..dfca101 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 18c45c1a0891fdc40a07d309b564ec6d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/LinkedResource.cs + uploadId: 762203 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..b93920e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 136b98b695702f048857ea31d91193fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ManifestResourceAttributes.cs + uploadId: 762203 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..f705c7b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 081d03dc18ced1648ae4d9f2eefd3370 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MarshalInfo.cs + uploadId: 762203 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..dde9ef1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e5caa999f42a585459c62b7b65eefdd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberDefinitionCollection.cs + uploadId: 762203 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..b9138fe --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2dcf551ad731c204fa148ec1ee0ce881 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MemberReference.cs + uploadId: 762203 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..479d27e --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fdd2c926d773ffd4692e2d042135fd2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataResolver.cs + uploadId: 762203 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..d028896 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6ea39c5122499d14fbca126d3ed39890 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MetadataSystem.cs + uploadId: 762203 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..e2a9bb3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 84996fdfc826be145836c454e3781c51 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodAttributes.cs + uploadId: 762203 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..5e4eec2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1d279830051a3cf4a9a8198756171875 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodCallingConvention.cs + uploadId: 762203 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..ed2bf33 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 51ade7e624f84ae4abb8dc28a51b3082 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodDefinition.cs + uploadId: 762203 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..f31b3bf --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bbe7f8d150d8c6b48abd24ac7718c9f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodImplAttributes.cs + uploadId: 762203 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..158ea16 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e894eb84c10b07f42bf952d556a35c1c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReference.cs + uploadId: 762203 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..22744c2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e61a3569af766524884d80a918529ab8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReferenceComparer.cs + uploadId: 762203 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..b546715 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 51d33467d0e0c6f46a788d5da23d7b23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodReturnType.cs + uploadId: 762203 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..2e7dd78 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d6ed7cb61b1647749854923d7f4f2c4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSemanticsAttributes.cs + uploadId: 762203 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..ca892e4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aab27df5116b6ef46ac37993c843e4f0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/MethodSpecification.cs + uploadId: 762203 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..6e74940 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1fc5ce450d74933428a568f51cb49c16 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Modifiers.cs + uploadId: 762203 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..37f7b0c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: be36668e876daba419d4cf3aa5e8cac9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleDefinition.cs + uploadId: 762203 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..b7b947c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b85d54df2658b6d4bab3d27002f45a01 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleKind.cs + uploadId: 762203 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..8d62de7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2b28049ba3ae6394dbe9f227aaf9c88e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ModuleReference.cs + uploadId: 762203 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..b515985 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bb32c8b42f4f314448ff4b2c7eae0533 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/NativeType.cs + uploadId: 762203 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..50cbfcd --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cf3019a5e2270654abca972684f5c2f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeAttributes.cs + uploadId: 762203 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..24321fa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 56eb336b3d1ea2b4094fbb5586a11d49 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PInvokeInfo.cs + uploadId: 762203 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..14fabc7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dc153e8db6e9d1b4ca432cd66ecfe423 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterAttributes.cs + uploadId: 762203 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..12f9a8a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b6759139d8b53974087e609a88da6072 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinition.cs + uploadId: 762203 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..1321d66 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 01e788194a3a3604784e347ac41ec6b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterDefinitionCollection.cs + uploadId: 762203 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..3830856 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0643ce0ca6e47bf4e92518aed8a3a955 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ParameterReference.cs + uploadId: 762203 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..e1271ef --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6840e25b64ef0924d84724d5587cfa68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PinnedType.cs + uploadId: 762203 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..bd08332 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7b6a1c540eedbb04082628ad5dd10583 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PointerType.cs + uploadId: 762203 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..64b1ae9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 163bf99004c8e294f8b4113c29cae987 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyAttributes.cs + uploadId: 762203 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..ef38872 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6c1878ca62c0e6f4595238eb6a87edfd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyDefinition.cs + uploadId: 762203 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..5968daa --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a30c5751a554ce649bf88a597e4075e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/PropertyReference.cs + uploadId: 762203 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..82bfcd0 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 32a78d7552e92774cbf44ddd46fb7aa4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/ReferenceType.cs + uploadId: 762203 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..40a15d5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b8b738634202e694ebec627727698b83 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Resource.cs + uploadId: 762203 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..be792ca --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7a39c3df615f71b409a5aa3d102fb35e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SecurityDeclaration.cs + uploadId: 762203 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..5755f19 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 31522df8b7c72c1499a3fb951e73a69a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/SentinelType.cs + uploadId: 762203 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..8861ead --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 64ac90055dca01347b6ed93dff5e4284 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TargetRuntime.cs + uploadId: 762203 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..5bf414b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d2bba6ea16ba63147b7bff91a65ad5fa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/Treatments.cs + uploadId: 762203 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..a5394db --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a634580522059d74bb5ad1f997af1284 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeAttributes.cs + uploadId: 762203 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..a498bf8 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3777ff31cb318a8488a68226f53f7381 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeComparisonMode.cs + uploadId: 762203 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..9c3de17 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4616a4ea6dc219f419ae9ba98109f98c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinition.cs + uploadId: 762203 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..2a76b3f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4440ec34ba0da7b4aac0fc4c009035aa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeDefinitionCollection.cs + uploadId: 762203 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..de6dba2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 14cea45910a5fc94ca89429bd0587148 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeParser.cs + uploadId: 762203 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..efa9364 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: be8b0c2aa3d8d534a90ff392789fac67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReference.cs + uploadId: 762203 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..7a5b55c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5e048a822386ae34ab1124eaf29e1d84 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeReferenceEqualityComparer.cs + uploadId: 762203 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..a25b89a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 94140ae852ea8954489f238f1e59f865 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeResolver.cs + uploadId: 762203 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..6e53867 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4c1d979e44d835a4dbcdefa35a45a07a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSpecification.cs + uploadId: 762203 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..55c0900 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 68c81a3fab6a4ee4dbfc5e83e365d1c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/TypeSystem.cs + uploadId: 762203 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..190e04a --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b01b9a572a6975b4c806b3269b858cb1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/VariantType.cs + uploadId: 762203 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..4ec55c1 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4816f370ee0b9f64ca793643ba5481ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Cecil/WindowsRuntimeProjections.cs + uploadId: 762203 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..57e75c2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 465b9dfce90e5db47b3b735df83f25fd +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..b7f9874 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d1f0d8a176addda42a579d710c12a7ec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/Collection.cs + uploadId: 762203 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..5bc5689 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d94cb0938831f1349b60e63292a04b67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Collections.Generic/ReadOnlyCollection.cs + uploadId: 762203 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..dd61b11 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 300d5aad29f8cf04c8a1a3f0dfa3e014 +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..8097783 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 40b945bb6ba518c4b8ce54b5e54a06b5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoConvert.cs + uploadId: 762203 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..6a929c2 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c49085888b0afc047af3a03b536acdc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.Security.Cryptography/CryptoService.cs + uploadId: 762203 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..7cfba3f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 54d61064c2612804c9547e313fb726f0 +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..ce701e5 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1aa8d86dc5c9323419e8b535d582fccf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Disposable.cs + uploadId: 762203 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..50d4861 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4662c38d1a951c24b89aeae3390acd39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/Empty.cs + uploadId: 762203 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..b7d0dc4 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3c35ecf3bff670b4c8b367ed632eee37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/Mono/MergeSort.cs + uploadId: 762203 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..066c093 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 23311a592bb0c5640b641143d87bf5b7 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/MonoFN.Cecil.asmdef + uploadId: 762203 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..2bfa196 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs @@ -0,0 +1,21 @@ +// +// Author: +// Jb Evain (jbevain@gmail.com) +// +// Copyright (c) 2008 - 2015 Jb Evain +// +// Licensed under the MIT/X11 license. +// + +using MonoFN.Cecil; +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..5309b25 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 468fd5ab13cb01b4381e9c667167b2e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/ProjectInfo.cs + uploadId: 762203 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..68954f7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 3bca5ad696a6f8e47b4651fa04b2cd96 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/README.md + uploadId: 762203 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 0000000000000000000000000000000000000000..c0380d194f28fc76a32d609b60da01d7f75657ff GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096iT%UggG;A_TF?RUV?t(kNEnx54!(WE| zo@MGjY|$Id%cG-Hy@5ZU6GjEe_m6m7Uv$xpOcIN1B2`p7e>sB3~cm zDW?cfd({<#QDET<$3xN#qdK`)4E<=-dHnFad3ts`>|GE5@D-l+8RrSv6_;LzzU!~G zDm71|RX2O{FNU*iu>_zkxxvYHzN7NP0U`%U6b2c#K#i!@kbU;m&G}wLoGQ8MzvN7I zVps}ItYiDqW&pNdSAEmIua6gslpwgz;|JK$fk347H3pq@PT~VnK2Cagr>-!#sS2uz z?Er147%=gTx1{Lwitv&hd-PjTZ*up+3wSwrgYZA@SdqYdIwefG0Jr1>3uCU`_Oz%+ zMjn{Y&tQb2iHN9%LAqS3jJRp%Gg_`=^CZ;s4Qs6+7iARLaJNyT32*%p!zfQ#{mx4S z^}RMT_mkhK%9Qzrqfd8knqfVM&6bEaW+rN;I$~OI74DrrYP|z(;tRA<^x+KVX6+Tg zCfUqOu zs^7YOICxFn@|;LrrzlTuQ-2P8i@R^W>ZK`(uUnbRg&hZ6grggv>|2HE2rKJ?CnnA8 zR{HQmyK%WDlW?y{ryW|@-1NcKWy;Fh^T>F;uh}LqMwg+;(p+)^E|s(wxvCLpH<1nQ i{y?rxRw8t*>j7s<@|KG8kMC+K`9c~&xPULDw@({VAtYk} literal 0 HcmV?d00001 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..760020b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 71693fd731252cb4a9bd4d8abf7846c0 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/cecil.snk + uploadId: 762203 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..aaa0b72 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8723713565b9be445bb695a476bc77c6 +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..9ea055b --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4929a85ce2b13ee4799e6f2a768bd5e9 +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..075554f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f9ceb0221cb78b247b676d0ea5d57fc8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/AssemblyInfo.cs + uploadId: 762203 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..0f59f1f --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 370e40931a1b9cf478e08e66fd2a9eac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/DocCommentId.cs + uploadId: 762203 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..d84ed03 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b481a942e0bdf8649b6b5b20db207190 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/Functional.cs + uploadId: 762203 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..1ec983c --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2916628c81c279046adb746612d6067b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ILParser.cs + uploadId: 762203 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..a2007b3 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0c47d4756ed8120429bad0887cff0366 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodBodyRocks.cs + uploadId: 762203 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..b884af7 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7a5d7596633ddb043b9785a7fa7bfa6f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/MethodDefinitionRocks.cs + uploadId: 762203 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..7d2f4f9 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d90334a1cd085c047b9b8eddaa65cf09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ModuleDefinitionRocks.cs + uploadId: 762203 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..00a4580 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d276fe9256961b847b51a9bfc735384b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/ParameterReferenceRocks.cs + uploadId: 762203 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..d22d172 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e9d89d937b712004089aafb3a1391907 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/SecurityDeclarationRocks.cs + uploadId: 762203 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..9267eca --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b4d5393ac3307a84ca5babfbdcad9eea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeDefinitionRocks.cs + uploadId: 762203 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..71da0f6 --- /dev/null +++ b/Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 25d089a650c8e0f45a6f9086aaf0004a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/CodeGenerating/cecil-0.11.4/rocks/Mono.Cecil.Rocks/TypeReferenceRocks.cs + uploadId: 762203 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..3cc68ab --- /dev/null +++ b/Assets/FishNet/DOCUMENTATION.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: b8eb79f9866e25342b4565ac8c981645 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/DOCUMENTATION.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos.meta b/Assets/FishNet/Demos.meta new file mode 100644 index 0000000..e17a8d8 --- /dev/null +++ b/Assets/FishNet/Demos.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d066166ea67f71429de1af8cf635915 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Authenticator.meta b/Assets/FishNet/Demos/Authenticator.meta new file mode 100644 index 0000000..a4e42c3 --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ecbf3577da9f5be4fa280507f592d747 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Authenticator/Scenes.meta b/Assets/FishNet/Demos/Authenticator/Scenes.meta new file mode 100644 index 0000000..5c1296b --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 92540a55e648a2448bb430e825f01bc4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity b/Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity new file mode 100644 index 0000000..0492226 --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity @@ -0,0 +1,359 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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_SendPointerHoverToParent: 1 + 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_ConstrainProportionsScale: 0 + 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: + _allowHostAuthentication: 0 + _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_ConstrainProportionsScale: 0 + 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: + _refreshDefaultPrefabs: 0 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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/Demos/Authenticator/Scenes/Authenticator.unity.meta b/Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity.meta new file mode 100644 index 0000000..030aac1 --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0bc02b628363de5499d5e7c00bd63b1b +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Authenticator/Scenes/Authenticator.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Authenticator/Scripts.meta b/Assets/FishNet/Demos/Authenticator/Scripts.meta new file mode 100644 index 0000000..2c6fbb0 --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0cb111b6bede3a1478c9b179a609d822 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs b/Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs new file mode 100644 index 0000000..9991b66 --- /dev/null +++ b/Assets/FishNet/Demos/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/Demos/Authenticator/Scripts/Broadcasts.cs.meta b/Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs.meta new file mode 100644 index 0000000..38a3ce2 --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d26bb0c99070e9b49bc8632dc0b68214 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Authenticator/Scripts/Broadcasts.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs b/Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs new file mode 100644 index 0000000..905c5e7 --- /dev/null +++ b/Assets/FishNet/Demos/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, Channel channel) + { + //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.IsAuthenticated) + { + 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(); + using (RNGCryptoServiceProvider rng = new()) + { + 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() + { + Password = _hostHash, + }; + + base.NetworkManager.ClientManager.Broadcast(hpb); + return true; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs.meta b/Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs.meta new file mode 100644 index 0000000..321da5e --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c7497d751bb68f444b4343e3edc28e39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Authenticator/Scripts/HostAuthenticator.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs b/Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs new file mode 100644 index 0000000..c5f91ac --- /dev/null +++ b/Assets/FishNet/Demos/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() + { + 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, Channel channel) + { + /* 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.IsAuthenticated) + { + 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, Channel channel) + { + 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() + { + 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/Demos/Authenticator/Scripts/PasswordAuthenticator.cs.meta b/Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs.meta new file mode 100644 index 0000000..e45abfe --- /dev/null +++ b/Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 226e4eaf2fa685f48bdc3dfaa87c1453 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Authenticator/Scripts/PasswordAuthenticator.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks.meta b/Assets/FishNet/Demos/Benchmarks.meta new file mode 100644 index 0000000..ca684b0 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 373a11f85d89ed2478f8c9616440ea26 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Benchmarks/License.txt b/Assets/FishNet/Demos/Benchmarks/License.txt new file mode 100644 index 0000000..c18865e --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/License.txt @@ -0,0 +1,11 @@ +This license is solely for files within this Benchmarks folder. This license does not invalidate any other licenses for Fish-Networking or other third-party licenses included within Fish-Networking. + +License type: MIT. + +Copyright 2025 FirstGearGames (Benjamin Berwick). + +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/Demos/Benchmarks/License.txt.meta b/Assets/FishNet/Demos/Benchmarks/License.txt.meta new file mode 100644 index 0000000..2fb8a08 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/License.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 38954353b7368554b949cf2ef1d7fb29 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/License.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform.meta new file mode 100644 index 0000000..6bcf5d1 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 01b17d2e9e8e92c468aa520552226c3d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials.meta new file mode 100644 index 0000000..d0507f0 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ab9dbe1f15e41e241a375833da5ff97e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat new file mode 100644 index 0000000..320f862 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat @@ -0,0 +1,144 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Translucent_Unlit + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _ENVIRONMENTREFLECTIONS_OFF + - _SPECULARHIGHLIGHTS_OFF + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: + - _GLOSSYREFLECTIONS_OFF + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + - SHADOWCASTER + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 0 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 0 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 0 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 1, g: 1, b: 1, a: 0.039215688} + - _Color: {r: 1, g: 1, b: 1, a: 0.039215688} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &66919643633663574 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat.meta new file mode 100644 index 0000000..1d450cc --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 22954e4ae0b986e41b7190d7a1548a41 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Materials/Translucent_Unlit.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs.meta new file mode 100644 index 0000000..52db18f --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3060a1729644a5a489574203c0653563 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab new file mode 100644 index 0000000..00f7014 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab @@ -0,0 +1,330 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2844603473186117093 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1403066764901202070} + - component: {fileID: 136486614620090110} + m_Layer: 0 + m_Name: Sprite (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1403066764901202070 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2844603473186117093} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0.54, y: 0.28, z: 0} + m_LocalScale: {x: 8.15, y: 4, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &136486614620090110 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2844603473186117093} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, 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: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.16, y: 0.16} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !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: NetworkTransform Benchmark 2D + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4555216020026969547} + - {fileID: 1403066764901202070} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 6667641716399555817} + - {fileID: -5271135124957689192} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 12 + k__BackingField: 0 + k__BackingField: 5448584063611559504 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !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} + _tickCallbacks: 10 + _isActive: 1 + _is2d: 1 + _axes: 2 + _chancePerAxes: 0.5 + _rotationChance: 1 + _randomMovement: 1 + _goalDatas: [] + _moveInUpdate: 0 + _moveRate: 3 + _rotateRate: 3000 + _range: 10 +--- !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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 3 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 1 + Y: 1 + Z: 1 + _synchronizeScale: 0 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!1 &8182551741705586143 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4555216020026969547} + - component: {fileID: 7640783307509024941} + m_Layer: 0 + m_Name: Sprite + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4555216020026969547 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8182551741705586143} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 10, y: 10, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &7640783307509024941 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8182551741705586143} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, 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: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.16, y: 0.16} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab.meta new file mode 100644 index 0000000..bc30cd3 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 2D.prefab.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 35639798ad77fc145871588b25d66259 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform + Benchmark 2D.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab new file mode 100644 index 0000000..4c0587f --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab @@ -0,0 +1,244 @@ +%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: NetworkTransform Benchmark 3D Normal + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2745247498903379289} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 6667641716399555817} + - {fileID: -5271135124957689192} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 31 + k__BackingField: 0 + k__BackingField: 12758516177287752350 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !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} + _tickCallbacks: 10 + _isActive: 1 + _is2d: 0 + _axes: 3 + _chancePerAxes: 0.8 + _rotationChance: 0.33 + _randomMovement: 1 + _goalDatas: [] + _moveInUpdate: 0 + _moveRate: 3 + _rotateRate: 30 + _range: 10 +--- !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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 3 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 0 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!1 &5839577311109879042 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2745247498903379289} + - component: {fileID: 3127415364279584095} + - component: {fileID: 5921142425762840611} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2745247498903379289 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5839577311109879042} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3127415364279584095 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5839577311109879042} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5921142425762840611 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5839577311109879042} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab.meta new file mode 100644 index 0000000..8589f0a --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Normal.prefab.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: fe2b65b02f0484b41aa8cfa9fbbb0e1d +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform + Benchmark 3D Normal.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab new file mode 100644 index 0000000..175d071 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab @@ -0,0 +1,521 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2001404003834437235 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6977889744902779521} + - component: {fileID: 1078938483813926015} + - component: {fileID: 8480692657044937796} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6977889744902779521 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2001404003834437235} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1078938483813926015 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2001404003834437235} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &8480692657044937796 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2001404003834437235} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &3163177272819576045 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3428394258780372578} + - component: {fileID: 9011913500655397983} + - component: {fileID: 7409727912384225873} + 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 &3428394258780372578 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3163177272819576045} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 90, z: 90} +--- !u!33 &9011913500655397983 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3163177272819576045} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7409727912384225873 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3163177272819576045} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !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: 6130968803401110125} + - component: {fileID: -827448880904191606} + - component: {fileID: 4918976593009724217} + m_Layer: 0 + m_Name: NetworkTransform Benchmark 3D Rigidbodies + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 3782139768438858250} + - {fileID: 6977889744902779521} + - {fileID: 3708494304422182778} + - {fileID: 3428394258780372578} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 6130968803401110125} + - {fileID: -827448880904191606} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 17 + k__BackingField: 0 + k__BackingField: 6471616483407953140 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!114 &6130968803401110125 +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: 4792aba6419a83f4b9a79279748188e9, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 4512293259955182956} + _networkObjectCache: {fileID: 4512293259955182956} + _tickCallbacks: 6 + _isActive: 1 + _force: 30 + _interval: + Minimum: 3 + Maximum: 3 +--- !u!114 &-827448880904191606 +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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 0 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!54 &4918976593009724217 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4512293259955182959} + 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!1 &4864096001165680217 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3708494304422182778} + - component: {fileID: 2634080529458410135} + - component: {fileID: 1614143093118776398} + 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 &3708494304422182778 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4864096001165680217} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0} +--- !u!33 &2634080529458410135 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4864096001165680217} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1614143093118776398 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4864096001165680217} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &8649225185085352371 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3782139768438858250} + - component: {fileID: 1576000441102202228} + - component: {fileID: 5408706761248428093} + - component: {fileID: 7198232707440963935} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3782139768438858250 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1576000441102202228 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5408706761248428093 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!135 &7198232707440963935 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + 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/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab.meta new file mode 100644 index 0000000..e6c0815 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark 3D Rigidbodies.prefab.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: dafef736ca1ae384e9a19eb672843563 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform + Benchmark 3D Rigidbodies.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab new file mode 100644 index 0000000..e47c066 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab @@ -0,0 +1,244 @@ +%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: NetworkTransform Benchmark Cubes + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2745247498903379289} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 6667641716399555817} + - {fileID: -5271135124957689192} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 36 + k__BackingField: 0 + k__BackingField: 15845183709739124870 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !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} + _tickCallbacks: 10 + _isActive: 1 + _is2d: 0 + _axes: 3 + _chancePerAxes: 0.8 + _rotationChance: 0.33 + _randomMovement: 1 + _goalDatas: [] + _moveInUpdate: 0 + _moveRate: 3 + _rotateRate: 30 + _range: 10 +--- !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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 3 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 0 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!1 &5839577311109879042 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2745247498903379289} + - component: {fileID: 3127415364279584095} + - component: {fileID: 5921142425762840611} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2745247498903379289 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5839577311109879042} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3127415364279584095 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5839577311109879042} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5921142425762840611 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5839577311109879042} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab.meta new file mode 100644 index 0000000..7e80235 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Cubes.prefab.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: f32d4c19de900e64cb73cedcb8ba6f70 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform + Benchmark Cubes.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab new file mode 100644 index 0000000..0b7f75c --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab @@ -0,0 +1,269 @@ +%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: 6130968803401110125} + - component: {fileID: -827448880904191606} + - component: {fileID: 4918976593009724217} + m_Layer: 0 + m_Name: NetworkTransform Benchmark Rigidbodies + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 3782139768438858250} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 6130968803401110125} + - {fileID: -827448880904191606} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 35 + k__BackingField: 0 + k__BackingField: 15013615375196309252 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!114 &6130968803401110125 +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: 4792aba6419a83f4b9a79279748188e9, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 4512293259955182956} + _networkObjectCache: {fileID: 4512293259955182956} + _tickCallbacks: 6 + _isActive: 1 + _force: 30 + _interval: + Minimum: 1 + Maximum: 3 +--- !u!114 &-827448880904191606 +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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 1 + _synchronizePosition: 1 + _positionSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeRotation: 1 + _rotationSnapping: + X: 0 + Y: 0 + Z: 0 + _synchronizeScale: 0 + _scaleSnapping: + X: 0 + Y: 0 + Z: 0 +--- !u!54 &4918976593009724217 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4512293259955182959} + 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!1 &8649225185085352371 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3782139768438858250} + - component: {fileID: 1576000441102202228} + - component: {fileID: 5408706761248428093} + - component: {fileID: 7198232707440963935} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3782139768438858250 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1576000441102202228 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5408706761248428093 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!135 &7198232707440963935 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8649225185085352371} + 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/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab.meta new file mode 100644 index 0000000..de6a2f2 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform Benchmark Rigidbodies.prefab.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: b8017cef39731ba439c70fecc09488e3 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Prefabs/NetworkTransform + Benchmark Rigidbodies.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt new file mode 100644 index 0000000..9a47394 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt @@ -0,0 +1,21 @@ +This is a simple benchmark for our NetworkTransform component. + +Setup: + * Start server and client in two editors, or clientHost. + +Notes: + * To configure spawned object: + * Open NetworkTransform Benchmark scene. + * Select any prefab NetworkTransform Benchmark XYZ. + + * To configure spawn count: + * Open NetworkTransform Benchmark scene. + * Select Prefab Spawner, and set amount. + + * To configure tick rate: + * Open NetworkTransform Benchmark scene. + * Select NetworkManager, change Tick Rate on TimeManager. + + * To configure object rates and movement: + * Open prefab NetworkTransform Benchmark XYZ. + * Configure MoveRandomly. \ No newline at end of file diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt.meta new file mode 100644 index 0000000..a4b4f51 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: c373771d26a3af04a84bd1afd5aabfe8 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/ReadMe.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes.meta new file mode 100644 index 0000000..dfcee44 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c4bb86f41e1c4a84585e1ebef18ea9a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity new file mode 100644 index 0000000..20ad284 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity @@ -0,0 +1,1374 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 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: 50 + _pingInterval: 15 + _physicsMode: 1 +--- !u!1 &391166852 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 391166855} + - component: {fileID: 391166854} + - component: {fileID: 391166853} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &391166853 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 391166852} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &391166854 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 391166852} + 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 &391166855 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 391166852} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 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: Prefab Spawner + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1484980761} + - {fileID: 1382558462} + - {fileID: 2106259602} + - {fileID: 1953541344} + - {fileID: 616031447} + - {fileID: 1096753270} + m_Father: {fileID: 0} + m_RootOrder: 2 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3675717101 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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: 255 + _addedNetworkObject: {fileID: 442045876} + _networkObjectCache: {fileID: 0} + _prefab: {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3} + _count: 50 + _displayText: {fileID: 0} +--- !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_UseViewFrustumForShadowCasterCull: 1 + 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: 2, z: 10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 585532993} + m_RootOrder: 0 + 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: -34.6} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 584355002} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &616031446 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 616031447} + - component: {fileID: 616031450} + - component: {fileID: 616031449} + - component: {fileID: 616031448} + m_Layer: 0 + m_Name: Cube (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &616031447 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 616031446} + m_LocalRotation: {x: 0.5, y: 0.5, z: -0.5, w: 0.5} + m_LocalPosition: {x: -21.43, y: 5.95, z: -0.39} + m_LocalScale: {x: 40, y: 3, z: 40} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 90, y: 90, z: 0} +--- !u!65 &616031448 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 616031446} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &616031449 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 616031446} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 22954e4ae0b986e41b7190d7a1548a41, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &616031450 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 616031446} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1001 &745745795 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408886575219561} + m_Modifications: + - target: {fileID: 818862022, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: m_AnchoredPosition.x + value: 155.1001 + objectReference: {fileID: 0} + - target: {fileID: 818862022, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: m_AnchoredPosition.y + value: -52.69995 + objectReference: {fileID: 0} + - target: {fileID: 3414662637240338350, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: m_AnchoredPosition.x + value: 78.1001 + objectReference: {fileID: 0} + - target: {fileID: 3414662637240338350, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: m_AnchoredPosition.y + value: -121.400024 + objectReference: {fileID: 0} + - 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: 4393252310969058991, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: m_AdditionalShaderChannelsFlag + value: 25 + 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!224 &745745796 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 745745795} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1096753269 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1096753270} + - component: {fileID: 1096753273} + - component: {fileID: 1096753272} + - component: {fileID: 1096753271} + m_Layer: 0 + m_Name: Cube (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1096753270 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1096753269} + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 5.95, z: 21.21} + m_LocalScale: {x: 46, y: 3, z: 40} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!65 &1096753271 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1096753269} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1096753272 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1096753269} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 22954e4ae0b986e41b7190d7a1548a41, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1096753273 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1096753269} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1382558461 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1382558462} + - component: {fileID: 1382558465} + - component: {fileID: 1382558464} + - component: {fileID: 1382558463} + 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 &1382558462 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382558461} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 27.53, z: 0} + m_LocalScale: {x: 46, y: 3, z: 46} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1382558463 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382558461} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1382558464 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382558461} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 22954e4ae0b986e41b7190d7a1548a41, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1382558465 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1382558461} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1484980760 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1484980761} + - component: {fileID: 1484980764} + - component: {fileID: 1484980763} + - component: {fileID: 1484980762} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1484980761 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1484980760} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -15.7, z: 0} + m_LocalScale: {x: 46, y: 3, z: 46} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1484980762 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1484980760} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1484980763 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1484980760} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 22954e4ae0b986e41b7190d7a1548a41, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1484980764 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1484980760} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1953541343 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1953541344} + - component: {fileID: 1953541347} + - component: {fileID: 1953541346} + - component: {fileID: 1953541345} + m_Layer: 0 + m_Name: Cube (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1953541344 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1953541343} + m_LocalRotation: {x: 0.5, y: 0.5, z: -0.5, w: 0.5} + m_LocalPosition: {x: 21.55, y: 5.95, z: -0.39} + m_LocalScale: {x: 40, y: 3, z: 40} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 90, y: 90, z: 0} +--- !u!65 &1953541345 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1953541343} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &1953541346 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1953541343} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 22954e4ae0b986e41b7190d7a1548a41, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1953541347 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1953541343} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &2106259601 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2106259602} + - component: {fileID: 2106259605} + - component: {fileID: 2106259604} + - component: {fileID: 2106259603} + 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 &2106259602 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2106259601} + m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 5.95, z: -21.7} + m_LocalScale: {x: 46, y: 3, z: 40} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0} +--- !u!65 &2106259603 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2106259601} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 2, y: 2, z: 2} + m_Center: {x: 0, y: 0, z: 0} +--- !u!23 &2106259604 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2106259601} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 22954e4ae0b986e41b7190d7a1548a41, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &2106259605 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2106259601} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 745745796} + m_Father: {fileID: 0} + m_RootOrder: 1 + 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: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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: 370472796} + - component: {fileID: 7443408886575219565} + - component: {fileID: 7443408886575219564} + - component: {fileID: 7443408886575219566} + - component: {fileID: 7443408886575219567} + - component: {fileID: 7443408886575219568} + - component: {fileID: 7443408886575219569} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &7443408886575219564 +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 &7443408886575219565 +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: 1, g: 1, b: 1, a: 1} + _placement: 2 + _secondsAveraged: 10 + _showOutgoing: 1 + _showIncoming: 1 +--- !u!114 &7443408886575219566 +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: 68828c85278210948b9d50a8db3aab74, type: 3} + m_Name: + m_EditorClassIdentifier: + _authenticator: {fileID: 0} + _remoteClientTimeout: 2 + _remoteClientTimeoutDuration: 60 + _allowPredictedSpawning: 0 + _reservedObjectIds: 15 + _syncTypeRate: 0.1 + SpawnPacking: + Position: 0 + Rotation: 2 + Scale: 2 + _changeFrameRate: 1 + _frameRate: 70 + _shareIds: 1 + _startOnHeadless: 1 +--- !u!114 &7443408886575219567 +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: f9b6b565cd9533c4ebc18003f0fc18a2, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 1, g: 1, b: 1, a: 1} + _placement: 1 + _hideTickRate: 1 +--- !u!114 &7443408886575219568 +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: 7d413700c8c3cec4a99fad3b66e4575e, type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !u!114 &7443408886575219569 +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: 6d0962ead4b02a34aae248fccce671ce, type: 3} + m_Name: + m_EditorClassIdentifier: + WriteSceneObjectDetails: 0 + ValidateRpcLengths: 1 + DisableObserversRpcLinks: 0 + DisableTargetRpcLinks: 0 + DisableServerRpcLinks: 0 + DisableReplicateRpcLinks: 0 + DisableReconcileRpcLinks: 0 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity.meta new file mode 100644 index 0000000..a740ec0 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform Benchmark.unity.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 961b96636e4c7644584dc8dfd0d0aad6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scenes/NetworkTransform + Benchmark.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts.meta new file mode 100644 index 0000000..98fc423 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9f8d1e8e1482b84468258a26a4887d07 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs new file mode 100644 index 0000000..b7eadec --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using FishNet.Utility.Template; +using UnityEngine; +using Random = UnityEngine.Random; + +namespace FishNet.Demo.Benchmarks.NetworkTransforms +{ + public class MoveRandomlyNonPhysics : TickNetworkBehaviour + { + [System.Serializable] + private struct GoalData + { + public Vector3 Position; + public Vector3 Eulers; + } + + [SerializeField] + private bool _isActive = true; + + [SerializeField] + private bool _is2d; + + [Header("Changes")] + [SerializeField] + [Range(0, 3)] + private byte _axes = 3; + [Range(0.1f, 1f)] + [SerializeField] + private float _chancePerAxes = 0.8f; + [SerializeField] + [Range(0f, 1f)] + private float _rotationChance = 0.33f; + + [Header("Movement")] + [Range(0f, 5f)] + [SerializeField] + private float _delayBetweenMovements = 1.5f; + [SerializeField] + private float _yOffsetPerInstance = 0f; + [SerializeField] + private bool _randomMovement = true; + [SerializeField] + private List _goalDatas = new(); + + [SerializeField] + private bool _moveInUpdate = false; + [SerializeField] + [Range(0.1f, 30f)] + private float _moveRate = 3f; + [Range(1f, 30f)] + [SerializeField] + private float _rotateRate = 30f; + [Range(0.1f, 40f)] + [SerializeField] + private float _range = 6f; + + + //Position to move towards. + private Vector3 _goalPosition; + //Rotation to move towards. + private Quaternion _goalRotation; + + private Quaternion _lastRot; + private int _nextGoalDataIndex = 0; + + private float _nextMoveTime = float.MinValue; + + public override void OnStartNetwork() + { + + if (!base.IsServerStarted && !base.Owner.IsLocalClient) + { + base.SetTickCallbacks(TickCallback.None); + DestroyImmediate(this); + } + else + { + if (_moveInUpdate) + base.SetTickCallbacks(TickCallback.Update); + else + base.SetTickCallbacks(TickCallback.Tick); + } + } + + protected override void TimeManager_OnUpdate() + { + if (_moveInUpdate) + Move(Time.deltaTime); + } + + protected override void TimeManager_OnTick() + { + if (!_moveInUpdate) + { + float delta = (float)base.TimeManager.TickDelta; + Move(delta); + } + } + + private void Move(float delta) + { + if (!_isActive) + return; + if (!base.IsController) + return; + if (Time.time < _nextMoveTime) + return; + + transform.position = Vector3.MoveTowards(transform.position, _goalPosition, _moveRate * delta); + if (!_is2d) + transform.rotation = Quaternion.RotateTowards(transform.rotation, _goalRotation, _rotateRate * delta); + else + transform.rotation = _goalRotation; + + if (transform.position == _goalPosition) + SetNextGoal(); + } + + public override void OnStartServer() + { + TrySetFirstGoal(); + } + + public override void OnStartClient() + { + TrySetFirstGoal(); + } + + private void TrySetFirstGoal() + { + if (!base.IsController) + return; + + SetNextGoal(); + + transform.position = _goalPosition; + transform.rotation = _goalRotation; + + _nextMoveTime = float.MinValue; + } + + private void SetNextGoal() + { + if (_randomMovement) + SetRandomGoal(); + else + SetSpecifiedGoal(); + + _nextMoveTime = Time.time + _delayBetweenMovements; + + void SetSpecifiedGoal() + { + if (_goalDatas.Count == 0) return; + + if (_nextGoalDataIndex >= _goalDatas.Count) + _nextGoalDataIndex = 0; + + int index = _nextGoalDataIndex; + _nextGoalDataIndex++; + + _goalPosition = _goalDatas[index].Position; + _goalRotation = Quaternion.Euler(_goalDatas[index].Eulers); + } + + void SetRandomGoal() + { + if (_axes > 0) + { + Vector3 rnd = (Random.insideUnitSphere * _range); + Vector3 next = transform.position; + + if (_axes >= 1 && RandomizeAxes()) + next.x = rnd.x; + if (_axes >= 2 && RandomizeAxes()) + next.y = rnd.y; + if (_axes >= 3 && RandomizeAxes()) + next.z = rnd.z; + + //Make sure at least one axes is set. + if (next == transform.position) + next.x = rnd.x; + + bool RandomizeAxes() => (Random.Range(0f, 1f) <= _chancePerAxes); + + _goalPosition = next; + } + + if (_rotationChance > 0f) + { + if (Random.Range(0f, 1f) <= _rotationChance) + { + Vector3 euler; + if (!_is2d) + { + euler = Random.insideUnitSphere * 180f; + } + else + { + float nextY = (transform.eulerAngles.y == 0f) ? 180f : 0f; + euler = new Vector3(0f, nextY, 0f); + } + + _goalRotation = Quaternion.Euler(euler); + } + else + { + _goalRotation = transform.rotation; + } + } + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs.meta new file mode 100644 index 0000000..758f5b2 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9880e85651efd71469092ce519317f7b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyNonPhysics.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs new file mode 100644 index 0000000..452b260 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs @@ -0,0 +1,75 @@ +using System; +using FishNet.Component.Transforming; +using FishNet.Managing.Timing; +using FishNet.Utility.Template; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine; +using Random = UnityEngine.Random; + +namespace FishNet.Demo.Benchmarks.NetworkTransforms +{ + public class MoveRandomlyPhysics : TickNetworkBehaviour + { + [SerializeField] + private bool _isActive = true; + + [Header("Movement")] + [Tooltip("How much force to apply.")] + [Range(0f, 1000f)] + [SerializeField] + private float _force = 10f; + [Tooltip("How often to apply force.")] + [SerializeField] + private FloatRange _interval = new FloatRange(3f, 10f); + + private uint _nextForceTick = TimeManager.UNSET_TICK; + private Rigidbody _rigidbody; + + private void Awake() + { + _rigidbody = GetComponent(); + } + + public override void OnStartNetwork() + { + if (!base.IsServerStarted) + { + base.SetTickCallbacks(TickCallback.None); + _rigidbody.isKinematic = true; + DestroyImmediate(this); + } + else + { + base.SetTickCallbacks(TickCallback.Tick); + } + } + + protected override void TimeManager_OnTick() + { + Move(); + } + + private void Move() + { + if (!_isActive) + return; + + uint tick = base.TimeManager.LocalTick; + if (tick < _nextForceTick) + return; + + _nextForceTick = tick + base.TimeManager.TimeToTicks(_interval.RandomInclusive(), TickRounding.RoundUp); + + Vector3 force = Random.insideUnitSphere * _force; + + //Always ensure vertical movement, and movement away from center. + if (Math.Sign(force.x) == Math.Sign(transform.position.x)) + force.x *= -1f; + force.y = _force; + if (Math.Sign(force.z) == Math.Sign(transform.position.z)) + force.z *= -1f; + + _rigidbody.AddForce(force, ForceMode.Impulse); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs.meta new file mode 100644 index 0000000..0884965 --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4792aba6419a83f4b9a79279748188e9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/MoveRandomlyPhysics.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs new file mode 100644 index 0000000..65bb43b --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs @@ -0,0 +1,93 @@ +#if UNITY_EDITOR || !UNITY_SERVER +using System; +using FishNet.Component.Utility; +using FishNet.Connection; +using FishNet.Managing.Statistic; +using FishNet.Object; +using UnityEngine; +using UnityEngine.UI; + +namespace FishNet.Demo.Benchmarks.NetworkTransforms +{ + public class PrefabSpawner : NetworkBehaviour + { + [Header("General")] + [SerializeField] + private NetworkObject _prefab; + + [Header("Spawning")] + [SerializeField] + private int _count = 500; + + [Header("Display")] + [SerializeField] + private Text _displayText; + + // [SerializeField] + // private float _xyRange = 15f; + // [SerializeField] + // private float _zRange = 100f; + + private float _resetBandwidthTime = float.NegativeInfinity; + + public override void OnStartServer() + { + if (_prefab == null) + { + Debug.LogError($"Prefab is null."); + return; + } + + NetworkObject prefab = _prefab; + Vector3 currentPosition = transform.position; + + for (int i = 0; i < _count; i++) + { + NetworkObject nob = Instantiate(prefab, currentPosition, Quaternion.identity); + base.Spawn(nob); + } + } + + public override void OnSpawnServer(NetworkConnection connection) + { + //Reset bandwidth half a second after spawning in objects for a client. + _resetBandwidthTime = Time.time + 1f; + } + + private void Update() + { + if (_displayText == null) + return; + if (!base.IsServerInitialized) + return; + + if (_resetBandwidthTime != float.NegativeInfinity && Time.time >= _resetBandwidthTime) + { + _resetBandwidthTime = float.NegativeInfinity; + BandwidthDisplay bd = GameObject.FindObjectOfType(); + if (bd != null) + { + bd.ResetAverages(); + Debug.Log($"Resetting bandwidth averages."); + } + } + + uint updateFrequency = (uint)Mathf.FloorToInt((float)base.TimeManager.TickRate / 4f); + if (updateFrequency < 1) + updateFrequency = 1; + + if (base.TimeManager.LocalTick % updateFrequency == 0) + { + _displayText.text = "Spawned: " + _count; + _displayText.text += Environment.NewLine + "Tick Rate: " + base.TimeManager.TickRate; + + BandwidthDisplay bd = base.NetworkManager.gameObject.GetComponent(); + ulong serverOutAverage = bd.ServerAverages.GetAverage(inAverage: false); + + float perTransformAverage = (float)serverOutAverage / _count; + _displayText.text += Environment.NewLine + "Average Per Transform: " + $"{NetworkTraficStatistics.FormatBytesToLargest(perTransformAverage)}/s"; + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs.meta b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs.meta new file mode 100644 index 0000000..39c5cde --- /dev/null +++ b/Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3e21cdbd259430f4ea77e1f3a6c88fc4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Benchmarks/NetworkTransform/Scripts/PrefabSpawner.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback.meta b/Assets/FishNet/Demos/ColliderRollback.meta new file mode 100644 index 0000000..c97f882 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4a241a573ddd2841a4f3eb890a36bf2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Audio.meta b/Assets/FishNet/Demos/ColliderRollback/Audio.meta new file mode 100644 index 0000000..e2ae81e --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Audio.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 055912d614133a949a9ea99034287d3f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Audio/Glock.wav b/Assets/FishNet/Demos/ColliderRollback/Audio/Glock.wav new file mode 100644 index 0000000000000000000000000000000000000000..e603e10728a201a69f4050eae2deb1790081d2a1 GIT binary patch literal 122720 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: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 2759061792589502182} + - {fileID: 1348621277} + - {fileID: 1348621278} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 19 + k__BackingField: 0 + k__BackingField: 7473726319608011331 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 2, z: -2} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!114 &2759061792589502182 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4117607941181585914} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9d3558aad46c24549bea48d0e3938264, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 8475222101369129519} + _networkObjectCache: {fileID: 8475222101369129519} + _hitboxLayer: + serializedVersion: 2 + m_Bits: 32 + _audio: {fileID: 8300000, guid: 0330762d2b3c8d641bfe11ad89b7e196, type: 3} + _muzzleFlashPrefab: {fileID: 39148481766341303, guid: 4385a793e032d634bb912f84a23d6db1, type: 3} +--- !u!114 &1348621277 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4117607941181585914} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e6d656f377f37164d8d7431aa4e43cdb, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 8475222101369129519} + _networkObjectCache: {fileID: 8475222101369129519} +--- !u!114 &1348621278 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4117607941181585914} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c718ab30626bbd648952910f74780a06, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 2 + _addedNetworkObject: {fileID: 8475222101369129519} + _networkObjectCache: {fileID: 8475222101369129519} + _moveRate: 3 +--- !u!143 &5081159371976248031 +CharacterController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4117607941181585914} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Height: 1.85 + 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.925, z: 0} diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab.meta b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab.meta new file mode 100644 index 0000000..a7a4cfd --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 8cf33e8e99a9b0c4c8f29ff725650de6 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Prefabs/Player.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization.meta b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization.meta new file mode 100644 index 0000000..b27609c --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0f2b8542dea4ed449a71c582716ed888 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab new file mode 100644 index 0000000..9bf45ae --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab @@ -0,0 +1,95 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &5088285619167334560 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5088285619167334572} + - component: {fileID: 5088285619167334563} + - component: {fileID: 5088285619167334562} + - component: {fileID: 7203670863068453979} + m_Layer: 0 + m_Name: ClientPosition + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5088285619167334572 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5088285619167334560} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 2.04, y: 1.37, z: -3.993} + m_LocalScale: {x: 1, y: 1.85, z: 0.1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5088285619167334563 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5088285619167334560} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &5088285619167334562 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5088285619167334560} + 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: 997fc4a5ccc62814697fc91eb139e535, 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!114 &7203670863068453979 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5088285619167334560} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3f244f524a030c6459722c6594b1e9bd, type: 3} + m_Name: + m_EditorClassIdentifier: + _delay: 3 diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab.meta b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab.meta new file mode 100644 index 0000000..a5dc7fb --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: ad8f5b5504b971d42886f4f6fd087d90 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ClientPosition.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab new file mode 100644 index 0000000..4d95c5a --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab @@ -0,0 +1,95 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7134941405922954312 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2532797443735268486} + - component: {fileID: 5847025287746828581} + - component: {fileID: 2682424605327152458} + - component: {fileID: 6822465367542887163} + m_Layer: 0 + m_Name: ServerPosition + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2532797443735268486 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7134941405922954312} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 3.85, y: 1.37, z: -3.993} + m_LocalScale: {x: 1, y: 1.85, z: 0.1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &5847025287746828581 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7134941405922954312} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &2682424605327152458 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7134941405922954312} + 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: 14cdcc3d5d8692d469297ede7ced7ab8, 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!114 &6822465367542887163 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7134941405922954312} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3f244f524a030c6459722c6594b1e9bd, type: 3} + m_Name: + m_EditorClassIdentifier: + _delay: 3 diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab.meta b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab.meta new file mode 100644 index 0000000..7905d34 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 85cfddb33a414594490116dc8e2a91f4 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/ServerPosition.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab new file mode 100644 index 0000000..e3b3a19 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab @@ -0,0 +1,282 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &4295747174325081366 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 8244491120346562302} + - component: {fileID: 5312765372148467592} + - component: {fileID: 2637820444415577480} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &8244491120346562302 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4295747174325081366} + 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: 6334056160868253460} + 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 &5312765372148467592 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4295747174325081366} + m_CullTransparentMesh: 0 +--- !u!114 &2637820444415577480 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4295747174325081366} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, 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_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 1 + m_MinSize: 14 + m_MaxSize: 36 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!1 &6334056159345675329 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6334056159345675354} + - component: {fileID: 6334056159345675333} + - component: {fileID: 6334056159345675332} + - component: {fileID: 6334056159345675335} + - component: {fileID: 6334056159345675334} + - component: {fileID: 1319350220} + m_Layer: 5 + m_Name: TextCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6334056159345675354 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056159345675329} + 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: 6334056160868253460} + 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!223 &6334056159345675333 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056159345675329} + 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: 25 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!114 &6334056159345675332 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056159345675329} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!114 &6334056159345675335 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056159345675329} + 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 &6334056159345675334 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056159345675329} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d0dc71af4e9bb144195230171a19cca9, type: 3} + m_Name: + m_EditorClassIdentifier: + _text: {fileID: 2637820444415577480} +--- !u!114 &1319350220 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056159345675329} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3f244f524a030c6459722c6594b1e9bd, type: 3} + m_Name: + m_EditorClassIdentifier: + _delay: 3 +--- !u!1 &6334056160868253463 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6334056160868253460} + - component: {fileID: 6334056160868253482} + - component: {fileID: 6334056160868253461} + m_Layer: 5 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6334056160868253460 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056160868253463} + 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: 8244491120346562302} + m_Father: {fileID: 6334056159345675354} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -30.1} + m_SizeDelta: {x: 0, y: 60.16568} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &6334056160868253482 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056160868253463} + m_CullTransparentMesh: 0 +--- !u!114 &6334056160868253461 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6334056160868253463} + 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, g: 0, b: 0, a: 0.49019608} + m_RaycastTarget: 1 + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 0} + 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/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab.meta b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab.meta new file mode 100644 index 0000000..754e69b --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: a7bbd13c7452746409efbaeae6a0d8e3 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Prefabs/Rollback Visualization/TextCanvas.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/ReadMe.txt b/Assets/FishNet/Demos/ColliderRollback/ReadMe.txt new file mode 100644 index 0000000..d4cebca --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/ReadMe.txt @@ -0,0 +1,12 @@ +This demo requires Fish-Networking Pro. Use of collider rollback for raycasts is displayed in this demo. + +Setup: + * Start two editors or builds. + * Press play on each. + * Run one as server, and the other as client. + +Notes: + * Red shows where the object was rolled back to on the server. + * Green shows where the object actually was on server. + + * Rollback indicators in demo will not be correct when firing as clientHost. diff --git a/Assets/FishNet/Demos/ColliderRollback/ReadMe.txt.meta b/Assets/FishNet/Demos/ColliderRollback/ReadMe.txt.meta new file mode 100644 index 0000000..2df1a6c --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/ReadMe.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 683b83b178f491a479b732df31da7ade +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/ReadMe.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scenes.meta b/Assets/FishNet/Demos/ColliderRollback/Scenes.meta new file mode 100644 index 0000000..99cbb3b --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 79a89eaf9d5b7364e9a9f0e6ae485f84 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity b/Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity new file mode 100644 index 0000000..cd4cf36 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity @@ -0,0 +1,2446 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + 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_LightingSettings: {fileID: 1382340864} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &78153333 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 78153335} + - component: {fileID: 78153334} + m_Layer: 0 + m_Name: HeadHitbox + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &78153334 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 78153333} + 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!4 &78153335 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 78153333} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1289693644} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &110068715 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 110068720} + - component: {fileID: 110068719} + - component: {fileID: 110068718} + - component: {fileID: 110068716} + - component: {fileID: 110068721} + m_Layer: 0 + m_Name: EnemySetup + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!114 &110068716 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 110068715} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01a271dd97d875347b0cea860df29a9d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 110068721} + _networkObjectCache: {fileID: 110068721} + _boundingBox: 0 + _physicsType: 1 + _boundingBoxSize: {x: 3, y: 3, z: 3} + _colliderParents: + - {fileID: 1625859525} + - {fileID: 78153333} +--- !u!23 &110068718 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 110068715} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 4c62c607906e6e1499102239be9499be, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &110068719 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 110068715} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &110068720 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 110068715} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -4.45, y: 1.5, z: 7.95} + m_LocalScale: {x: 1, y: 1.85, z: 0.1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1625859526} + - {fileID: 1289693644} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &110068721 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 110068715} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1002110711 + SerializedTransformProperties: + Position: {x: -4.45, y: 1.5, z: 7.95} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &293940165 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 293940166} + - component: {fileID: 293940169} + - component: {fileID: 293940168} + - component: {fileID: 293940167} + m_Layer: 0 + m_Name: BackWall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &293940166 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 293940165} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 3, z: -12} + m_LocalScale: {x: 25, y: 5, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1760695700} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &293940167 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 293940165} + 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 &293940168 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 293940165} + m_Enabled: 0 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &293940169 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 293940165} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &315136619 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 315136621} + - component: {fileID: 315136620} + 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 &315136620 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 315136619} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.90961653, b: 0.745283, 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 + 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: 2 + 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_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &315136621 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 315136619} + m_LocalRotation: {x: 0.29130033, y: -0.654909, z: 0.30399895, w: 0.62755316} + m_LocalPosition: {x: -5.9194202, y: 8.696499, z: 15.588365} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1845740120} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 49.800003, y: -92.44401, z: 0} +--- !u!1 &333123234 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 333123235} + - component: {fileID: 333123238} + - component: {fileID: 333123237} + - component: {fileID: 333123236} + m_Layer: 0 + m_Name: FrontWall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &333123235 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 333123234} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 3, z: 12} + m_LocalScale: {x: 25, y: 5, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1760695700} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &333123236 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 333123234} + 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 &333123237 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 333123234} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 14d23cd2d8240244baa6026ca98df0bf, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &333123238 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 333123234} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &374579912 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 374579917} + - component: {fileID: 374579916} + - component: {fileID: 374579915} + - component: {fileID: 374579922} + - component: {fileID: 374579919} + - component: {fileID: 374579913} + - component: {fileID: 374579921} + - component: {fileID: 374579914} + m_Layer: 0 + m_Name: MovingEnemyDemo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &374579913 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 088014ab71fc022458b82ed8c9792b1b, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 374579922} + _networkObjectCache: {fileID: 374579922} + MoveRate: 3 + MoveDistance: 5 +--- !u!114 &374579914 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 3 + _addedNetworkObject: {fileID: 374579922} + _networkObjectCache: {fileID: 374579922} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 0 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 0 + _sendToOwner: 0 + _interval: 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!23 &374579915 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 4c62c607906e6e1499102239be9499be, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &374579916 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &374579917 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1.5, z: 8} + m_LocalScale: {x: 1, y: 2, z: 0.1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2042333235} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &374579919 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6c5d5c9fcdb14704d9c64b18dc05a809, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 374579922} + _networkObjectCache: {fileID: 374579922} + _originalPrefab: {fileID: 5088285619167334560, guid: ad8f5b5504b971d42886f4f6fd087d90, type: 3} + _rollbackPrefab: {fileID: 7134941405922954312, guid: 85cfddb33a414594490116dc8e2a91f4, type: 3} + _textCanvasPrefab: {fileID: 6334056159345675334, guid: a7bbd13c7452746409efbaeae6a0d8e3, type: 3} +--- !u!114 &374579921 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 01a271dd97d875347b0cea860df29a9d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 2 + _addedNetworkObject: {fileID: 374579922} + _networkObjectCache: {fileID: 374579922} + _boundingBox: 0 + _physicsType: 1 + _boundingBoxSize: {x: 3, y: 3, z: 3} + _colliderParents: + - {fileID: 2042333234} +--- !u!114 &374579922 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 374579912} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1420819439 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &485115532 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 485115533} + m_Layer: 0 + m_Name: Muzzle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &485115533 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 485115532} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.014, z: 0.0242} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5670937365706158997} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &557728923 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 557728924} + - component: {fileID: 557728926} + - component: {fileID: 557728925} + m_Layer: 0 + m_Name: Displays + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &557728924 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 557728923} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1016939666208092860} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &557728925 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 557728923} + 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: 3 + _secondsAveraged: 1 + _showOutgoing: 1 + _showIncoming: 1 +--- !u!114 &557728926 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 557728923} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f9b6b565cd9533c4ebc18003f0fc18a2, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 0, g: 0, b: 0, a: 1} + _placement: 2 + _hideTickRate: 1 +--- !u!1 &1136385240 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1136385243} + - component: {fileID: 1136385244} + - component: {fileID: 1136385242} + - component: {fileID: 1136385241} + m_Layer: 0 + m_Name: Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1136385241 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136385240} + m_Enabled: 1 +--- !u!20 &1136385242 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136385240} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.63492346, g: 0.70660126, b: 0.8207547, 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: 0 + m_AllowMSAA: 0 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 1 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &1136385243 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136385240} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 2, z: -2} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5670937365706158997} + m_Father: {fileID: 1845740120} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1136385244 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1136385240} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe0209d73083bf744ba4925f8cad7144, type: 3} + m_Name: + m_EditorClassIdentifier: + MuzzleFlash: {fileID: 485115533} +--- !u!1 &1289693643 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1289693644} + - component: {fileID: 1289693646} + - component: {fileID: 1289693645} + m_Layer: 0 + m_Name: Head + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1289693644 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1289693643} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.672, z: 0} + m_LocalScale: {x: 0.35, y: 0.35, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 78153335} + m_Father: {fileID: 110068720} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1289693645 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1289693643} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 4c62c607906e6e1499102239be9499be, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1289693646 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1289693643} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!850595691 &1382340864 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Settings.lighting + serializedVersion: 4 + m_GIWorkflowMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_LightmapCompression: 3 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentMIS: 0 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_PVRTiledBaking: 0 +--- !u!1 &1469142509 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1469142510} + - component: {fileID: 1469142513} + - component: {fileID: 1469142512} + - component: {fileID: 1469142511} + m_Layer: 0 + m_Name: LeftWall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1469142510 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1469142509} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -12, y: 3, z: 0} + m_LocalScale: {x: 1, y: 5, z: 25} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1760695700} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1469142511 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1469142509} + 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 &1469142512 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1469142509} + m_Enabled: 0 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1469142513 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1469142509} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1499550446 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1499550447} + - component: {fileID: 1499550450} + - component: {fileID: 1499550449} + - component: {fileID: 1499550448} + m_Layer: 0 + m_Name: Floor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1499550447 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1499550446} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 25, y: 1, z: 25} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1760695700} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1499550448 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1499550446} + 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 &1499550449 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1499550446} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 3d329b71ccd856144ab8753e70f69e4e, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1499550450 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1499550446} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1508050967 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1508050968} + - component: {fileID: 1508050970} + - component: {fileID: 1508050969} + m_Layer: 5 + m_Name: Crosshairs (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1508050968 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1508050967} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1885986765} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 130, y: 130} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1508050969 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1508050967} + 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, g: 0, b: 0, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: dd2a76027478bf540b7194d78248edf8, 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 &1508050970 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1508050967} + m_CullTransparentMesh: 0 +--- !u!1 &1625859525 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1625859526} + - component: {fileID: 1625859527} + m_Layer: 0 + m_Name: BodyHitbox + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1625859526 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1625859525} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 110068720} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1625859527 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1625859525} + 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!1 &1670963065 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1670963066} + - component: {fileID: 1670963069} + - component: {fileID: 1670963068} + - component: {fileID: 1670963067} + m_Layer: 0 + m_Name: RightWall + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1670963066 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1670963065} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 12, y: 3, z: 0} + m_LocalScale: {x: 1, y: 5, z: 25} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1760695700} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1670963067 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1670963065} + 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 &1670963068 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1670963065} + m_Enabled: 0 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1670963069 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1670963065} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1697504689 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1697504690} + - component: {fileID: 1697504692} + - component: {fileID: 1697504691} + m_Layer: 5 + m_Name: Crosshairs + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1697504690 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1697504689} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1885986765} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1697504691 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1697504689} + 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_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: dd2a76027478bf540b7194d78248edf8, 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 &1697504692 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1697504689} + m_CullTransparentMesh: 0 +--- !u!1001 &1758153306 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1016939666208092860} + 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: 4393252310969058994, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: _autoStartType + value: 2 + 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 &1758153307 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 1758153306} + m_PrefabAsset: {fileID: 0} +--- !u!1 &1760695699 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1760695700} + m_Layer: 0 + m_Name: WallsAndFloor + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1760695700 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1760695699} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1499550447} + - {fileID: 333123235} + - {fileID: 293940166} + - {fileID: 1469142510} + - {fileID: 1670963066} + m_Father: {fileID: 1845740120} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1845740119 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1845740120} + m_Layer: 0 + m_Name: Scene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1845740120 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1845740119} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1136385243} + - {fileID: 1898334455} + - {fileID: 1885986765} + - {fileID: 315136621} + - {fileID: 1760695700} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1885986761 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1885986765} + - component: {fileID: 1885986764} + - component: {fileID: 1885986763} + - component: {fileID: 1885986762} + m_Layer: 5 + m_Name: CrosshairsCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1885986762 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1885986761} + 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 &1885986763 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1885986761} + 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 + m_PresetInfoIsWorld: 0 +--- !u!223 &1885986764 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1885986761} + 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_VertexColorAlwaysGammaSpace: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1885986765 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1885986761} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1508050968} + - {fileID: 1697504690} + m_Father: {fileID: 1845740120} + m_RootOrder: 2 + 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 &1898334452 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1898334455} + - component: {fileID: 1898334454} + - component: {fileID: 1898334453} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1898334453 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1898334452} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1898334454 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1898334452} + 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 &1898334455 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1898334452} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: -1.3194205, y: 1.8064992, z: 15.788364} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1845740120} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &2042333234 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2042333235} + - component: {fileID: 2042333236} + m_Layer: 5 + m_Name: Hitbox + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2042333235 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042333234} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 374579917} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2042333236 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2042333234} + 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!114 &1016939666208092849 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !u!4 &1016939666208092860 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1758153307} + - {fileID: 557728924} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1016939666208092862 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1016939666208092860} + - component: {fileID: 1016939666208092863} + - component: {fileID: 1016939666208092849} + - component: {fileID: 1016939666208092864} + - component: {fileID: 1016939666208092865} + - component: {fileID: 1016939666208092866} + - component: {fileID: 1016939666208092867} + - component: {fileID: 1016939666208092868} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1016939666208092863 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !u!114 &1016939666208092864 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b185516acd802904383e2a5f1a666750, type: 3} + m_Name: + m_EditorClassIdentifier: + _boundingBoxLayer: + serializedVersion: 2 + m_Bits: 0 + _maximumRollbackTime: 1 + Interpolation: 2 +--- !u!114 &1016939666208092865 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + 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 + _physicsMode: 1 +--- !u!114 &1016939666208092866 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _stopSocketsOnThread: 0 + _dontRoute: 0 + _unreliableMtu: 1023 + _ipv4BindAddress: + _enableIpv6: 1 + _ipv6BindAddress: + _port: 7770 + _maximumClients: 4095 + _clientAddress: localhost +--- !u!114 &1016939666208092867 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + 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 &1016939666208092868 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1016939666208092862} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 34e4a322dca349547989b14021da4e23, type: 3} + m_Name: + m_EditorClassIdentifier: + Transport: {fileID: 1016939666208092866} + _intermediateLayer: {fileID: 0} + _latencySimulator: + _enabled: 0 + _simulateHost: 1 + _latency: 40 + _outOfOrder: 0 + _packetLoss: 0 +--- !u!23 &5670937365703733617 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666481} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 1aa329960fc39aa4490b3a9e7e965fa8, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &5670937365703733619 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666483} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: e86c377702aa20748ac223d70f899dc1, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &5670937365704864689 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666481} + m_Mesh: {fileID: 4300000, guid: c1232b0a1b78ddf44bd877453fb7a84c, type: 3} +--- !u!33 &5670937365704864691 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666483} + m_Mesh: {fileID: 4300002, guid: c1232b0a1b78ddf44bd877453fb7a84c, type: 3} +--- !u!1 &5670937365705666481 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5670937365706158993} + - component: {fileID: 5670937365704864689} + - component: {fileID: 5670937365703733617} + m_Layer: 0 + m_Name: Mesh1_Group2_Group1_Model + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &5670937365705666483 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5670937365706158995} + - component: {fileID: 5670937365704864691} + - component: {fileID: 5670937365703733619} + m_Layer: 0 + m_Name: Mesh2_Group3_Group1_Model + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &5670937365705666485 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 5670937365706158997} + m_Layer: 0 + m_Name: Glock + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &5670937365706158993 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666481} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5670937365706158997} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &5670937365706158995 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666483} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 5670937365706158997} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &5670937365706158997 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5670937365705666485} + m_LocalRotation: {x: 0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0.25, y: -0.25, z: 0.55} + m_LocalScale: {x: 5, y: 5, z: 6} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 5670937365706158993} + - {fileID: 5670937365706158995} + - {fileID: 485115533} + m_Father: {fileID: 1136385243} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity.meta b/Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity.meta new file mode 100644 index 0000000..849bc46 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 9e30e3df1ec43184daee875457d49364 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scenes/ColliderRollbackDemo.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts.meta new file mode 100644 index 0000000..4ed8dd8 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: af8606f905937fc419c0dfcad970cd1d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs new file mode 100644 index 0000000..7a5f284 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs @@ -0,0 +1,17 @@ +using UnityEngine; + +namespace FishNet.Example.ColliderRollbacks +{ + public class DestroyAfterDelay : MonoBehaviour + { + [SerializeField] + private float _delay = 1f; + + private void Awake() + { + Destroy(gameObject, _delay); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs.meta new file mode 100644 index 0000000..d712ccb --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3f244f524a030c6459722c6594b1e9bd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/DestroyAfterDelay.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player.meta new file mode 100644 index 0000000..4c32959 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a9331e21bfaa0a4aaac4ffe7d8d644c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs new file mode 100644 index 0000000..049a179 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs @@ -0,0 +1,67 @@ +using FishNet.Object; +using UnityEngine; + + +namespace FishNet.Example.ColliderRollbacks +{ + + /// + /// DEMO. CODE IS NOT OPTIMIZED. + /// Aims the camera. + /// + public class Aim : NetworkBehaviour + { + + public PlayerCamera PlayerCamera { get; private set; } + private readonly Vector3 _offset = new(0f, 1.65f, 0f); + + public override void OnStartClient() + { + if (base.IsOwner) + PlayerCamera = Camera.main.transform.GetComponent(); + } + + private void Update() + { + if (!base.IsOwner || PlayerCamera == null) + return; + + Cursor.lockState = CursorLockMode.Locked; + Cursor.visible = false; + MoveAim(); + MoveCamera(); + } + + /// + /// Aims camera. + /// + private void MoveAim() + { + float speed = 2f; + //Yaw. + transform.Rotate(new(0f, Input.GetAxis("Mouse X") * speed, 0f)); + //Pitch. + float pitch = PlayerCamera.transform.eulerAngles.x - (Input.GetAxis("Mouse Y") * speed); + /* If not signed on X then make it + * signed for easy clamping. */ + if (pitch > 180f) + pitch -= 360f; + pitch = Mathf.Clamp(pitch, -89f, 89f); + + PlayerCamera.transform.eulerAngles = new(pitch, transform.eulerAngles.y, transform.eulerAngles.z); + } + + /// + /// Moves camera. + /// + private void MoveCamera() + { + PlayerCamera.transform.position = transform.position + _offset; + PlayerCamera.transform.rotation = Quaternion.Euler(PlayerCamera.transform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z); + } + + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs.meta new file mode 100644 index 0000000..5ff51ec --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e6d656f377f37164d8d7431aa4e43cdb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Aim.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs new file mode 100644 index 0000000..6fcb21e --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs @@ -0,0 +1,22 @@ +using FishNet.Component.ColliderRollback; +using FishNet.Managing.Timing; +using FishNet.Object; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + + +namespace FishNet.Example.ColliderRollbacks +{ + + /// + /// DEMO. CODE IS NOT OPTIMIZED. + /// Fires at objects. + /// + public class Fire : NetworkBehaviour + { + + + } +} + diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs.meta new file mode 100644 index 0000000..49f2734 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9d3558aad46c24549bea48d0e3938264 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Player/Fire.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs new file mode 100644 index 0000000..53772b5 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs @@ -0,0 +1,19 @@ +using UnityEngine; + +namespace FishNet.Example.ColliderRollbacks +{ + + /// + /// DEMO. CODE IS NOT OPTIMIZED. + /// Doesn't do much... + /// + public class PlayerCamera : MonoBehaviour + { + /// + /// MuzzleFlash on the weapon. + /// + public Transform MuzzleFlash; + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs.meta new file mode 100644 index 0000000..2c17a59 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fe0209d73083bf744ba4925f8cad7144 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerCamera.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs new file mode 100644 index 0000000..e7658d3 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs @@ -0,0 +1,50 @@ +using FishNet.Object; +using UnityEngine; + + +namespace FishNet.Example.ColliderRollbacks +{ + + + /// + /// DEMO. CODE IS NOT OPTIMIZED. + /// Moves the player around. + /// + public class PlayerMotor : NetworkBehaviour + { + [SerializeField] + private float _moveRate = 3f; + + private CharacterController _characterController; + + public override void OnStartClient() + { + if (base.IsOwner) + _characterController = GetComponent(); + } + + + private void Update() + { + if (base.IsOwner) + { + Move(); + } + } + + private void Move() + { + if (_characterController == null) + return; + + Vector3 gravity = new(0f, -10f, 0f); + Vector3 inputs = transform.TransformDirection( + new(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")) + ); + + _characterController.Move((gravity + inputs) * _moveRate * Time.deltaTime); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs.meta new file mode 100644 index 0000000..e3a5c21 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c718ab30626bbd648952910f74780a06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Player/PlayerMotor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization.meta new file mode 100644 index 0000000..b7151b8 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9b80990f4a80a944c9394e21c9c2187c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs new file mode 100644 index 0000000..35a9fc3 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs @@ -0,0 +1,65 @@ +using FishNet.Connection; +using FishNet.Object; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + + +namespace FishNet.Example.ColliderRollbacks +{ + /// + /// DEMO. CODE IS NOT OPTIMIZED. + /// Shows where an object was when client hit it, and where it was after server rolled it back. + /// + + public class RollbackVisualizer : NetworkBehaviour + { + [SerializeField] + private GameObject _originalPrefab; + [SerializeField] + private GameObject _rollbackPrefab; + [SerializeField] + private TextCanvas _textCanvasPrefab; + + /// + /// Shows difference between where object was when client shot it, and where it was after rollback. + /// + /// + /// + [Server] + public void ShowDifference(NetworkObject clientObject, Vector3 original, Vector3 rolledBack) + { + TargetShowDifference(clientObject.Owner, original, rolledBack); + } + + + [TargetRpc] + private void TargetShowDifference(NetworkConnection conn, Vector3 original, Vector3 rollback) + { + Instantiate(_originalPrefab, original, transform.rotation); + Instantiate(_rollbackPrefab, rollback, transform.rotation); + + float difference = Vector3.Distance(original, rollback); + if (difference <= 0.00001f) + difference = 0f; + + _differences.Add(difference); + if (_differences.Count > 20) + _differences.RemoveAt(0); + float averageDifference = (_differences.Sum() / _differences.Count); + + string accuracyText = (base.IsServerStarted) ? + $"Accuracy will not show properly when as clientHost.{Environment.NewLine}Use a separate client and server for testing." + : $"Difference {difference.ToString("0.000")}m. Average difference {averageDifference.ToString("0.000")}m."; + + TextCanvas tc = Instantiate(_textCanvasPrefab); + tc.SetText(accuracyText); + Debug.Log(accuracyText); + } + + private List _differences = new(); + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs.meta new file mode 100644 index 0000000..1376279 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6c5d5c9fcdb14704d9c64b18dc05a809 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/RollbackVisualizer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs new file mode 100644 index 0000000..1260f90 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs @@ -0,0 +1,29 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace FishNet.Example.ColliderRollbacks +{ + public class TextCanvas : MonoBehaviour + { + [SerializeField] + private Text _text; + + private static TextCanvas _instance; + + private void Awake() + { + if (_instance != null) + Destroy(_instance.gameObject); + + _instance = this; + } + + public void SetText(string text) + { + _text.text = text; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs.meta new file mode 100644 index 0000000..f1fd0e0 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d0dc71af4e9bb144195230171a19cca9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Rollback Visualization/TextCanvas.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs b/Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs new file mode 100644 index 0000000..f2020f5 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs @@ -0,0 +1,35 @@ +using FishNet.Object; +using UnityEngine; + + +namespace FishNet.Example.ColliderRollbacks +{ + + public class Strafe : NetworkBehaviour + { + public float MoveRate = 2f; + public float MoveDistance = 3f; + + private bool _movingRight = true; + private float _startX; + public override void OnStartServer() + { + + _startX = transform.position.x; + } + + private void Update() + { + if (base.IsServerStarted) + { + float x = (_movingRight) ? _startX + MoveDistance : _startX - MoveDistance; + Vector3 goal = new(x, transform.position.y, transform.position.z); + transform.position = Vector3.MoveTowards(transform.position, goal, MoveRate * Time.deltaTime); + if (transform.position == goal) + _movingRight = !_movingRight; + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs.meta b/Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs.meta new file mode 100644 index 0000000..d1a2ed2 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 088014ab71fc022458b82ed8c9792b1b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Scripts/Strafe.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Textures.meta b/Assets/FishNet/Demos/ColliderRollback/Textures.meta new file mode 100644 index 0000000..3655e19 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7782cde83489d9a4ca40e344c67aa02a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png b/Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png new file mode 100644 index 0000000000000000000000000000000000000000..c15bd338b2057f7e39496e76d985e09e3c2bad86 GIT binary patch literal 2673 zcmeHJO>7%Q6kZdQv_X-IBB9bsuxvSi#CT_a?OozDi5(hCIE2(9DdN!a&aAzPy|e6Y z;varhaVdyct(dxIsLzNhR1d&wuYMhgRX)lCE zFo5-=ewrX*a*G7AlEz6$n}(&*?xu~#z&eU&*T74k@3z2Exg9WhkwttpblB_~^W!6% z&Zsk38?1))Zlu(aS)Db&ECOQ6z*SJ=F^_6|hd2h)gJ=RfA2Jg2u*`XVvrr40D547B z;!{hWPkA$XI$_jbzzQU0IVq_c!WL50vZC3#ZtEF@bsOWJX$f>dNlflA*2*|*>xQjo z|7Ogo=eGA^S}NH^KZ=R(uwo%60jad-QJYdmtct55lCdo0WPuBzCP7%SbVJuGgjRaI zV10o*t4aEj^*z~A?f}LhLpD>H;wq+rFjWWwnJS1Vn4yYmxmqP-nX0CDqXe`ULJxKh zsc%J1SW!T9O~N#+J1A=c4mETORT!i!tLcmy3d>NcN+NY^mxm1kI`JB$%H+VWO5$H^ z^1RN&5~q-JZR>b3pI->M>(yZ*T0Ax*7H9LOVwomV(@G+HpaBHuya;OC?r)^y?A3m+ z?ulJ>+QdmVMNV=OH83jmhrMmmytjaQu+16+&|Os0GmE$zH%Z8*t5A}=s!-mEd_>mn zLS1)Fs^bhYSf-LFL`7MZsK~I0Lo$wKFaxstKT_{wJ2m208Jw`Pbnn6JP43$U^Un=< zFLl^H7&$pudkT~96km64%OMPf><+=no^%IS3cL(1CFVn(Lb!5nJ~8sKAPk)-7N*Os zvl~BtGj?j~z|@O3TIWYLENO#JBI>zA${|!QZqI)w%Y?t=m`DZ+-vXhv(mT zedUoiud?5Tuk8l}a=-oi3_x}}!zUj;EWERS=l1?a`w#i%>F<7+d};EnuO7Sj(fGO4 z)EQydcJ8MG?$FyfwN3kc{g+hs$|qN!U3&cR&j(Fm-}wFZ-(}#np^f!NM`kWxemVIl NEY8jsKASmt>QAUpvXTG* literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png.meta b/Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png.meta new file mode 100644 index 0000000..8ab3bf8 --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png.meta @@ -0,0 +1,111 @@ +fileFormatVersion: 2 +guid: dd2a76027478bf540b7194d78248edf8 +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: -100 + wrapU: 1 + wrapV: 1 + wrapW: -1 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Textures/Crosshair.png + uploadId: 762203 diff --git a/Assets/FishNet/Demos/ColliderRollback/Textures/Particles.meta b/Assets/FishNet/Demos/ColliderRollback/Textures/Particles.meta new file mode 100644 index 0000000..ebf063e --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Textures/Particles.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eb2e86322ed960149a79e8ed8199b52b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png b/Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..db571f56c8c108645b3bf034b2179f278b2d2ee8 GIT binary patch literal 6439 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 literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png.meta b/Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png.meta new file mode 100644 index 0000000..0ed756b --- /dev/null +++ b/Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 155f67fd8750f8e4e83c8efb1731f3a0 +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 7 + 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: 2 + mipBias: -100 + wrapU: -1 + wrapV: -1 + wrapW: -1 + 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 + platformSettings: + - serializedVersion: 2 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + - serializedVersion: 2 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + vertices: [] + indices: + edges: [] + weights: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/ColliderRollback/Textures/Particles/circle.png + uploadId: 762203 diff --git a/Assets/FishNet/Demos/CustomSyncType.meta b/Assets/FishNet/Demos/CustomSyncType.meta new file mode 100644 index 0000000..e706033 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6860196879fb52a408948f34a9ad992a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync.meta b/Assets/FishNet/Demos/CustomSyncType/Component State Sync.meta new file mode 100644 index 0000000..45ec865 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ae35102a88f9145439386a0fcf0a72e5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs new file mode 100644 index 0000000..8a62bed --- /dev/null +++ b/Assets/FishNet/Demos/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/Demos/CustomSyncType/Component State Sync/AMonoScript.cs.meta b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs.meta new file mode 100644 index 0000000..8832447 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a5821b28e5b90ef44b4910a640482c15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/CustomSyncType/Component State Sync/AMonoScript.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs new file mode 100644 index 0000000..25624e7 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs @@ -0,0 +1,154 @@ +using FishNet.Documenting; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Runtime.CompilerServices; +using FishNet.Managing; +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) + base.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.IsInitialized) + return; + + if (base.NetworkManager != null && !base.NetworkBehaviour.IsServerStarted) + { + 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. + protected internal override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.WriteBoolean(Component.enabled); + } + + /// + /// Writes all values. + /// + protected internal 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] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool _, out bool canModifyValues); + + bool prevValue = GetState(); + bool nextValue = reader.ReadBoolean(); + + /* 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. */ + if (canModifyValues) + Component.enabled = nextValue; + + if (newChangeId) + OnChange?.Invoke(Component, prevValue, nextValue, asServer); + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => null; + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta new file mode 100644 index 0000000..e597db9 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ce7cdae4a8f3d914fa9141b4bd760faa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentStateSync.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs new file mode 100644 index 0000000..d68ca90 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs @@ -0,0 +1,53 @@ +using FishNet.Object; +using FishNet.Object.Synchronizing; +using FishNet.Serializing; +using UnityEngine; + +namespace FishNet.Example.ComponentStateSync +{ + public static class AMSSerializer + { + public static void WriteAMS(this Writer w, AMonoScript value) + { + } + public static AMonoScript ReadAMS(this Reader r) + { + return default; + } + } + public class ComponentSyncStateBehaviour : NetworkBehaviour + { + /// + /// Using my custom SyncType for Structy. + /// + + private readonly ComponentStateSync _syncScript = new(); + + 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.IsServerStarted && Time.frameCount % 200 == 0) + _syncScript.Enabled = !_syncScript.Enabled; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta new file mode 100644 index 0000000..6c7a6ec --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a7327b94eff9c7d4aa876aa54ca6b439 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/CustomSyncType/Component State Sync/ComponentSyncStateBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync.meta b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync.meta new file mode 100644 index 0000000..358c36f --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 158d38828d286af468fbdb3b594ad9bc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs new file mode 100644 index 0000000..d6c6cc8 --- /dev/null +++ b/Assets/FishNet/Demos/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. + /// + + private readonly StructySync _structy = new(); + + 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.IsServerStarted && 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/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta new file mode 100644 index 0000000..df1510c --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c2cc7cbbeb4170642ac60367303222ef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructSyncBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs new file mode 100644 index 0000000..e14dc73 --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs @@ -0,0 +1,255 @@ +using FishNet.Managing; +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(); + #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(); + /// + /// True if values have changed since initialization. + /// + private bool _valuesChanged; + /// + /// Last value after dirty call. + /// + private Structy _lastDirtied = new(); + #endregion + + protected override void Initialized() + { + base.Initialized(); + _initialValue = Value; + } + + /// + /// Adds an operation and invokes locally. + /// + /// + /// + /// + /// + private void AddOperation(CustomOperation operation, Structy prev, Structy next) + { + if (!base.IsInitialized) + return; + + if (base.NetworkManager != null && !base.NetworkBehaviour.IsServerStarted) + { + base.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(operation, next); + _changed.Add(cd); + OnChange?.Invoke(operation, prev, next, asServer); + } + + /// + /// Writes all changed values. + /// + /// + ///True to set the next time data may sync. + protected internal 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.WriteUInt8Unpacked((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. + /// + /// + protected internal 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.WriteUInt8Unpacked((byte)CustomOperation.Full); + //Write value. + writer.Write(Value); + } + + /// + /// Reads and sets the current values for server or client. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool _, out bool canModifyValues); + + int changes = reader.ReadInt32(); + for (int i = 0; i < changes; i++) + { + CustomOperation operation = (CustomOperation)reader.ReadUInt8Unpacked(); + Structy prev = Value; + Structy next = prev; + + //Full. + if (operation == CustomOperation.Full) + next = reader.Read(); + //Name. + else if (operation == CustomOperation.Name) + next.Name = reader.ReadStringAllocated(); + //Age + else if (operation == CustomOperation.Age) + next.Age = reader.ReadUInt16(); + + if (canModifyValues) + Value = next; + + if (newChangeId) + OnChange?.Invoke(operation, prev, next, asServer); + } + } + + /// + /// 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. + /// + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + _changed.Clear(); + Value = _initialValue; + _valuesChanged = false; + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => typeof(Structy); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs.meta b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs.meta new file mode 100644 index 0000000..a82e97b --- /dev/null +++ b/Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4c0a84e32efc60f4aa471e3a42cf2e0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/CustomSyncType/Custom Struct Sync/StructySync.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/FishNet.Demos.asmdef b/Assets/FishNet/Demos/FishNet.Demos.asmdef new file mode 100644 index 0000000..fcf46f0 --- /dev/null +++ b/Assets/FishNet/Demos/FishNet.Demos.asmdef @@ -0,0 +1,17 @@ +{ + "name": "FishNet.Demos", + "rootNamespace": "", + "references": [ + "GUID:7c88a4a7926ee5145ad2dfa06f454c67", + "GUID:1d82bdf40e2465b44b34adf79595e74c" + ], + "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..8c1e4d2 --- /dev/null +++ b/Assets/FishNet/Demos/FishNet.Demos.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0bb35fc3181999548a4abea731e00e89 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/FishNet.Demos.asmdef + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid.meta b/Assets/FishNet/Demos/HashGrid.meta new file mode 100644 index 0000000..bed0572 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 02c5cb48c15b7f445b5f7947b3caab23 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/HashGrid/Prefabs.meta b/Assets/FishNet/Demos/HashGrid/Prefabs.meta new file mode 100644 index 0000000..3bf7b0f --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 77d19422e209c5843b939e6dcd6b4971 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab new file mode 100644 index 0000000..0079ac9 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab @@ -0,0 +1,234 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2758258074630985 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2058948186138587895} + - component: {fileID: 2529588038898116402} + m_Layer: 0 + m_Name: Sprite + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2058948186138587895 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2758258074630985} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &2529588038898116402 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2758258074630985} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, 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: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0} + m_Color: {r: 1, g: 0.6277604, b: 0, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 2.9, y: 2.22} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !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: 4670340455971777434} + - component: {fileID: 3019520109258855553} + m_Layer: 0 + m_Name: HashGrid_Moving + 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: 3, y: 3, z: 3} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2058948186138587895} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 4670340455971777434} + - {fileID: 3019520109258855553} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 25 + k__BackingField: 0 + k__BackingField: 11406911356865610645 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 3, y: 3, z: 3} + IsValid: 1 +--- !u!114 &4670340455971777434 +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: 10f399a5388d3b3459b7a8476ae13e6a, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 4512293259955182956} + _networkObjectCache: {fileID: 4512293259955182956} + _renderer: {fileID: 2529588038898116402} +--- !u!114 &3019520109258855553 +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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 1 + _teleportThreshold: 0.5 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 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/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab.meta b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab.meta new file mode 100644 index 0000000..45c9f59 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 44611030e61220d42ab7c37ba3c0ea92 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Moving.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab new file mode 100644 index 0000000..070f26e --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab @@ -0,0 +1,188 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2758258074630985 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2058948186138587895} + - component: {fileID: 2529588038898116402} + m_Layer: 0 + m_Name: Sprite + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 4294967295 + m_IsActive: 1 +--- !u!4 &2058948186138587895 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2758258074630985} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 4512293259955182957} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &2529588038898116402 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2758258074630985} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, 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: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 10913, guid: 0000000000000000f000000000000000, type: 0} + m_Color: {r: 1, g: 0.20549601, b: 0, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 2.9, y: 2.22} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 0 +--- !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: 437322326027960749} + m_Layer: 0 + m_Name: HashGrid_Static + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 4294967295 + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2058948186138587895} + 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 437322326027960749} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 38 + k__BackingField: 0 + k__BackingField: 17472515426990886281 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!114 &437322326027960749 +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: 8a6a39c46bf52104ba8efe3100bce3f7, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 4512293259955182956} + _networkObjectCache: {fileID: 4512293259955182956} diff --git a/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab.meta b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab.meta new file mode 100644 index 0000000..6266efb --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0d6d0f48b03b17f49a6340103cd9b9d0 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/Prefabs/HashGrid_Static.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid/ReadMe.txt b/Assets/FishNet/Demos/HashGrid/ReadMe.txt new file mode 100644 index 0000000..abb6636 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/ReadMe.txt @@ -0,0 +1,12 @@ +This demo displays how to setup and use the Hash + +Setup: + * Open scene as server, clientHost, or client only. + * Press Play. + +Notes: + * Demo only works on clientHost. Real usage works with separate client and server, and clientHost. + * Scene view can be used to see objects appear and disappear as the player object moves between grid spots. + + * See NetworkManager > HashGrid component to update hash grid settings. + * Notice that the ObserverManager has GridCondition as a default condition. \ No newline at end of file diff --git a/Assets/FishNet/Demos/HashGrid/ReadMe.txt.meta b/Assets/FishNet/Demos/HashGrid/ReadMe.txt.meta new file mode 100644 index 0000000..bbdd659 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/ReadMe.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 4193a544613faaf4a8dd0a4a215523f3 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/ReadMe.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid/Scenes.meta b/Assets/FishNet/Demos/HashGrid/Scenes.meta new file mode 100644 index 0000000..a80ec58 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f1bf5e92afde20428b1ccf93f8648a0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity b/Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity new file mode 100644 index 0000000..942bfc9 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity @@ -0,0 +1,652 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &51369845 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 51369846} + - component: {fileID: 51369847} + m_Layer: 0 + m_Name: Background + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &51369846 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 51369845} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 5} + m_LocalScale: {x: 50, y: 50, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 442045875} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!212 &51369847 +SpriteRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 51369845} + m_Enabled: 1 + m_CastShadows: 0 + m_ReceiveShadows: 0 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 0 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10754, 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: 0 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_Sprite: {fileID: 21300000, guid: 89cc97401eb588a408315f5fa0148929, type: 3} + m_Color: {r: 0.06933072, g: 0.11994747, b: 0.3584906, a: 1} + m_FlipX: 0 + m_FlipY: 0 + m_DrawMode: 0 + m_Size: {x: 0.16, y: 0.16} + m_AdaptiveModeThreshold: 0.5 + m_SpriteTileMode: 0 + m_WasSpriteAssigned: 1 + m_MaskInteraction: 0 + m_SpriteSortPoint: 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: 15 + _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} + - component: {fileID: 442045878} + m_Layer: 0 + m_Name: --GridSpawner + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 51369846} + m_Father: {fileID: 0} + m_RootOrder: 2 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4227841466 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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: 5f70001444cb8ef49bef71f3b6e3a925, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 442045876} + _networkObjectCache: {fileID: 442045876} + _staticPrefab: {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3} + _movingPrefab: {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3} + _movingCount: 100 + _spacing: 2 +--- !u!114 &442045878 +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: c71fd7f855ec523429999fc4e14a1928, type: 3} + m_Name: + m_EditorClassIdentifier: + _overrideType: 3 + _updateHostVisibility: 1 + _observerConditions: [] +--- !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} + m_Layer: 0 + m_Name: Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 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: 2 + m_BackGroundColor: {r: 0, g: 0, b: 0, 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: 1 + orthographic size: 2.75 + 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: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &1157313956 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408886575219561} + 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: 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!224 &1157313957 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 1157313956} + m_PrefabAsset: {fileID: 0} +--- !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: 44611030e61220d42ab7c37ba3c0ea92, 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1157313957} + m_Father: {fileID: 0} + m_RootOrder: 1 + 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: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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: 7443408886575219556} + - component: {fileID: 370472796} + - component: {fileID: 7443408886575219566} + - component: {fileID: 7443408886575219567} + - component: {fileID: 7443408886575219568} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &7443408886575219566 +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: 0a9fc3aafb02eb74fb571c300f846bf2, type: 3} + m_Name: + m_EditorClassIdentifier: + _gridAxes: 0 + _accuracy: 10 +--- !u!114 &7443408886575219567 +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: 6f48f002b825cbd45a19bd96d90f9edb, type: 3} + m_Name: + m_EditorClassIdentifier: + _stopSocketsOnThread: 0 + _dontRoute: 0 + _unreliableMtu: 1023 + _ipv4BindAddress: + _enableIpv6: 1 + _ipv6BindAddress: + _port: 7112 + _maximumClients: 4095 + _clientAddress: localhost +--- !u!114 &7443408886575219568 +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: + _updateHostVisibility: 1 + _maximumTimedObserversDuration: 2 + _defaultConditions: + - {fileID: 11400000, guid: cc503f7541ebd424c94541e6a767efee, type: 2} diff --git a/Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity.meta b/Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity.meta new file mode 100644 index 0000000..7e290e9 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 99947f0521cb96a4087d4496b8761a23 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/Scenes/HashGrid_Demo.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid/Scripts.meta b/Assets/FishNet/Demos/HashGrid/Scripts.meta new file mode 100644 index 0000000..5b137ee --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c8112cf1a9acc7f47946446bdc61e7a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs b/Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs new file mode 100644 index 0000000..5efdb08 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs @@ -0,0 +1,43 @@ +using FishNet.Object; +using UnityEngine; + + +namespace FishNet.Demo.HashGrid +{ + + public class GridSpawner : NetworkBehaviour + { + [SerializeField] + private NetworkObject _staticPrefab; + [SerializeField] + private NetworkObject _movingPrefab; + [SerializeField] + private int _movingCount = 100; + [SerializeField] + private byte _spacing = 2; + + private float _range => MoveRandomly.Range; + + public override void OnStartServer() + { + + + for (int x = (int)(_range * -1); x < _range; x+= _spacing) + { + for (int y = (int)(_range * -1); y < _range; y++) + { + NetworkObject n = Instantiate(_staticPrefab, new(x, y, transform.position.z), Quaternion.identity); + base.Spawn(n); + } + } + + for (int i = 0; i < _movingCount; i++) + { + NetworkObject n = Instantiate(_movingPrefab, transform.position, transform.rotation); + base.Spawn(n); + } + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs.meta b/Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs.meta new file mode 100644 index 0000000..ede15d1 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5f70001444cb8ef49bef71f3b6e3a925 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/Scripts/GridSpawner.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs b/Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs new file mode 100644 index 0000000..7d13a1a --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs @@ -0,0 +1,86 @@ +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Demo.HashGrid +{ + public class MoveRandomly : NetworkBehaviour + { + //Colors green for client. + [SerializeField] + private Renderer _renderer; + + //How quickly to move over 1s. + private float _moveRate = 0.5f; + //Maximum range for new position. + public const float Range = 25f; + //Position to move towards. + private Vector3 _goal; + //Position at spawn. + private Vector3 _start; + + private void Update() + { + if (!base.IsController) + return; + + transform.position = Vector3.MoveTowards(transform.position, _goal, (_moveRate * Time.deltaTime)); + if (transform.position == _goal) + RandomizeGoal(); + } + + public override void OnStartNetwork() + { + _start = transform.position; + RandomizeGoal(); + } + + public override void OnStartServer() + { + if (!base.Owner.IsValid) + transform.position = (_start + RandomInsideRange()); + } + + public override void OnStartClient() + { + if (base.Owner.IsLocalClient) + { + _renderer.material.color = Color.green; + _moveRate *= 3f; + transform.position -= new Vector3(0f, 0f, 1f); + Camera c = Camera.main; + c.transform.SetParent(transform); + c.transform.localScale = Vector3.one; + c.transform.localPosition = new(0f, 0f, -5f); + } + else + { + _renderer.material.color = Color.gray; + } + } + + public override void OnStopClient() + { + if (base.IsOwner) + { + Camera c = Camera.main; + if (c != null) + { + c.transform.SetParent(null); + c.transform.localScale = Vector3.one; + } + } + } + + private void RandomizeGoal() + { + _goal = _start + RandomInsideRange(); + } + + private Vector3 RandomInsideRange() + { + Vector3 goal = (Random.insideUnitSphere * Range); + goal.z = transform.position.z; + return goal; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs.meta b/Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs.meta new file mode 100644 index 0000000..50daf19 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 10f399a5388d3b3459b7a8476ae13e6a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/Scripts/MoveRandomly.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/HashGrid/Textures.meta b/Assets/FishNet/Demos/HashGrid/Textures.meta new file mode 100644 index 0000000..c2e0d05 --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2632d5734cb07bb419c6b6aa4e4b245d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png b/Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png new file mode 100644 index 0000000000000000000000000000000000000000..4927ef6d4e27d75e08f334ec983e5d06aaf0d549 GIT binary patch literal 1793 zcma)7eM}o=7(b@fiGWL(6a7OkR|lI*@7gOL_R?FWEgKa|O8F`njo0?to^9{CyMum= z;m4R}0~68AWUTQ69h>|iI*bc$ZgZJ1V$@9~E{-URiDdWz$c82$_}&!?tg`sWwf8>1 z-}Ah`=Y5~&x&5|^otfzmq(cytX(=_^!GD_ctX~IyUkO+J2tkT0rrIGotmTA@3!;>p zbJA!e7y>v1=?#$(Mcp5H13s5z(JssBbZVzEM z?^tvOD-!XEVu--7a5#*HwJ67XF^x{A!_+v2<0`;V2~k$0A}Ur;N+A+4%(UR*nUKhE zEG)&OoLr+wA_&0YC1t@-GB_(NW)IE*i%=m7}kf~#?@O08Alc(taG&}s-Ru2Sc#)k(6Ib2Fak8svg}KqgkQL3y|- zk@`RU2-?Plb-aY0NwfNX`1+P{fk3DH{ z3kuc7>nDHdnnq`pg+DOwWc$l64PTp#w06!k?ftyH?y0`o--U&SZ|%mqv){n_o?{Kg z5o6?;BX4z{+B(wxuB+_a;JF8_=$7W-_*A;#P`qdIrJ;t0H$wil54PQC`}vH+;;*No zx<3Nl$L2-`oA)a_4riAAwrRqcXWn2ybMIH4-Edp`1%2y%5A8YLwWDPrJf9IAs&UtD zefz`m1NjPl*QO&eqtkx9wLN2V-@>!AJ$ZNTIXIqW_|A|sPPPNile_?+;w!|qsu4U*(Y*en{2-Q%Szv;tpocW z|GH&HcWU2-p5|diVB)dfco=O-E5f`N-$XA}f^X4Xq0)K*g6__io;0X4o&$^-7ISg+ f`a`b27ZwbVVt(Dc<4=E<^yRXYRG8m4KK;VKrh#JQ literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png.meta b/Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png.meta new file mode 100644 index 0000000..592f1be --- /dev/null +++ b/Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png.meta @@ -0,0 +1,111 @@ +fileFormatVersion: 2 +guid: 89cc97401eb588a408315f5fa0148929 +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: 1 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/HashGrid/Textures/1x1 Pixel.png + uploadId: 762203 diff --git a/Assets/FishNet/Demos/IntermediateLayer.meta b/Assets/FishNet/Demos/IntermediateLayer.meta new file mode 100644 index 0000000..46c84ea --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ea07d809d9bc44b4ba4025cb56b199b3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/IntermediateLayer/Scenes.meta b/Assets/FishNet/Demos/IntermediateLayer/Scenes.meta new file mode 100644 index 0000000..fda9e58 --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1eaf920277ad1b043b6175001766791c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity b/Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity new file mode 100644 index 0000000..e6ec10f --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity @@ -0,0 +1,356 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1001 &1896523400 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1468556956860775637} + 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: 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!224 &1896523401 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 1896523400} + m_PrefabAsset: {fileID: 0} +--- !u!4 &1468556956860775637 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1468556956860775639} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1896523401} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1468556956860775638 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1468556956860775639} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _refreshDefaultPrefabs: 0 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !u!1 &1468556956860775639 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1468556956860775637} + - component: {fileID: 1468556956860775638} + - component: {fileID: 1468556956860775641} + - component: {fileID: 1468556956860775642} + - component: {fileID: 1468556956860775643} + - component: {fileID: 1468556956860775644} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1468556956860775641 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1468556956860775639} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 34e4a322dca349547989b14021da4e23, type: 3} + m_Name: + m_EditorClassIdentifier: + Transport: {fileID: 0} + _intermediateLayer: {fileID: 1468556956860775642} + _latencySimulator: + _enabled: 0 + _simulateHost: 1 + _latency: 0 + _outOfOrder: 0 + _packetLoss: 0 +--- !u!114 &1468556956860775642 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1468556956860775639} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 10c18bbe05944364d9295f5ee411a95f, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &1468556956860775643 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1468556956860775639} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d0962ead4b02a34aae248fccce671ce, type: 3} + m_Name: + m_EditorClassIdentifier: + WriteSceneObjectDetails: 0 + ValidateRpcLengths: 0 + DisableObserversRpcLinks: 0 + DisableTargetRpcLinks: 0 + DisableServerRpcLinks: 0 + DisableReplicateRpcLinks: 0 + DisableReconcileRpcLinks: 0 +--- !u!114 &1468556956860775644 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1468556956860775639} + 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: 5 + _pingInterval: 1 + _physicsMode: 0 diff --git a/Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity.meta b/Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity.meta new file mode 100644 index 0000000..9e4fe5a --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: fa009a2a1952c0647ae5cf75a5fd4c05 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/IntermediateLayer/Scenes/IntermediateLayer.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/IntermediateLayer/Scripts.meta b/Assets/FishNet/Demos/IntermediateLayer/Scripts.meta new file mode 100644 index 0000000..af18117 --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a91fd8f80ac0aee48bf00ae0d84167c4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs b/Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs new file mode 100644 index 0000000..2933047 --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs @@ -0,0 +1,60 @@ +using FishNet.Managing.Transporting; +using System; + +namespace FishNet.Example.IntermediateLayers +{ + /* Below is an example of creating a basic Caesar Cipher. + * Bytes are modified by a set value of CIPHER_KEY, and then + * the original src ArraySegment is returned. + * + * It's very important to only iterate the bytes provided + * as the segment. For example, if the ArraySegment contains + * 1000 bytes but the Offset is 3 and Count is 5 then you should + * only iterate bytes on index 3, 4, 5, 6, 7. The code below + * shows one way of properly doing so. + * + * If you are to change the byte array reference, size, or segment + * count be sure to return a new ArraySegment with the new values. + * For example, if your Offset was 0 and count was 10 but after + * encrypting data the Offset was still 0 and count 15 you would + * return new ArraySegment(theArray, 0, 15); */ + public class IntermediateLayerCipher : IntermediateLayer + { + private const byte CIPHER_KEY = 5; + //Decipher incoming data. + public override ArraySegment HandleIncoming(ArraySegment src, bool fromServer) + { + byte[] arr = src.Array; + int length = src.Count; + int offset = src.Offset; + + for (int i = src.Offset; i < (offset + length); i++) + { + short next = (short)(arr[i] - CIPHER_KEY); + if (next < 0) + next += 256; + arr[i] = (byte)next; + } + + return src; + } + //Cipher outgoing data. + public override ArraySegment HandleOutgoing(ArraySegment src, bool toServer) + { + byte[] arr = src.Array; + int length = src.Count; + int offset = src.Offset; + + for (int i = offset; i < (offset + length); i++) + { + short next = (short)(arr[i] + CIPHER_KEY); + if (next > byte.MaxValue) + next -= 256; + arr[i] = (byte)next; + } + + return src; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs.meta b/Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs.meta new file mode 100644 index 0000000..78e4630 --- /dev/null +++ b/Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 10c18bbe05944364d9295f5ee411a95f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/IntermediateLayer/Scripts/IntermediateLayerCipher.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction.meta b/Assets/FishNet/Demos/Prediction.meta new file mode 100644 index 0000000..368f132 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 63e5c7d3bb1a4a34fabadecc9726f54e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/CharacterController.meta b/Assets/FishNet/Demos/Prediction/CharacterController.meta new file mode 100644 index 0000000..efba932 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: accc5b93ebf3b0040baaec2298c7d394 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity b/Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity new file mode 100644 index 0000000..1124c5d --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity @@ -0,0 +1,1544 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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: 30 + _pingInterval: 1 + _physicsMode: 1 +--- !u!114 &192429406 +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 &192429409 +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: e08bb003fce297d4086cf8cba5aa459a, type: 3} + m_Name: + m_EditorClassIdentifier: + _dropExcessiveReplicates: 1 + _maximumServerReplicates: 5 + _createLocalStates: 1 + _stateInterpolation: 2 + _stateOrder: 1 +--- !u!1 &493975406 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 493975407} + - component: {fileID: 493975409} + - component: {fileID: 493975408} + - component: {fileID: 493975410} + m_Layer: 0 + m_Name: Graphics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &493975407 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 493975406} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 3, y: 0.25, z: 5} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 2017714963} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &493975408 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 493975406} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a2ca0973c8f8a4846bb7f3894c3bc5cf, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &493975409 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 493975406} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!114 &493975410 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 493975406} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 453481867b26f7c43b5bf38802a5f50e, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 2017714968} + _networkObjectCache: {fileID: 0} + _initializationSettings: + TargetTransform: {fileID: 2017714963} + DetachOnStart: 0 + AttachOnStop: 0 + _controllerMovementSettings: + EnableTeleport: 0 + TeleportThreshold: 0 + AdaptiveInterpolationValue: 4 + InterpolationValue: 2 + SmoothedProperties: 4294967295 + SnapNonSmoothedProperties: 0 + _spectatorMovementSettings: + EnableTeleport: 0 + TeleportThreshold: 0 + AdaptiveInterpolationValue: 4 + InterpolationValue: 2 + SmoothedProperties: 4294967295 + SnapNonSmoothedProperties: 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a2ca0973c8f8a4846bb7f3894c3bc5cf, 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &594065180 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408886491487332} + 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: 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!1 &595508190 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 595508193} + - component: {fileID: 595508192} + - component: {fileID: 595508191} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &595508191 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595508190} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &595508192 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595508190} + 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 &595508193 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595508190} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &618963832 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 618963833} + - component: {fileID: 618963834} + m_Layer: 0 + m_Name: MovingPlatform (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &618963833 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 618963832} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 8.081, y: -1.02, z: 2.76} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1814793385} + m_Father: {fileID: 1784594015} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &618963834 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 618963832} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 3, y: 0.25, z: 5} + m_Center: {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_UseViewFrustumForShadowCasterCull: 1 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 3 + 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: 0 +--- !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: 8.64} + m_LocalScale: {x: 1, y: 5, z: 5} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 4 + 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: 2, y: 2, z: 2} + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a2ca0973c8f8a4846bb7f3894c3bc5cf, 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 + m_AdditionalVertexStreams: {fileID: 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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: -2.8, y: -2, z: 0} + m_LocalScale: {x: 100, y: 1, z: 100} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!224 &1366966565 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 594065180} + m_PrefabAsset: {fileID: 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1112005916} + - {fileID: 555580085} + - {fileID: 1852016427} + - {fileID: 872683031} + - {fileID: 967467090} + - {fileID: 2017714963} + - {fileID: 618963833} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1814793384 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1814793385} + - component: {fileID: 1814793388} + - component: {fileID: 1814793387} + m_Layer: 0 + m_Name: Graphics + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1814793385 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1814793384} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 3, y: 0.25, z: 5} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 618963833} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1814793387 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1814793384} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a2ca0973c8f8a4846bb7f3894c3bc5cf, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1814793388 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1814793384} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1971436028 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1971436032} + - component: {fileID: 1971436031} + - component: {fileID: 1971436030} + - component: {fileID: 1971436029} + - component: {fileID: 1971436033} + m_Layer: 5 + m_Name: StaminaCanvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1971436029 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1971436028} + 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 &1971436030 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1971436028} + 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 + m_PresetInfoIsWorld: 0 +--- !u!223 &1971436031 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1971436028} + 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_VertexColorAlwaysGammaSpace: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1971436032 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1971436028} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2021829092} + m_Father: {fileID: 0} + m_RootOrder: 3 + 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 &1971436033 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1971436028} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2ca42d40476d05444acd0179c78b44cc, type: 3} + m_Name: + m_EditorClassIdentifier: + _staminaBar: {fileID: 2021829093} +--- !u!1 &2017714962 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2017714963} + - component: {fileID: 2017714964} + - component: {fileID: 2017714967} + - component: {fileID: 2017714968} + m_Layer: 0 + m_Name: MovingPlatform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2017714963 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2017714962} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 4.75, y: -1.02, z: 2.76} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 493975407} + m_Father: {fileID: 1784594015} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &2017714964 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2017714962} + m_Material: {fileID: 0} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 3, y: 0.25, z: 5} + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &2017714967 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2017714962} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0fca32023f23445429675b0c692d00d1, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 2017714968} + _networkObjectCache: {fileID: 0} + _tickCallbacks: 2 + _moveRate: 4 +--- !u!114 &2017714968 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2017714962} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 1 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 2 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 0 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2952109295 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &2021829091 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2021829092} + - component: {fileID: 2021829094} + - component: {fileID: 2021829093} + m_Layer: 5 + m_Name: Image + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2021829092 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021829091} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1971436032} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 0} + m_AnchoredPosition: {x: -200, y: 40} + m_SizeDelta: {x: 400, y: 40} + m_Pivot: {x: 0.5, y: 1} +--- !u!114 &2021829093 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021829091} + 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.39657754, g: 0.7830189, b: 0.2622375, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: f9b2bae1e919cc648a88e211cd094c53, type: 3} + m_Type: 3 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 0 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 1 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2021829094 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2021829091} + m_CullTransparentMesh: 1 +--- !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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1366966565} + m_Father: {fileID: 0} + m_RootOrder: 2 + 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: 192429409} + - component: {fileID: 192429404} + - component: {fileID: 192429406} + - component: {fileID: 7443408886491487336} + - component: {fileID: 7443408886491487337} + - component: {fileID: 7443408886491487338} + - component: {fileID: 7443408886491487339} + 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: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !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: 8bc8f0363ddc75946a958043c5e49a83, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 0, g: 0, b: 0, a: 1} + _placement: 2 + _secondsAveraged: 1 + _showOutgoing: 1 + _showIncoming: 1 +--- !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: 756c28cd3141c4140ae776188ee26729, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkTraffic: + _updateInteval: 1 + _updateClient: 1 + _updateServer: 1 +--- !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: 68828c85278210948b9d50a8db3aab74, type: 3} + m_Name: + m_EditorClassIdentifier: + _authenticator: {fileID: 0} + _remoteClientTimeout: 2 + _remoteClientTimeoutDuration: 5 + _allowPredictedSpawning: 0 + _reservedObjectIds: 15 + _syncTypeRate: 0.1 + SpawnPacking: + Position: 0 + Rotation: 2 + Scale: 2 + _changeFrameRate: 1 + _frameRate: 500 + _shareIds: 1 + _startOnHeadless: 1 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity.meta b/Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity.meta new file mode 100644 index 0000000..6218116 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/CharacterController Prediction Demo.unity.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 941f6f8cbac7e9d418ffc6f22ee9359f +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/CharacterController + Prediction Demo.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Materials.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Materials.meta new file mode 100644 index 0000000..945b291 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bdae6fa333d2d94449c27856e21d936a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat b/Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat new file mode 100644 index 0000000..f5975e7 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-6285344074005954244 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BlueMat + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 0.36344597, b: 0.5471698, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat.meta new file mode 100644 index 0000000..3d645ad --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: a2ca0973c8f8a4846bb7f3894c3bc5cf +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Materials/BlueMat.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs.meta new file mode 100644 index 0000000..e0abc32 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 50d54accc2af0c746b0729b097981b93 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab b/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab new file mode 100644 index 0000000..dac1faf --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab @@ -0,0 +1,381 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !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: -2060332294883903109} + - component: {fileID: 201277550} + - component: {fileID: 4148834954576211901} + m_Layer: 0 + m_Name: CharacterControllerPrediction + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6952090537135875148} + - {fileID: 7416592246441027450} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &-2060332294883903109 +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: 53a6d84aa5d214e48a7308646e5d20da, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 201277550} + _networkObjectCache: {fileID: 201277550} + _tickCallbacks: 6 + _jumpForce: 13 + _moveRate: 4 +--- !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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: -2060332294883903109} + - {fileID: 8022627623488540705} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: + - {fileID: 8013474958425823559} + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 1 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 2 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 0 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 23 + k__BackingField: 0 + k__BackingField: 9608169563376464318 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: -4.80351, y: 0.18147132, z: 5.430528} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!143 &4148834954576211901 +CharacterController: + 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_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: 1, z: 0} +--- !u!1 &2693313506199881053 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7416592246441027450} + - component: {fileID: 1810266687591957409} + - component: {fileID: 2367975056509727580} + - component: {fileID: 8013474958425823559} + m_Layer: 4 + m_Name: SphereCollider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7416592246441027450 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2693313506199881053} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 303449598114786577} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &1810266687591957409 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2693313506199881053} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.3 + m_Center: {x: 0, y: 0.26, z: 0} +--- !u!114 &2367975056509727580 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2693313506199881053} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3dc97a8b9e628044e83004c93095c14d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 8013474958425823559} + _networkObjectCache: {fileID: 8013474958425823559} + _maximumSimultaneousHits: 16 + _additionalSize: 0.1 + _layers: + serializedVersion: 2 + m_Bits: 1 +--- !u!114 &8013474958425823559 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2693313506199881053} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 1 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 2367975056509727580} + InitializedParentNetworkBehaviour: {fileID: -2060332294883903109} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 21 + k__BackingField: 0 + k__BackingField: 16574675316859699392 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!1 &5873068582516782542 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6952090537135875148} + - component: {fileID: 6605329024375725182} + - component: {fileID: 9006911641345807741} + - component: {fileID: 4946798872430241371} + - component: {fileID: 8022627623488540705} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6952090537135875148 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5873068582516782542} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 303449598114786577} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6605329024375725182 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5873068582516782542} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &9006911641345807741 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5873068582516782542} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &4946798872430241371 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5873068582516782542} + 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 &8022627623488540705 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5873068582516782542} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 453481867b26f7c43b5bf38802a5f50e, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 201277550} + _networkObjectCache: {fileID: 201277550} + _initializationSettings: + TargetTransform: {fileID: 303449598114786577} + DetachOnStart: 0 + AttachOnStop: 0 + _controllerMovementSettings: + EnableTeleport: 0 + TeleportThreshold: 0 + AdaptiveInterpolationValue: 4 + InterpolationValue: 2 + SmoothedProperties: 4294967295 + SnapNonSmoothedProperties: 0 + _spectatorMovementSettings: + EnableTeleport: 0 + TeleportThreshold: 0 + AdaptiveInterpolationValue: 4 + InterpolationValue: 2 + SmoothedProperties: 4294967295 + SnapNonSmoothedProperties: 0 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta new file mode 100644 index 0000000..141c821 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 5b712878ecece354ba4ffb026c0a221c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Prefabs/CharacterControllerPrediction.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt b/Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt new file mode 100644 index 0000000..88b6320 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt @@ -0,0 +1,17 @@ +This demo shows how to use a character controller with our prediction system. + +Important: + * Beta ReplicateStates must be enabled to use this demo. You can toggle this setting using the Fish-Networking menu. + +Setup: + * Open demo scene. + * Start server, host, or client only. You may test with parrelSync or host the project. + +Notes: + * Server and all clients run the same inputs. This is done by using State Forwarding on the NetworkObject inspector. + * NetworkTrigger is used to attach to platforms. You may review code within the controller script to see attach logic and comments. + On the prefab the trigger is attached as a child of the NetworkObject for sorting, but not under the graphical, as we do + not want the trigger to be modified outside the tick system (graphical gets smoothed, and is updated outside the tick loop). + * Moving platform predicts fully into the future so the client may step on it in real-time. See notes in MovingPlatform script. + Spectated objects may appear to correct when moving onto the platform. This can be resolved by balancing the future + prediction amount on the platform and the spectated objects. diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt.meta b/Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt.meta new file mode 100644 index 0000000..c5ef910 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 242afb68b5bce2f40ab2da029c12d008 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/ReadMe.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts.meta new file mode 100644 index 0000000..6ddee3b --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eaf414b899169f04bb4b0d65424a40bf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs new file mode 100644 index 0000000..2bac159 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs @@ -0,0 +1,487 @@ +#if !FISHNET_STABLE_REPLICATESTATES +using System; +using FishNet.Component.Prediction; +using FishNet.Connection; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Transporting; +using FishNet.Utility.Template; +using UnityEngine; + +namespace FishNet.Demo.Prediction.CharacterControllers +{ + public class CharacterControllerPrediction : TickNetworkBehaviour + { + #region Types. + /// + /// One-time inputs accumulated over frames between ticks. + /// + public struct OneTimeInput + { + /// + /// True to jump. + /// + public bool Jump; + + /// + /// Unset inputs. + /// + public void ResetState() + { + Jump = false; + } + } + + public struct ReplicateData : IReplicateData + { + public ReplicateData(Vector2 input, bool run, OneTimeInput oneTimeInputs) + { + OneTimeInputs = oneTimeInputs; + Input = input; + Run = run; + + _tick = 0; + } + + /// + /// True if Jump input was pressed for the tick. + /// + public OneTimeInput OneTimeInputs; + /// + /// Current movement directions held. + /// + public Vector2 Input; + /// + /// True if run is held. + /// + public readonly bool Run; + + /// + /// Tick is set at runtime. There is no need to manually assign this value. + /// + private uint _tick; + + public void Dispose() + { + OneTimeInputs.ResetState(); + } + + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + + public struct ReconcileData : IReconcileData + { + public ReconcileData(Vector3 position, float verticalVelocity, float stamina, MovingPlatform currentPlatform) + { + Position = position; + VerticalVelocity = verticalVelocity; + Stamina = stamina; + CurrentPlatform = currentPlatform; + + _tick = 0; + } + + /// + /// Position of the character. + /// + public Vector3 Position; + /// + /// Current vertical velocity. + /// + /// Used to simulate jumps and falls. + public float VerticalVelocity; + /// + /// Amount of stamina remaining to run or jump. + /// + public float Stamina; + + public MovingPlatform CurrentPlatform; + + /// + /// Tick is set at runtime. There is no need to manually assign this value. + /// + private uint _tick; + + public void Dispose() { } + + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + #endregion + + /// + /// Invokes whenever NetworkStart is called for owner. + /// + public static event Action OnOwner; + /// + /// Current stamina remaining. + /// + public float Stamina { get; private set; } + + /// + /// Amount of force for jumping. + /// + [Tooltip("Amount of force for jumping.")] + [SerializeField] + private float _jumpForce = 30f; + /// + /// How quickly to move. + /// + [Tooltip("How quickly to move.")] + [SerializeField] + private float _moveRate = 4f; + + /// + /// Current vertical velocity. + /// + private float _verticalVelocity; + /// + /// One-time inputs accumulated over frames between ticks. + /// + private OneTimeInput _oneTimeInputs = new(); + /// + /// Last data which was supplied during replicate outside of reconcile. + /// + private ReplicateData _lastTickedReplicateData = default; + /// + /// Current platform the player is on. + /// + private MovingPlatform _currentPlatform; + /// + /// Reference to the CharacterController component. + /// + private CharacterController _characterController; + /// + /// NetworkTrigger on this character; used to detact platforms. + /// + private NetworkTrigger _characterTrigger; + /// + /// maximum amount of stamina allowed. + /// + public const float Maximum_Stamina = 50f; + + private void Awake() + { + _characterController = GetComponent(); + + _characterTrigger = GetComponentInChildren(); + _characterTrigger.OnEnter += CharacterTrigger_OnEnter; + _characterTrigger.OnExit += CharacterTrigger_OnExit; + + //We only need the OnTick callback for non-physics. + base.SetTickCallbacks(TickCallback.Tick); + } + + public override void OnOwnershipClient(NetworkConnection prevOwner) + { + if (base.IsOwner) + OnOwner?.Invoke(this); + } + + private void Update() + { + SetOneTimeInputs(); + } + + /// + /// Checks setting inputs which are one-time(not held). + /// + private void SetOneTimeInputs() + { + if (!base.IsOwner) return; + + /* Check to jump. */ + if (Input.GetKeyDown(KeyCode.Space)) + _oneTimeInputs.Jump = true; + } + + protected override void TimeManager_OnTick() + { + PerformReplicate(BuildMoveData()); + CreateReconcile(); + } + + /// + /// Returns replicate data to send as the controller. + /// + private ReplicateData BuildMoveData() + { + /* Only the controller needs to build move data. + * This could be the server if the server if no owner, for example + * such as AI, or the owner of the object. */ + if (!base.IsOwner) return default; + + float horizontal = Input.GetAxisRaw("Horizontal"); + float vertical = Input.GetAxisRaw("Vertical"); + //Run when left shift is held. + bool run = Input.GetKey(KeyCode.LeftShift); + + ReplicateData md = new(new(horizontal, vertical), run, _oneTimeInputs); + + //Reset one tine inputs since they've been processed for the tick. + _oneTimeInputs.ResetState(); + + return md; + } + + /// + /// Creates a reconcile that is sent to clients. + /// + public override void CreateReconcile() + { + /* Both the server and client should create reconcile data. + * The client will use their copy as a fallback if they do not + * get data from the server, such as a dropped packet. + * + * The client will not reconcile unless it receives at least one + * reconcile packet from the server for the tick. */ + + /* You do not have to reconcile every tick if you wish to + * save bandwidth/perf, or simply feel as though it's not needed + * for your game type. + * + * Even when not reconciling every tick it's still recommended + * to build the reconcile as client; this cost is very little.*/ + + /* This is an example of only sending a reconcile occasionally + * if the server. Simply uncomment the if statement below to + * test this behavior. */ + // if (base.IsServerStarted) + // { + // //Exit early if 10 ticks have not passed. + // if (base.TimeManager.LocalTick % 10 != 0) return; + // } + + //Build the data using current information and call the reconcile method. + ReconcileData rd = new(transform.localPosition, _verticalVelocity, Stamina, _currentPlatform); + PerformReconcile(rd); + } + + [Replicate] + private void PerformReplicate(ReplicateData rd, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable) + { + //Always use the tickDelta as your delta when performing actions inside replicate. + float delta = (float)base.TimeManager.TickDelta; + bool useDefaultForces = false; + + /* When client only run some checks to + * further predict the clients future movement. + * This can keep the object more inlined with real-time by + * guessing what the clients input might be before we + * actually receive it. + * + * Doing this does risk a chance of graphical jitter in the + * scenario a de-synchronization occurs, but if only predicting + * a couple ticks the chances are low. */ + //See https://fish-networking.gitbook.io/docs/manual/guides/prediction/version-2/creating-code/predicting-states + if (!base.IsServerStarted && !base.IsOwner) + { + /* If ticked then set last ticked value. + * Ticked means the replicate is being run from the tick cycle, more + * specifically NOT from a replay/reconcile. */ + if (state.ContainsTicked()) + { + /* Dispose of old should it have anything that needs to be cleaned up. + * If you are only using value types in your data you do not need to call Dispose. + * You must implement dispose manually to cache any non-value types, if you wish. */ + _lastTickedReplicateData.Dispose(); + //Set new. + _lastTickedReplicateData = rd; + } + /* In the future means there is no way the data can be known to this client + * yet. For example, the client is running this script locally and due to + * how networking works, they have not yet received the latest information from + * the server. + * + * If in the future then we are only going to predict up to + * a certain amount of ticks in the future. This is us assuming that the + * server (or client which owns this in this case) is going to use the + * same input for at least X number of ticks. You can predict none, or as many + * as you like, but the more inputs you predict the higher likeliness of guessing + * wrong. If you do however predict wrong often smoothing will cover up the mistake. */ + else if (state.IsFuture()) + { + /* Predict up to 1 tick more. */ + if (rd.GetTick() - _lastTickedReplicateData.GetTick() > 1) + { + useDefaultForces = true; + } + else + { + /* If here we are predicting the future. */ + + /* You likely do not need to dispose rd here since it would be default + * when state is 'not created'. We are simply doing it for good practice, should your ReplicateData + * contain any garbage collection. */ + rd.Dispose(); + + rd = _lastTickedReplicateData; + + /* There are some fields you might not want to predict, for example + * jump. The odds of a client pressing jump two ticks in a row is unlikely. + * The stamina check below would likely prevent such a scenario. + * + * We're going to unset jump for this reason. */ + rd.OneTimeInputs.Jump = false; + + /* Be aware that future predicting is not a one-size fits all + * feature. How much you predict into the future, if at all, depends + * on your game mechanics and your desired outcome. */ + } + } + } + + Vector3 forces; + + if (useDefaultForces) + { + /* Character controllers are a bit problematic with colliders. + * If you were to pass Vector3.zero into the move then there's + * a chance other colliders will clip through the characterController. + * When this is combined with reconciles, it's practically gauranteed + * this will happen. + * + * Because of this issue, if we are 'using default forces' we will apply a + * very insignificant amount of force which makes the characterController + * update properly. */ + forces = new(0f, -1f, 0f); + } + //Calculate forces. + else + { + //Stamina regained over every second. + const float regainedStamina = 25f; + //Add stamina with every tick. + ModifyStamina(regainedStamina * delta); + + //Add gravity. Extra gravity is added for snappier jumps. + _verticalVelocity += (Physics.gravity.y * delta * 3f); + //Cap gravity to -20f so the player doesn't fall too fast. + if (_verticalVelocity < -40f) + _verticalVelocity = -40f; + + //Normalize direction so the player does not move faster at angles. + rd.Input = rd.Input.normalized; + + /* Typically speaking any modification which can affect your CSP (prediction) should occur + * inside replicate. This is why we add/remove stamina, and move within replicate. */ + + //Default run multiplier. + float runMultiplier; + //Stamina required to run over a second. + const float runStamina = 50f; + if (rd.Run && TryRemoveStamina(runStamina * delta)) + runMultiplier = 1.5f; + else + runMultiplier = 1f; + + //Stamina required to jump. + const byte jumpStamina = 30; + /* For consistent jumps set to jump force when jumping, rather + * than add force onto current gravity. */ + if (rd.OneTimeInputs.Jump && TryRemoveStamina(jumpStamina)) + _verticalVelocity = _jumpForce; + + forces = new Vector3(rd.Input.x, 0f, rd.Input.y) * (_moveRate * runMultiplier); + //Add vertical velocity to forces. + forces.y = _verticalVelocity; + } + + _characterController.Move(forces * delta); + } + + [Reconcile] + private void PerformReconcile(ReconcileData rd, Channel channel = Channel.Unreliable) + { + /* Simply set current values to as they are + * in the reconcile data. */ + _verticalVelocity = rd.VerticalVelocity; + Stamina = rd.Stamina; + + /* Even though the platform is traced for in replicate we must also + * pass the current platform into the reconcile, and set our local value + * to whatever is provided in the reconcile. + * + * This is done because we use local position to reset the character + * and if the clients currentPlatform differs from what the server + * had when reconciling the world position would be significantly + * different due to the parent not aligning. */ + _currentPlatform = rd.CurrentPlatform; + //Set transform parent after assigning current. + SetParent(); + + /* Update position AFTER setting the parent, otherwise + * you would face a potentially huge positional de-sync + * as mentioned above. */ + transform.localPosition = rd.Position; + } + + + /// + /// Called when the trigger on this object enters another collider. + /// + private void CharacterTrigger_OnEnter(Collider c) + { + //We only care about moving platforms. + if (!c.TryGetComponent(out MovingPlatform mp)) + return; + + _currentPlatform = mp; + SetParent(); + } + + /// + /// Called when the trigger on this object exits another collider. + /// + private void CharacterTrigger_OnExit(Collider c) + { + if (!c.TryGetComponent(out MovingPlatform mp)) + return; + + //Only check if already attached to a platform. + if (_currentPlatform == null) + return; + //Not the current platform. + if (_currentPlatform != mp) + return; + + //Is the current platform, unset. + _currentPlatform = null; + SetParent(); + } + + /// + /// Updates parent state based on current platforms value. + /// + private void SetParent() + { + if (_currentPlatform != null) + transform.SetParent(_currentPlatform.transform); + else + transform.SetParent(null); + } + + /// + /// Modifies stamina by adding or removing stamina. + /// + private void ModifyStamina(float value) + { + float next = Stamina + value; + Stamina = Mathf.Clamp(next, 0f, Maximum_Stamina); + } + + /// + /// Removes stamina if enough stamina is available. + /// + /// >True if stamina was available and removed. + private bool TryRemoveStamina(float value) + { + if (Stamina < value) return false; + + Stamina -= value; + return true; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta new file mode 100644 index 0000000..46f2413 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 53a6d84aa5d214e48a7308646e5d20da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Scripts/CharacterControllerPrediction.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs new file mode 100644 index 0000000..c43ebcc --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Transporting; +using FishNet.Utility.Template; +using UnityEngine; + +namespace FishNet.Demo.Prediction.CharacterControllers +{ + public class MovingPlatform : TickNetworkBehaviour + { + #region Types. + public struct ReplicateData : IReplicateData + { + public ReplicateData(uint unused = 0) + { + _tick = 0; + } + + /// + /// Tick is set at runtime. There is no need to manually assign this value. + /// + private uint _tick; + + public void Dispose() { } + + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + + public struct ReconcileData : IReconcileData + { + public ReconcileData(Vector3 position, byte goalIndex) + { + Position = position; + GoalIndex = goalIndex; + _tick = 0; + } + + /// + /// Position of the character. + /// + public Vector3 Position; + /// + /// Current vertical velocity. + /// + /// Used to simulate jumps and falls. + public byte GoalIndex; + + /// + /// Tick is set at runtime. There is no need to manually assign this value. + /// + private uint _tick; + + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + #endregion + + [SerializeField] + private float _moveRate = 4f; + /// + /// Goal to move towards. + /// + private byte _goalIndex; + /// + /// Goals to move towards. + /// + private List _goals = new(); + + private void Awake() + { + const float offset = 5f; + Vector3 position = transform.position; + _goals.Add(position + new Vector3(0f, 0f, offset)); + _goals.Add(position + new Vector3(0f, 0f, -offset)); + } + + public override void OnStartNetwork() + { + base.SetTickCallbacks(TickCallback.Tick); + } + + protected override void TimeManager_OnTick() + { + PerformReplicate(default); + CreateReconcile(); + } + + /// + /// Creates a reconcile that is sent to clients. + /// + public override void CreateReconcile() + { + ReconcileData rd = new(transform.position, _goalIndex); + PerformReconcile(rd); + } + + [Replicate] + private void PerformReplicate(ReplicateData rd, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable) + { + /* Move logic is always called regardless of state. + * + * This means that move will be called when replaying after a reconcile, + * as well during OnTick, as well even if there is no data being received + * from the server (IsFuture). + * + * By doing this we allow the platform to be ahead of what the client has + * received by the server. + * + * When observed the client will actually see the platform + * ahead of the server depending on their ping, being more ahead with higher pings. + * This is the desired outcome as that's where the platform will be by the time + * the client sends input to the server. If the platform was not ahead of the server + * then when the client perhaps jumped onto it, the platform would actually be further + * on the server then where the client observed it. In result, the client would snap to + * a correct position when landing on the platform. + * + * If you want to visually see this simple uncomment the line below. Be sure to add + * latency to make the correction more noticeable. */ + // if (state.IsFuture()) + // return; + + //Always use the tickDelta as your delta when performing actions inside replicate. + float delta = (float)base.TimeManager.TickDelta; + + Vector3 goal = _goals[_goalIndex]; + Vector3 next = Vector3.MoveTowards(transform.position, goal, delta * _moveRate); + + transform.position = next; + + if (next == goal) + { + _goalIndex++; + if (_goalIndex >= _goals.Count) + _goalIndex = 0; + } + } + + [Reconcile] + private void PerformReconcile(ReconcileData rd, Channel channel = Channel.Unreliable) + { + transform.position = rd.Position; + _goalIndex = rd.GoalIndex; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs.meta new file mode 100644 index 0000000..c1b8b12 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0fca32023f23445429675b0c692d00d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Scripts/MovingPlatform.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs new file mode 100644 index 0000000..7d0b89d --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs @@ -0,0 +1,41 @@ +#if !FISHNET_STABLE_REPLICATESTATES +using UnityEngine; +using UnityEngine.UI; + +namespace FishNet.Demo.Prediction.CharacterControllers +{ + /// + /// This is a basic implementation of hooking up the UI to the owners character stamina. + /// + public class StaminaCanvas : MonoBehaviour + { + [SerializeField] + private Image _staminaBar; + + private CharacterControllerPrediction _character; + + private void Awake() + { + CharacterControllerPrediction.OnOwner += CCP_OnOwner; + } + + private void OnDestroy() + { + CharacterControllerPrediction.OnOwner -= CCP_OnOwner; + } + + private void Update() + { + if (_character == null) return; + + float fillAmount = (_character.Stamina / CharacterControllerPrediction.Maximum_Stamina); + _staminaBar.fillAmount = fillAmount; + } + + private void CCP_OnOwner(CharacterControllerPrediction ccp) + { + _character = ccp; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs.meta new file mode 100644 index 0000000..0a52fa6 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2ca42d40476d05444acd0179c78b44cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Scripts/StaminaCanvas.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Textures.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Textures.meta new file mode 100644 index 0000000..a71cb23 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d3aa9f1d4a89e9d438bd7a3048417f2f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png b/Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png new file mode 100644 index 0000000000000000000000000000000000000000..4927ef6d4e27d75e08f334ec983e5d06aaf0d549 GIT binary patch literal 1793 zcma)7eM}o=7(b@fiGWL(6a7OkR|lI*@7gOL_R?FWEgKa|O8F`njo0?to^9{CyMum= z;m4R}0~68AWUTQ69h>|iI*bc$ZgZJ1V$@9~E{-URiDdWz$c82$_}&!?tg`sWwf8>1 z-}Ah`=Y5~&x&5|^otfzmq(cytX(=_^!GD_ctX~IyUkO+J2tkT0rrIGotmTA@3!;>p zbJA!e7y>v1=?#$(Mcp5H13s5z(JssBbZVzEM z?^tvOD-!XEVu--7a5#*HwJ67XF^x{A!_+v2<0`;V2~k$0A}Ur;N+A+4%(UR*nUKhE zEG)&OoLr+wA_&0YC1t@-GB_(NW)IE*i%=m7}kf~#?@O08Alc(taG&}s-Ru2Sc#)k(6Ib2Fak8svg}KqgkQL3y|- zk@`RU2-?Plb-aY0NwfNX`1+P{fk3DH{ z3kuc7>nDHdnnq`pg+DOwWc$l64PTp#w06!k?ftyH?y0`o--U&SZ|%mqv){n_o?{Kg z5o6?;BX4z{+B(wxuB+_a;JF8_=$7W-_*A;#P`qdIrJ;t0H$wil54PQC`}vH+;;*No zx<3Nl$L2-`oA)a_4riAAwrRqcXWn2ybMIH4-Edp`1%2y%5A8YLwWDPrJf9IAs&UtD zefz`m1NjPl*QO&eqtkx9wLN2V-@>!AJ$ZNTIXIqW_|A|sPPPNile_?+;w!|qsu4U*(Y*en{2-Q%Szv;tpocW z|GH&HcWU2-p5|diVB)dfco=O-E5f`N-$XA}f^X4Xq0)K*g6__io;0X4o&$^-7ISg+ f`a`b27ZwbVVt(Dc<4=E<^yRXYRG8m4KK;VKrh#JQ literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png.meta b/Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png.meta new file mode 100644 index 0000000..3441b94 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png.meta @@ -0,0 +1,111 @@ +fileFormatVersion: 2 +guid: f9b2bae1e919cc648a88e211cd094c53 +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: 1 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/CharacterController/Textures/Stamina_Bar.png + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody.meta b/Assets/FishNet/Demos/Prediction/Rigidbody.meta new file mode 100644 index 0000000..0f82871 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6f3426c410532ce438e1d80868d45500 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials.meta new file mode 100644 index 0000000..e6e6110 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 15ebc39fcc93fdf478b190c392816c1e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat new file mode 100644 index 0000000..c18c8b2 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: BlueMat + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0, g: 0.36344597, b: 0.5471698, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &2234912879387491547 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat.meta new file mode 100644 index 0000000..9aed7b7 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 67717b2583b1d8644a7ed4857736c9cd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Materials/BlueMat.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat new file mode 100644 index 0000000..9c0ad4c --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: FrontWheel + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0.71853435, g: 0.53301877, b: 1, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &2791185524806145886 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat.meta new file mode 100644 index 0000000..b18d342 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: c9adb005e24322147b85e0d1ed5fa70e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Materials/FrontWheel.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat new file mode 100644 index 0000000..1bd6866 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat @@ -0,0 +1,140 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-8376420411095658724 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: GreenMat + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 0.13762352, g: 1, b: 0, a: 0.49019608} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat.meta new file mode 100644 index 0000000..e9a4fe4 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 7b5b45d5c3b714340a1ca8f4c642bd85 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Materials/GreenMat.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat new file mode 100644 index 0000000..edf3310 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-1536949353321725279 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: OrangeMat + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 1, g: 0.52609503, b: 0, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat.meta new file mode 100644 index 0000000..e9d5779 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 927568f7fc1cfa14ca78ab4e2ecd8d76 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Materials/OrangeMat.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial new file mode 100644 index 0000000..761c965 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.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: Slippery + dynamicFriction: 0.25 + staticFriction: 0.25 + bounciness: 0 + frictionCombine: 0 + bounceCombine: 0 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial.meta new file mode 100644 index 0000000..1f55d7c --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 5279afe67245e4641af070c5a02df994 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 13400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Materials/Slippery.physicMaterial + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs.meta new file mode 100644 index 0000000..9ec03ed --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7a2f99f17af3b58498907ae93ce16887 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab b/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab new file mode 100644 index 0000000..55cb947 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab @@ -0,0 +1,501 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &562823040 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 562823041} + - component: {fileID: 562823045} + - component: {fileID: 562823044} + - component: {fileID: 562823043} + - component: {fileID: 562823042} + m_Layer: 0 + m_Name: RearWheel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &562823041 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562823040} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.32, z: -1.72} + m_LocalScale: {x: 1, y: 2, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 303449598114786577} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &562823045 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562823040} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &562823044 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562823040} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 67717b2583b1d8644a7ed4857736c9cd, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!135 &562823043 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562823040} + 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!54 &562823042 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 562823040} + 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!1 &1491914380 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1491914381} + - component: {fileID: 1491914384} + - component: {fileID: 1491914383} + - component: {fileID: 1491914382} + - component: {fileID: 1491914385} + m_Layer: 0 + m_Name: FrontWheel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1491914381 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1491914380} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.19, z: 1.52} + m_LocalScale: {x: 1, y: 2, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 303449598114786577} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1491914384 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1491914380} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &1491914383 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1491914380} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: c9adb005e24322147b85e0d1ed5fa70e, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!135 &1491914382 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1491914380} + 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!54 &1491914385 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1491914380} + 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!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: -4020445548991912745} + - component: {fileID: 201277550} + - component: {fileID: 1517669882} + - component: {fileID: 1517669883} + - component: {fileID: 1517669884} + - component: {fileID: -7879966745361712897} + 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: 0.5, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1701588101648257645} + - {fileID: 1491914381} + - {fileID: 562823041} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &-4020445548991912745 +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: 1ad6d36d37ac2b84391a4f7362846abf, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 201277550} + _networkObjectCache: {fileID: 201277550} + _tickCallbacks: 6 + _frontWheelRigidbody: {fileID: 1491914385} + _rearWheelRigidbody: {fileID: 562823042} + _boostDuration: 2 + _boostForce: 30 + _moveRate: 40 + _turnRate: 15 +--- !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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: -4020445548991912745} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 1 + _predictionType: 1 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 2 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 4 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 3 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 22 + k__BackingField: 0 + k__BackingField: 9421305386573913446 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: -4.80351, y: 0.18147132, z: 5.430528} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 0.5, z: 1} + IsValid: 1 +--- !u!54 &1517669882 +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 + m_UseGravity: 1 + m_IsKinematic: 0 + m_Interpolate: 0 + m_Constraints: 0 + m_CollisionDetection: 0 +--- !u!138 &1517669883 +FixedJoint: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_ConnectedBody: {fileID: 1491914385} + m_ConnectedArticulationBody: {fileID: 0} + m_BreakForce: Infinity + m_BreakTorque: Infinity + m_EnableCollision: 1 + m_EnablePreprocessing: 1 + m_MassScale: 1 + m_ConnectedMassScale: 1 +--- !u!138 &1517669884 +FixedJoint: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 303449598114786579} + m_ConnectedBody: {fileID: 562823042} + m_ConnectedArticulationBody: {fileID: 0} + m_BreakForce: Infinity + m_BreakTorque: Infinity + m_EnableCollision: 1 + m_EnablePreprocessing: 1 + m_MassScale: 1 + m_ConnectedMassScale: 1 +--- !u!65 &-7879966745361712897 +BoxCollider: + 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_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!1 &4223887704561426673 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1701588101648257645} + - component: {fileID: 4038509139872355570} + - component: {fileID: 948196868863103541} + - component: {fileID: 3171307449894521636} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1701588101648257645 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4223887704561426673} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 2, y: 1, z: 2} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 303449598114786577} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4038509139872355570 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4223887704561426673} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &948196868863103541 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4223887704561426673} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 67717b2583b1d8644a7ed4857736c9cd, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!65 &3171307449894521636 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4223887704561426673} + 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} diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta new file mode 100644 index 0000000..5777cdf --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 26a567abbe21227428f5c3d309d1d73c +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Prefabs/RigidbodyPrediction.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity b/Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity new file mode 100644 index 0000000..5c52e09 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity @@ -0,0 +1,1234 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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: 50 + _pingInterval: 1 + _physicsMode: 1 +--- !u!114 &192429406 +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 &192429409 +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: e08bb003fce297d4086cf8cba5aa459a, type: 3} + m_Name: + m_EditorClassIdentifier: + _dropExcessiveReplicates: 1 + _maximumServerReplicates: 15 + _createLocalStates: 1 + _stateInterpolation: 1 + _stateOrder: 1 +--- !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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 934211206 + SerializedTransformProperties: + Position: {x: 10.585218, y: 24.346329, z: 20.032343} + Rotation: {x: 0.37959778, y: -0.5981522, z: 0.3830318, w: 0.5927952} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1001 &594065180 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408886491487332} + 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: 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!1 &595508190 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 595508193} + - component: {fileID: 595508192} + - component: {fileID: 595508191} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &595508191 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595508190} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_SendPointerHoverToParent: 1 + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &595508192 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595508190} + 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 &595508193 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 595508190} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &782884182 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 782884183} + - component: {fileID: 782884186} + - component: {fileID: 782884185} + - component: {fileID: 782884184} + m_Layer: 0 + m_Name: Ramp + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &782884183 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 782884182} + m_LocalRotation: {x: -0.085818455, y: -0.17384347, z: -0.01520934, w: 0.98090893} + m_LocalPosition: {x: -3.3, y: -2.26, z: 21.8} + m_LocalScale: {x: 7.6, y: 3, z: 10} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: -10, y: -20.1, z: 0} +--- !u!65 &782884184 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 782884182} + m_Material: {fileID: 13400000, guid: 5279afe67245e4641af070c5a02df994, type: 2} + 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 &782884185 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 782884182} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 927568f7fc1cfa14ca78ab4e2ecd8d76, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &782884186 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 782884182} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 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_UseViewFrustumForShadowCasterCull: 1 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 2 + 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: 13400000, guid: 5279afe67245e4641af070c5a02df994, type: 2} + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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: -2.8, y: -2, z: 0} + m_LocalScale: {x: 200, y: 1, z: 200} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!224 &1366966565 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 594065180} + m_PrefabAsset: {fileID: 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} + - component: {fileID: 1784594016} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1112005916} + - {fileID: 1852016427} + - {fileID: 872683031} + - {fileID: 782884183} + - {fileID: 1978975901} + - {fileID: 1793840075} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1784594016 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1784594014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1073675958 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1793840074 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1793840075} + - component: {fileID: 1793840078} + - component: {fileID: 1793840077} + - component: {fileID: 1793840079} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1793840075 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1793840074} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 3.6, y: -1.93, z: 6} + m_LocalScale: {x: 25, y: 3, z: 25} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1793840077 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1793840074} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 927568f7fc1cfa14ca78ab4e2ecd8d76, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1793840078 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1793840074} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!64 &1793840079 +MeshCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1793840074} + m_Material: {fileID: 13400000, guid: 5279afe67245e4641af070c5a02df994, type: 2} + m_IsTrigger: 0 + m_Enabled: 1 + serializedVersion: 4 + m_Convex: 1 + m_CookingOptions: 30 + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1978975897 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1978975901} + - component: {fileID: 1978975900} + - component: {fileID: 1978975899} + - component: {fileID: 1978975902} + - component: {fileID: 1978975903} + - component: {fileID: 1978975904} + m_Layer: 0 + m_Name: Boost + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!23 &1978975899 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1978975897} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 7b5b45d5c3b714340a1ca8f4c642bd85, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1978975900 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1978975897} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1978975901 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1978975897} + m_LocalRotation: {x: 0.35355338, y: -0.1464466, z: 0.35355338, w: 0.8535535} + m_LocalPosition: {x: 5.21, y: 0.39, z: 5} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1784594015} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 45, y: 0, z: 45} +--- !u!114 &1978975902 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1978975897} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3dc97a8b9e628044e83004c93095c14d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 1784594016} + _networkObjectCache: {fileID: 0} + _maximumSimultaneousHits: 16 + _additionalSize: 0.1 + _layers: + serializedVersion: 2 + m_Bits: 1 +--- !u!135 &1978975903 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1978975897} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.65 + m_Center: {x: 0.00000047683716, y: 0.00000047683716, z: 0} +--- !u!114 &1978975904 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1978975897} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3cd8a84b2a9f149449a54fc8110bbfde, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 255 + _addedNetworkObject: {fileID: 1784594016} + _networkObjectCache: {fileID: 0} + _rotateRate: 90 +--- !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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1366966565} + 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: 192429409} + - component: {fileID: 192429404} + - component: {fileID: 192429406} + - component: {fileID: 7443408886491487336} + - component: {fileID: 7443408886491487337} + - 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: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, type: 3} + _addToDefaultScene: 1 + Spawns: [] +--- !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: 8bc8f0363ddc75946a958043c5e49a83, type: 3} + m_Name: + m_EditorClassIdentifier: + _color: {r: 0, g: 0, b: 0, a: 1} + _placement: 2 + _secondsAveraged: 1 + _showOutgoing: 1 + _showIncoming: 1 +--- !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: 756c28cd3141c4140ae776188ee26729, type: 3} + m_Name: + m_EditorClassIdentifier: + NetworkTraffic: + _updateInteval: 1 + _updateClient: 1 + _updateServer: 1 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity.meta new file mode 100644 index 0000000..1f64724 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0b7f6237292ee8a4eabe7f955f4a4de5 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Rigidbody Prediction Demo.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts.meta new file mode 100644 index 0000000..352c8f5 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2b7a81baadcf96f41ad7f4b44f2ca2b8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs new file mode 100644 index 0000000..9d98329 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs @@ -0,0 +1,36 @@ +using FishNet.Component.Prediction; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Demo.Prediction.Rigidbodies +{ + public class Boost : NetworkBehaviour + { + [SerializeField] + private float _rotateRate = 90f; + + private void Awake() + { + NetworkTrigger networkTrigger = GetComponent(); + /* No need to unsubscribe this networkTrigger is on this object. + * The subscription will die with the object. */ + networkTrigger.OnEnter += NetworkTrigger_OnEnter; + } + + private void Update() + { + transform.Rotate(new Vector3(0f, 1f, 0f) * (_rotateRate * Time.deltaTime)); + } + + private void NetworkTrigger_OnEnter(Collider c) + { + if (!c.transform.root.TryGetComponent(out RigidbodyPrediction rbp)) return; + + /* When the vehicle enters this object call set boosted. + * This trigger will invoke if the client enters it after a reconcile as well. + * Because of this, it's not unusual to see enter/exit called many times over a second + * due to the vehicle reconciling and running through the trigger again. */ + rbp.SetBoosted(); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs.meta new file mode 100644 index 0000000..de881f1 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3cd8a84b2a9f149449a54fc8110bbfde +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/Boost.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs new file mode 100644 index 0000000..bf274be --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs @@ -0,0 +1,293 @@ +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Transporting; +using FishNet.Utility.Template; +using UnityEngine; + +/* Note: the graphical object for this predicted NetworkObject is unset. +* This is because currently the NetworkObject only allows setting of one + * graphical object, but there are three things that move independently. + * + * In version 4.5.8 there will be an option to support multiple graphical objects + * and this demo will be updated. There are plans to release 4.5.8 with only this improvement + * to get the update out fast as possible. */ + +namespace FishNet.Demo.Prediction.Rigidbodies +{ + public class RigidbodyPrediction : TickNetworkBehaviour + { + #region Types. + public struct ReplicateData : IReplicateData + { + public ReplicateData(Vector2 input, bool fire) + { + Input = input; + Fire = fire; + + _tick = 0; + } + + /// + /// Current movement directions held. + /// + public Vector2 Input; + /// + /// True to fire. + /// + public bool Fire; + + /// + /// Tick is set at runtime. There is no need to manually assign this value. + /// + private uint _tick; + + public void Dispose() { } + + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + + public struct ReconcileData : IReconcileData + { + public ReconcileData(PredictionRigidbody root, PredictionRigidbody frontWheel, PredictionRigidbody rearWheel, uint boostStartTick) + { + Root = root; + FrontWheel = frontWheel; + RearWheel = rearWheel; + BoostStartTick = boostStartTick; + + _tick = 0; + } + + /// + /// PredictionRigidbody on the root. + /// + public PredictionRigidbody Root; + /// + /// PredictionRigidbody controlling the front wheel. + /// + public PredictionRigidbody FrontWheel; + /// + /// PredictionRigidbody controlling the rear wheel. + /// + public PredictionRigidbody RearWheel; + /// + /// Tick which the boost started. + /// + public uint BoostStartTick; + + /// + /// Tick is set at runtime. There is no need to manually assign this value. + /// + private uint _tick; + + /* You do not need to dispose PredictionRigidbody when used with prediction. + * These references will automatically use pooling to prevent garbage allocations! */ + public void Dispose() { } + public uint GetTick() => _tick; + public void SetTick(uint value) => _tick = value; + } + #endregion + + [SerializeField] + private Rigidbody _frontWheelRigidbody; + [SerializeField] + private Rigidbody _rearWheelRigidbody; + + [SerializeField] + private float _boostDuration = 1f; + [SerializeField] + private float _boostForce = 20f; + + [SerializeField] + private float _moveRate = 4f; + [SerializeField] + private float _turnRate = 4f; + + /// + /// Root of the vehicle. + /// + private PredictionRigidbody _root = new(); + /// + /// Drives turning (front wheels). + /// + private PredictionRigidbody _frontWheel = new(); + /// + /// Drives acceleration (rear wheels). + /// + private PredictionRigidbody _rearWheel = new(); + + /// + /// Tick which the boost started. + /// + private uint _boostStartTick = TimeManager.UNSET_TICK; + /// + /// Tick on the last replicate. + /// + private uint _lastReplicateTick; + /// + /// Next tick the controller is allowed to predicted fire. + /// + private uint _nextAllowedFireTick; + + private void Awake() + { + _root.Initialize(GetComponent()); + _frontWheel.Initialize(_frontWheelRigidbody); + _rearWheel.Initialize(_rearWheelRigidbody); + } + + public override void OnStartNetwork() + { + //Rigidbodies need tick and postTick. + base.SetTickCallbacks(TickCallback.Tick | TickCallback.PostTick); + } + + protected override void TimeManager_OnTick() + { + PerformReplicate(BuildMoveData()); + CreateReconcile(); + } + + /// + /// Returns replicate data to send as the controller. + /// + private ReplicateData BuildMoveData() + { + /* Only the controller needs to build move data. + * This could be the server if the server if no owner, for example + * such as AI, or the owner of the object. */ + if (!base.IsOwner) return default; + + float horizontal = Input.GetAxisRaw("Horizontal"); + float vertical = Input.GetAxisRaw("Vertical"); + //To keep things simple firing is done by holding left shift. + bool fire = Input.GetKey(KeyCode.LeftShift); + + ReplicateData md = new(new(horizontal, vertical), fire); + + return md; + } + + /// + /// Creates a reconcile that is sent to clients. + /// + public override void CreateReconcile() + { + /* Both the server and client should create reconcile data. + * The client will use their copy as a fallback if they do not + * get data from the server, such as a dropped packet. + * + * The client will not reconcile unless it receives at least one + * reconcile packet from the server for the tick. */ + + /* You do not have to reconcile every tick if you wish to + * save bandwidth/perf, or simply feel as though it's not needed + * for your game type. + * + * Even when not reconciling every tick it's still recommended + * to build the reconcile as client; this cost is very little.*/ + + /* This is an example of only sending a reconcile occasionally + * if the server. Simply uncomment the if statement below to + * test this behavior. */ + // if (base.IsServerStarted) + // { + // //Exit early if 10 ticks have not passed. + // if (base.TimeManager.LocalTick % 10 != 0) return; + // } + + //Build the data using current information and call the reconcile method. + ReconcileData rd = new(_root, _frontWheel, _rearWheel, _boostStartTick); + PerformReconcile(rd); + } + + [Replicate] + private void PerformReplicate(ReplicateData rd, ReplicateState state = ReplicateState.Invalid, Channel channel = Channel.Unreliable) + { + uint rdTick = rd.GetTick(); + _lastReplicateTick = rdTick; + /* Since rigidbodies typically carry inertia you do not necessarily need + * to predict in the future; rigidbodies will continue to move along + * the same path anyway. + * + * You can still predict inputs a couple ticks like we did in the + * CharacterController example, if you find doing so creates + * better results. */ + + Vector3 turningForce = new(rd.Input.x * _turnRate, 0f, 0f); + Vector3 forwardForce = new(0f, 0f, rd.Input.y * _moveRate); + /* If boostStartTick is not unset then a boost is started. + * + * Make sure that the current data tick is at least equal + * to boost tick before adding boost. + * This is done in the scenario a boost happened outside replay, + * we don't want to boost during a replay before the boost started. */ + if (_boostStartTick != TimeManager.UNSET_TICK && rdTick >= _boostStartTick) + { + //Add boost to forward force. + forwardForce += new Vector3(0f, 0f, _boostForce); + + uint boostTimeToTicks = base.TimeManager.TimeToTicks(_boostDuration, TickRounding.RoundUp); + //This is when boost will end. + uint endTick = (_boostStartTick + boostTimeToTicks); + + //Unset boost if tick is met. + if (rdTick >= endTick) + _boostStartTick = TimeManager.UNSET_TICK; + } + + //Convert forwards based on root forward. + Transform rootTransform = _root.Rigidbody.transform; + turningForce = rootTransform.TransformDirection(turningForce); + forwardForce = rootTransform.TransformDirection(forwardForce); + + //Flip turning if vehicle is also flipped. + if (rootTransform.up.y <= -0.1f) + turningForce *= -1f; + + /* Add turning and forward force. + * + * Notice that forces are NOT multiplied by + * delta. Just like Unity physics, predictionRigidbodies + * do not include delta in calculated forces. */ + _frontWheel.AddForce(turningForce); + _rearWheel.AddForce(forwardForce); + + _root.Simulate(); + _frontWheel.Simulate(); + _rearWheel.Simulate(); + } + + [Reconcile] + private void PerformReconcile(ReconcileData rd, Channel channel = Channel.Unreliable) + { + /* Reconcile boosted start tick. Even though the NetworkTrigger will + * invoke again if a replayed replicate pushes the vehicle through + * the trigger, it will not if the vehicle is in the trigger before the reconcile, + * as well after; since they never left, enter will not be called.*/ + _boostStartTick = rd.BoostStartTick; + //Reconcile all the rigidbodies. + _root.Reconcile(rd.Root); + _frontWheel.Reconcile(rd.FrontWheel); + _rearWheel.Reconcile(rd.RearWheel); + } + + /// + /// Sets boosted state for a number of ticks. + /// + public void SetBoosted() + { + /* Boost start is set to whatever tick was last replicated. + * Replicate is called every tick, so if the controller hits + * the collider during a replay the tick will be whatever replicate + * is being replayed, if outside a replay it will be the current + * tick. + * + * If owner or server the current tick would be localTick, otherwise + * it will be server tick. */ + _boostStartTick = _lastReplicateTick; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta new file mode 100644 index 0000000..74c82b4 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1ad6d36d37ac2b84391a4f7362846abf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Scripts/RigidbodyPrediction.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Textures.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Textures.meta new file mode 100644 index 0000000..6f2209d --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 352b4412576d5134aaf72b172a328810 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png b/Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png new file mode 100644 index 0000000000000000000000000000000000000000..4927ef6d4e27d75e08f334ec983e5d06aaf0d549 GIT binary patch literal 1793 zcma)7eM}o=7(b@fiGWL(6a7OkR|lI*@7gOL_R?FWEgKa|O8F`njo0?to^9{CyMum= z;m4R}0~68AWUTQ69h>|iI*bc$ZgZJ1V$@9~E{-URiDdWz$c82$_}&!?tg`sWwf8>1 z-}Ah`=Y5~&x&5|^otfzmq(cytX(=_^!GD_ctX~IyUkO+J2tkT0rrIGotmTA@3!;>p zbJA!e7y>v1=?#$(Mcp5H13s5z(JssBbZVzEM z?^tvOD-!XEVu--7a5#*HwJ67XF^x{A!_+v2<0`;V2~k$0A}Ur;N+A+4%(UR*nUKhE zEG)&OoLr+wA_&0YC1t@-GB_(NW)IE*i%=m7}kf~#?@O08Alc(taG&}s-Ru2Sc#)k(6Ib2Fak8svg}KqgkQL3y|- zk@`RU2-?Plb-aY0NwfNX`1+P{fk3DH{ z3kuc7>nDHdnnq`pg+DOwWc$l64PTp#w06!k?ftyH?y0`o--U&SZ|%mqv){n_o?{Kg z5o6?;BX4z{+B(wxuB+_a;JF8_=$7W-_*A;#P`qdIrJ;t0H$wil54PQC`}vH+;;*No zx<3Nl$L2-`oA)a_4riAAwrRqcXWn2ybMIH4-Edp`1%2y%5A8YLwWDPrJf9IAs&UtD zefz`m1NjPl*QO&eqtkx9wLN2V-@>!AJ$ZNTIXIqW_|A|sPPPNile_?+;w!|qsu4U*(Y*en{2-Q%Szv;tpocW z|GH&HcWU2-p5|diVB)dfco=O-E5f`N-$XA}f^X4Xq0)K*g6__io;0X4o&$^-7ISg+ f`a`b27ZwbVVt(Dc<4=E<^yRXYRG8m4KK;VKrh#JQ literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png.meta b/Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png.meta new file mode 100644 index 0000000..81b20f3 --- /dev/null +++ b/Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png.meta @@ -0,0 +1,111 @@ +fileFormatVersion: 2 +guid: 303016f61ea9dd642a6b2812b80f428e +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: 1 + 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 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prediction/Rigidbody/Textures/Stamina_Bar.png + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prefabs.meta b/Assets/FishNet/Demos/Prefabs.meta new file mode 100644 index 0000000..c54301c --- /dev/null +++ b/Assets/FishNet/Demos/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 05b745a985085ca4aa6cd3ef0b947167 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab b/Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab new file mode 100644 index 0000000..bd874b2 --- /dev/null +++ b/Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab @@ -0,0 +1,618 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &818862021 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 818862022} + - component: {fileID: 818862024} + - component: {fileID: 818862023} + m_Layer: 5 + m_Name: Logo + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &818862022 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 818862021} + 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_ConstrainProportionsScale: 0 + m_Children: [] + 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: 155.1, y: -52.7} + m_SizeDelta: {x: 298, y: 96} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &818862024 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 818862021} + m_CullTransparentMesh: 1 +--- !u!114 &818862023 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 818862021} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 2800000, guid: 9f392fdb366cd42408f283227989684c, type: 3} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!1 &4288007435728233782 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3414662637240338350} + - component: {fileID: 1957689240189400251} + - component: {fileID: 1987398659} + m_Layer: 5 + m_Name: ServerButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3414662637240338350 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4288007435728233782} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4393252311652982283} + m_Father: {fileID: 4393252310969058990} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 78.1, y: -121.4} + m_SizeDelta: {x: 145, y: 35} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1957689240189400251 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4288007435728233782} + m_CullTransparentMesh: 1 +--- !u!114 &1987398659 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4288007435728233782} + 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, g: 0, b: 0, a: 0.5019608} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 89cc97401eb588a408315f5fa0148929, 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 818862022} + - {fileID: 390329387706649706} + - {fileID: 3414662637240338350} + 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: + _autoStartType: 0 + _stoppedColor: {r: 0, g: 0, b: 0, a: 0.5019608} + _changingColor: {r: 1, g: 1, b: 1, a: 0.5019608} + _startedColor: {r: 0, g: 0.5882353, b: 0.64705884, a: 1} + _serverIndicator: {fileID: 1987398659} + _clientIndicator: {fileID: 1987398657} +--- !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_VertexColorAlwaysGammaSpace: 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 + m_PresetInfoIsWorld: 0 +--- !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 &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: Text + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 3414662637240338350} + 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: 6, y: -6} + m_SizeDelta: {x: -12, y: -12} + 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_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: cc43c1385315d554ba6b0a5cad49aa19, 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_WrapAround: 0 + 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_TargetAssemblyTypeName: + 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 &5478332172321550133 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 390329387706649706} + - component: {fileID: 7515190914058854442} + - component: {fileID: 1987398657} + m_Layer: 5 + m_Name: ClientButton + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &390329387706649706 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5478332172321550133} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 6234007557760571470} + 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: 232.6, y: -121.4} + m_SizeDelta: {x: 145, y: 35} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7515190914058854442 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5478332172321550133} + m_CullTransparentMesh: 1 +--- !u!114 &1987398657 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 5478332172321550133} + 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, g: 0, b: 0, a: 0.5019608} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 89cc97401eb588a408315f5fa0148929, 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 &8529726041794608053 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6234007557760571470} + - component: {fileID: 4349044074868699622} + - component: {fileID: 8105977095617423441} + - component: {fileID: 8525056661963687219} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6234007557760571470 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8529726041794608053} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 390329387706649706} + 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: 6, y: -6} + m_SizeDelta: {x: -12, y: -12} + m_Pivot: {x: 0, y: 1} +--- !u!222 &4349044074868699622 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8529726041794608053} + m_CullTransparentMesh: 0 +--- !u!114 &8105977095617423441 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8529726041794608053} + 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_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 3ae205103debe66408545096e23a3780, 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 &8525056661963687219 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8529726041794608053} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + 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: 8105977095617423441} + m_OnClick: + m_PersistentCalls: + m_Calls: + - m_Target: {fileID: 4393252310969058994} + m_TargetAssemblyTypeName: FishNet.Example.NetworkHudCanvases, FishNet.Demos + 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 diff --git a/Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab.meta b/Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab.meta new file mode 100644 index 0000000..efad8a7 --- /dev/null +++ b/Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0570b6f7f713dc44a90463654bbcd8d0 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prefabs/NetworkHudCanvas.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Prefabs/NetworkManager.prefab b/Assets/FishNet/Demos/Prefabs/NetworkManager.prefab new file mode 100644 index 0000000..fce3e55 --- /dev/null +++ b/Assets/FishNet/Demos/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/Demos/Prefabs/NetworkManager.prefab.meta b/Assets/FishNet/Demos/Prefabs/NetworkManager.prefab.meta new file mode 100644 index 0000000..7562205 --- /dev/null +++ b/Assets/FishNet/Demos/Prefabs/NetworkManager.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 0b650fca685f2eb41a86538aa883e4c1 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Prefabs/NetworkManager.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples).meta b/Assets/FishNet/Demos/SceneManager (Old Examples).meta new file mode 100644 index 0000000..62518d4 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples).meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 20e6b1dc6b2c2aa41a7bab869daa1ce5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials.meta new file mode 100644 index 0000000..9fde7aa --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5985db0dc6b663146adf62e089060cc6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat new file mode 100644 index 0000000..a80eccf --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat @@ -0,0 +1,140 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Black + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0.177 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 0, g: 0, b: 0, a: 0.39215687} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &3427638675963313607 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat.meta new file mode 100644 index 0000000..3921893 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 2e7517d1496ae784f94a2307a88e2bb5 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Black.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat new file mode 100644 index 0000000..a558b4d --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat @@ -0,0 +1,141 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-1287697584379549821 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Blue + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + - SHADOWCASTER + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 0, g: 0.6867471, b: 1, a: 0.39215687} + - _Color: {r: 0, g: 0.6867471, b: 1, a: 0.39215687} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat.meta new file mode 100644 index 0000000..aa5d79f --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: d0a99caade0a68842b2274726d1bc7c3 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Blue.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat new file mode 100644 index 0000000..4c0f717 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat @@ -0,0 +1,140 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Green + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 0, g: 1, b: 0.030314447, a: 0.39215687} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &5270195256977020384 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat.meta new file mode 100644 index 0000000..a9b8726 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: e0dd7b8c357813f4ba3ae9b60783a6cd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Green.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat new file mode 100644 index 0000000..3f29541 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat @@ -0,0 +1,141 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-1102843071779147320 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Red + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: + - _SURFACE_TYPE_TRANSPARENT + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: + - MOTIONVECTORS + - DepthOnly + - SHADOWCASTER + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 1 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _DstBlendAlpha: 10 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 1 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 0 + m_Colors: + - _BaseColor: {r: 1, g: 0.018451946, b: 0, a: 0.39215687} + - _Color: {r: 1, g: 0.018451946, b: 0, a: 0.39215687} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat.meta new file mode 100644 index 0000000..f11f405 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: a35bce0c956282a42a90a04b25492fb6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Materials/Red.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs.meta new file mode 100644 index 0000000..2a9b0a0 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5da04b10582d51f42a06a7be2bffd814 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab new file mode 100644 index 0000000..22f15b2 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab @@ -0,0 +1,368 @@ +%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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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: 6, y: -2, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + 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: 4 + _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: 1, 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: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 5090726670223187108} + - {fileID: 6654616088585099699} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 29 + k__BackingField: 0 + k__BackingField: 12334122808499987737 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 6, y: -2, z: -10} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !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} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + _clientAuthoritative: 1 + _sendToOwner: 1 + _interval: 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/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab.meta new file mode 100644 index 0000000..b6f7022 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: bf5f023b4017a5e41a9815ec5745df3d +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Prefabs/Player.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes.meta new file mode 100644 index 0000000..44c5a86 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2523bec4bc0c4b54898b5ad26591d08c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive.meta new file mode 100644 index 0000000..3ef5016 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ee2b2b6fe8d82c3448ade70b9e30300b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity new file mode 100644 index 0000000..4f8071d --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity @@ -0,0 +1,291 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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: -4.4, z: 6.2} + m_LocalScale: {x: 4, y: 4, z: 4} + m_ConstrainProportionsScale: 0 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3716020219 + SerializedTransformProperties: + Position: {x: -5, y: -4.4, z: 6.2} + Rotation: {x: -0, y: -0, z: -0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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} diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity.meta new file mode 100644 index 0000000..947e373 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: cc489be8bb6ae444283f394c5e5fa8e2 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveConnection.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity new file mode 100644 index 0000000..7729cf1 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity @@ -0,0 +1,291 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3992510446 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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} diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity.meta new file mode 100644 index 0000000..73026f6 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: c43835f124dc68747a2091c4b8c42f80 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveGlobal.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity new file mode 100644 index 0000000..5b5ffa8 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity @@ -0,0 +1,881 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 4890085278179872738, guid: 95f2c6c8cc0aaeb408f24853b464e9d1, type: 2} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1001 &583975432 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408887680269496} + 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: 4393252310969058994, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: _autoStartType + value: 2 + 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 &583975433 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 583975432} + m_PrefabAsset: {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_UseViewFrustumForShadowCasterCull: 1 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1155569020} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 583975433} + 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} + - component: {fileID: 7443408887680269504} + 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: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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 + _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: + _stopSocketsOnThread: 0 + _dontRoute: 0 + _unreliableMtu: 1023 + _ipv4BindAddress: + _enableIpv6: 1 + _ipv6BindAddress: + _port: 7770 + _maximumClients: 4095 + _clientAddress: localhost +--- !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} + _remoteClientTimeout: 2 + _remoteClientTimeoutDuration: 60 + _allowPredictedSpawning: 0 + _reservedObjectIds: 15 + _syncTypeRate: 0.1 + SpawnPacking: + Position: 0 + Rotation: 2 + Scale: 2 + _changeFrameRate: 1 + _frameRate: 9999 + _shareIds: 1 + _startOnHeadless: 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: 0 + Spawns: [] +--- !u!114 &7443408887680269504 +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 + _moveClientObjects: 1 + _setActiveScene: 0 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity.meta new file mode 100644 index 0000000..223322a --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: e7d3ac2d556912042aca9aa1947aea07 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Additive/AdditiveMain.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace.meta new file mode 100644 index 0000000..b86e4a0 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1daac01325da73f41a38e1da80d8bba4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity new file mode 100644 index 0000000..e03a9b6 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity @@ -0,0 +1,666 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1073184439 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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_ConstrainProportionsScale: 0 + 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_ConstrainProportionsScale: 0 + 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_UseViewFrustumForShadowCasterCull: 1 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity.meta new file mode 100644 index 0000000..5f0d63a --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 58053e81b62de3a499afaf0f73d01b01 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceConnection.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity new file mode 100644 index 0000000..2cc0f31 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity @@ -0,0 +1,785 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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_ConstrainProportionsScale: 0 + 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: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 1 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 511705074 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 65535 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3732856310 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 0} + Scale: {x: 0, y: 0, z: 0} + IsValid: 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_UseViewFrustumForShadowCasterCull: 1 + 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_ConstrainProportionsScale: 0 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity.meta new file mode 100644 index 0000000..505b641 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: fddd81e62368fe9448b0eb0a80da6bb4 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceGlobal.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity new file mode 100644 index 0000000..222e4b2 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity @@ -0,0 +1,867 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 4890085278179872738, guid: 8fa881c2b33440b488753a73333b050d, type: 2} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 825925885} + - {fileID: 1406093440} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1001 &506553227 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408887680269496} + 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: 4393252310969058994, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + propertyPath: _autoStartType + value: 2 + 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 &506553228 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 506553227} + m_PrefabAsset: {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_UseViewFrustumForShadowCasterCull: 1 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + 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_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1155569020} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 506553228} + 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: + _refreshDefaultPrefabs: 1 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !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 + _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: + _stopSocketsOnThread: 0 + _dontRoute: 0 + _unreliableMtu: 1023 + _ipv4BindAddress: + _enableIpv6: 1 + _ipv6BindAddress: + _port: 7770 + _maximumClients: 4095 + _clientAddress: localhost +--- !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 + _moveClientObjects: 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: + _updateHostVisibility: 1 + _maximumTimedObserversDuration: 10 + _defaultConditions: [] diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity.meta new file mode 100644 index 0000000..45e2790 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 099398b014b86004abd99a7d21cf417a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMain.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting new file mode 100644 index 0000000..7095b63 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting @@ -0,0 +1,64 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!850595691 &4890085278179872738 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: ReplaceMainSettings + serializedVersion: 4 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_LightmapCompression: 3 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVREnvironmentSampleCount: 500 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentMIS: 0 + m_PVRFilteringMode: 2 + m_PVRDenoiserTypeDirect: 0 + m_PVRDenoiserTypeIndirect: 0 + m_PVRDenoiserTypeAO: 0 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_PVRTiledBaking: 0 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting.meta new file mode 100644 index 0000000..b7a54ea --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 8fa881c2b33440b488753a73333b050d +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 4890085278179872738 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scenes/Replace/ReplaceMainSettings.lighting + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts.meta new file mode 100644 index 0000000..99b53a1 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46f0b48d71e410342872ac48531da35e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs new file mode 100644 index 0000000..77e1e33 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs @@ -0,0 +1,78 @@ +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; + + private void Awake() + { + Debug.Log(transform.position); + } + public override void 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.IsServerStarted)) + { + 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(transform.position + new Vector3(0f, 0.05f, 0f), -Vector3.up); + if (Physics.Raycast(ray, 0.1f + -gravity)) + gravity = 0f; + + /* Moving. */ + Vector3 direction = new( + 0f, + gravity, + ver * _moveRate * Time.deltaTime); + + transform.position += transform.TransformDirection(direction); + transform.Rotate(new(0f, hor * 100f * Time.deltaTime, 0f)); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs.meta new file mode 100644 index 0000000..c1408d6 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 26e4f626a9ca9704f9befe7673a8dd15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/PlayerController.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs new file mode 100644 index 0000000..7bedc8f --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/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(); + + + [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.IsServerStarted) + 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(); + 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() + { + AutomaticallyUnload = _automaticallyUnload, + }; + + //Make scene data. + SceneLoadData sld = new(_scenes); + sld.PreferredActiveScene = new(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/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs.meta new file mode 100644 index 0000000..60c2ea0 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fa23b6e6f9b08d74885e3707aa0d9bc7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneLoaderExample.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs new file mode 100644 index 0000000..397bc3f --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/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.IsServerStarted) + 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() + { + Mode = (_unloadUnused) ? UnloadOptions.ServerUnloadMode.UnloadUnused : UnloadOptions.ServerUnloadMode.KeepUnused + }; + + SceneUnloadData sud = new(_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/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs.meta b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs.meta new file mode 100644 index 0000000..5a11b03 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 84ae572fef1171b41ab287d1c9b5da63 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager (Old Examples)/Scripts/SceneUnloaderExample.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager.meta b/Assets/FishNet/Demos/SceneManager.meta new file mode 100644 index 0000000..2a159c4 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 73b29a5af2f04864699941fc23d5e110 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes.meta new file mode 100644 index 0000000..33b8a63 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 790edab37fb30a74b8e438c04b447d60 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials.meta new file mode 100644 index 0000000..c2893c0 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 726a49d6d35281a4aa8f6015d79246fd +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat new file mode 100644 index 0000000..9c87a73 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Ground + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0.18867919, g: 0, b: 0.12506452, a: 1} + - _Color: {r: 0.18867916, g: 0, b: 0.12506449, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &8207039885153752390 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat.meta new file mode 100644 index 0000000..373b518 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 83bc8ce733133754198df883775fe67b +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Ground.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat new file mode 100644 index 0000000..8d914c0 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: Player + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0.6132076, g: 0.5462585, b: 0.3210662, a: 1} + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &6627004917969117345 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat.meta new file mode 100644 index 0000000..dac5a4c --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: a159d46f2a1d7034382d44be4cf8aa37 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/Player.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat new file mode 100644 index 0000000..4bba063 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-9123596202908848842 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TreeGrowth + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0.20627496, g: 0.7075471, b: 0.09011211, a: 1} + - _Color: {r: 0.20627493, g: 0.7075471, b: 0.09011208, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat.meta new file mode 100644 index 0000000..700b0c0 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 92aa8b0f5f5816f4cae8c6f2091860d4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeGrowth.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat new file mode 100644 index 0000000..06d14d8 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat @@ -0,0 +1,138 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 8 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: TreeStump + m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3} + m_Parent: {fileID: 0} + m_ModifiedSerializedProperties: 0 + m_ValidKeywords: [] + m_InvalidKeywords: [] + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: + RenderType: Opaque + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BaseMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _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} + - _SpecGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_Lightmaps: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_LightmapsInd: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - unity_ShadowMasks: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Ints: [] + m_Floats: + - _AddPrecomputedVelocity: 0 + - _AlphaClip: 0 + - _AlphaToMask: 0 + - _Blend: 0 + - _BlendModePreserveSpecular: 1 + - _BumpScale: 1 + - _ClearCoatMask: 0 + - _ClearCoatSmoothness: 0 + - _Cull: 2 + - _Cutoff: 0.5 + - _DetailAlbedoMapScale: 1 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _DstBlendAlpha: 0 + - _EnvironmentReflections: 1 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _QueueOffset: 0 + - _ReceiveShadows: 1 + - _Smoothness: 0.5 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _SrcBlendAlpha: 1 + - _Surface: 0 + - _UVSec: 0 + - _WorkflowMode: 1 + - _ZWrite: 1 + m_Colors: + - _BaseColor: {r: 0.45283014, g: 0.13425767, b: 0, a: 1} + - _Color: {r: 0.45283008, g: 0.13425764, b: 0, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} + m_BuildTextureStacks: [] + m_AllowLocking: 1 +--- !u!114 &6257638694722121506 +MonoBehaviour: + m_ObjectHideFlags: 11 + 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: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat.meta new file mode 100644 index 0000000..d5d48fa --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 15634b93de15be5419ffed25bc199637 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Materials/TreeStump.mat + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models.meta new file mode 100644 index 0000000..f42a1f8 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 24cb991248d4645448d297cef8db2667 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx new file mode 100644 index 0000000000000000000000000000000000000000..af7a055d8f6515d710febe8d3d4ee8f3f586144a GIT binary patch literal 17116 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 bhrd)gb?X;yf9>SY_rCqx`Zu!=F0cE4i)Wd$ literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx.meta new file mode 100644 index 0000000..4db6583 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx.meta @@ -0,0 +1,104 @@ +fileFormatVersion: 2 +guid: d912959aabd6a3e439552066ae82eb71 +ModelImporter: + serializedVersion: 19301 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 1 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + motionNodeName: + rigImportErrors: + rigImportWarnings: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVPackMargin: 4 + useFileScale: 1 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Models/Tree.fbx + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions.meta new file mode 100644 index 0000000..8605a4f --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa6a1647ec1d24048a454ab29a4ae703 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset new file mode 100644 index 0000000..1fcc5ea --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.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: 7c3e28fa2e37d1d41b4f63c8a0cc2553, type: 3} + m_Name: DistanceCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} + _maximumDistance: 13 + _hideDistancePercent: 0.1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset.meta new file mode 100644 index 0000000..e86d859 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 37bd3d466aaa29b4da784cddaaa365ea +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Observer Conditions/DistanceCondition.asset + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs.meta new file mode 100644 index 0000000..d416963 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a4ba80779d8f594a99ac095c656b95e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab new file mode 100644 index 0000000..1440289 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab @@ -0,0 +1,562 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1814087131438229314 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6217164647077334158} + - component: {fileID: 364480097386140387} + m_Layer: 0 + m_Name: Spot Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6217164647077334158 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1814087131438229314} + m_LocalRotation: {x: 0.5735764, y: 0, z: 0, w: 0.8191521} + m_LocalPosition: {x: 0, y: 3, z: 2} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 6508211851680439895} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 70, y: 0, z: 0} +--- !u!108 &364480097386140387 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1814087131438229314} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 0 + m_Shape: 0 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Intensity: 2.5 + m_Range: 15 + m_SpotAngle: 105 + m_InnerSpotAngle: 1 + m_CookieSize: 10 + m_Shadows: + m_Type: 0 + 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_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!1 &2977802116279124631 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4020278404774456499} + - component: {fileID: 1556184897009707061} + - component: {fileID: 6310157271063819311} + m_Layer: 0 + m_Name: Capsule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4020278404774456499 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2977802116279124631} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 6605224599093577347} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &1556184897009707061 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2977802116279124631} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6310157271063819311 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2977802116279124631} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a159d46f2a1d7034382d44be4cf8aa37, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &6605224598710239337 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6605224598710239336} + - component: {fileID: 6605224598710239381} + - component: {fileID: 6605224598710239382} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6605224598710239336 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224598710239337} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1.5, z: 0.372} + m_LocalScale: {x: 0.75, y: 0.2, z: 0.5} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 6605224599093577347} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &6605224598710239381 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224598710239337} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &6605224598710239382 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224598710239337} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: a159d46f2a1d7034382d44be4cf8aa37, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!1 &6605224599093577351 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6605224599093577347} + - component: {fileID: 8192566354860284824} + - component: {fileID: 6767263130208162619} + - component: {fileID: -1062563901163859699} + - component: {fileID: 6105665237106955699} + - component: {fileID: -7170926880071132319} + m_Layer: 0 + m_Name: Player + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6605224599093577347 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224599093577351} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 20, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 4020278404774456499} + - {fileID: 6605224598710239336} + - {fileID: 6508211851680439895} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &8192566354860284824 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224599093577351} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: 6767263130208162619} + - {fileID: -7170926880071132319} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 13 + k__BackingField: 0 + k__BackingField: 5491988111533709812 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: 0, y: 20, z: 0} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!114 &6767263130208162619 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224599093577351} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c8541d1cca4da7043b84a0d563939dea, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 8192566354860284824} + _networkObjectCache: {fileID: 8192566354860284824} + _ownerObjects: {fileID: 6508211851680439895} + _moveRate: 3 +--- !u!54 &-1062563901163859699 +Rigidbody: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224599093577351} + 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 &6105665237106955699 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224599093577351} + 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 &-7170926880071132319 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6605224599093577351} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a2836e36774ca1c4bbbee976e17b649c, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 1 + _addedNetworkObject: {fileID: 8192566354860284824} + _networkObjectCache: {fileID: 8192566354860284824} + _componentConfiguration: 0 + _synchronizeParent: 0 + _packing: + Position: 1 + Rotation: 1 + Scale: 0 + _interpolation: 2 + _extrapolation: 2 + _enableTeleport: 0 + _teleportThreshold: 0.5 + _clientAuthoritative: 0 + _sendToOwner: 1 + _interval: 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 &7242742394113443571 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6508211851680439895} + m_Layer: 0 + m_Name: OwnerObjects + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &6508211851680439895 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7242742394113443571} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1653722379618799168} + - {fileID: 6217164647077334158} + m_Father: {fileID: 6605224599093577347} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &8611472312372460170 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1653722379618799168} + - component: {fileID: 8497199479931662325} + - component: {fileID: 2671839467167852026} + m_Layer: 0 + m_Name: Camera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1653722379618799168 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8611472312372460170} + m_LocalRotation: {x: 0.5735764, y: -0, z: -0, w: 0.8191521} + m_LocalPosition: {x: 0, y: 5, z: -1} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 6508211851680439895} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 70, y: 0, z: 0} +--- !u!20 &8497199479931662325 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8611472312372460170} + 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: 0 + 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 &2671839467167852026 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8611472312372460170} + m_Enabled: 1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab.meta new file mode 100644 index 0000000..1ef700e --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 6331b3542e64a564c81bc39cedf70c8d +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Prefabs/Player.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt b/Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt new file mode 100644 index 0000000..0b22495 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt @@ -0,0 +1,27 @@ +This demo shows how to use additive scenes per connection, both prewarming scenes on the server +as well hot-loading and unloading them on clients. AOI is separated by scenes and objects in +each scene, as well relative distance to the player. + +Setup: + * Place scenes in build settings, AdditiveScene_Start first, then the remainder scenes. + * Start on AdditiveScene_Start for server and client. + +On server: + * Start/press play and start server. + +On client: + * Start/press play and start client. + + +Notes: + * All scenes are loaded immediately on the server using ServerScenePrewarmer. + When loaded they are marked to not automatically unload, and KeepUnused when unloading. + See ServerScenePrewarmer and LeveLoader notes for more details. + + * The Player script is only used to move the player object on the server. It has no functionality + on the scene management. Waypoint is only used to tell the player where to move. + + * Demo works as clientHost as well server or client only. Load multiple clients to see the + Observer system work on moving objects across different scenes! + + * See NetworkManager > ObserverManager in AdditiveScene_Start to view ObserverConditions. \ No newline at end of file diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt.meta new file mode 100644 index 0000000..b1aac36 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 82ddd0874516b9040b167b083badc176 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/ReadMe.txt + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes.meta new file mode 100644 index 0000000..352f146 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4f574493adcd0c841bc7be2e53b79af8 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.meta new file mode 100644 index 0000000..e8bd9bc --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b626fdcb395bf5a448c33f78241beace +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity new file mode 100644 index 0000000..6d58435 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity @@ -0,0 +1,3549 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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: 112000002, guid: 4bb692eb322f5154c996f4ae7d8d8bb2, type: 2} + m_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &163306474 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 163306475} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &163306475 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 163306474} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 16.431755, y: 0.008353919, z: 925.7227} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &309548902 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149464853042} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 3 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3657235423 + SerializedTransformProperties: + Position: {x: 3.58, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &340750621 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149360249931} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 10 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1072562111 + SerializedTransformProperties: + Position: {x: 4.3, y: 0.5, z: -5.39} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &377764780 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149398439672} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 11 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1072462831 + SerializedTransformProperties: + Position: {x: -3.1, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &599706548 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148679833312} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 2 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 972980223 + SerializedTransformProperties: + Position: {x: -1.85, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &685977916 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148767525992} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 5 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3682402287 + SerializedTransformProperties: + Position: {x: -3.41, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &768984459 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148715813081} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 7 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2607774703 + SerializedTransformProperties: + Position: {x: 3.47, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &774807863 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148720704613} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 9 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4218191839 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: -6.24} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1116894006 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148124831330} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 4 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1535048607 + SerializedTransformProperties: + Position: {x: -1.97, y: 0.5, z: 2.16} + Rotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1139518620 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148146278856} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 1 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1609564143 + SerializedTransformProperties: + Position: {x: 2.0644817, y: 0.5, z: -0.2750473} + Rotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1300759153 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300759154} + - component: {fileID: 1300759155} + m_Layer: 0 + m_Name: Waypoint 0 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1300759154 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1300759153} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1521876172} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1300759155 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1300759153} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 70028e1b1708627408637ab9a3cd6bd4, type: 3} + m_Name: + m_EditorClassIdentifier: + WaypointIndex: 0 +--- !u!1 &1521876168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1521876172} + - component: {fileID: 1521876171} + - component: {fileID: 1521876170} + - component: {fileID: 1521876169} + m_Layer: 0 + m_Name: Platform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1521876169 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + 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 &1521876170 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 83bc8ce733133754198df883775fe67b, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1521876171 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1521876172 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 15, y: 1, z: 15} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300759154} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1538803105 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148545714383} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 12 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3649010607 + SerializedTransformProperties: + Position: {x: 2.88, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1582765032 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148455845556} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 8 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 972947455 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1617785153 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1617785157} + - component: {fileID: 1617785156} + - component: {fileID: 1617785155} + - component: {fileID: 1617785154} + m_Layer: 0 + m_Name: LevelLoader + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1617785154 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1617785153} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3152903151 + SerializedTransformProperties: + Position: {x: -2.5, y: 0, z: 2.5} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1617785155 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1617785153} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 02e5160f1a9b4d047a6cbdb3f094a86d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1617785154} + _networkObjectCache: {fileID: 1617785154} +--- !u!65 &1617785156 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1617785153} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!4 &1617785157 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1617785153} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.5, y: 0, z: 2.5} + m_LocalScale: {x: 20, y: 5, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1796835396 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147729872144} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 1 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 6 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4219437055 + SerializedTransformProperties: + Position: {x: 1.12, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1940549177 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1940549178} + - component: {fileID: 1940549180} + - component: {fileID: 1940549179} + m_Layer: 0 + m_Name: Trees(Networked) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1940549178 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1940549177} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 543450652602714994} + - {fileID: 543450653134138458} + - {fileID: 543450653399068808} + - {fileID: 543450652583335128} + - {fileID: 543450653022639826} + - {fileID: 543450651936747434} + - {fileID: 543450652972982883} + - {fileID: 543450652125852686} + - {fileID: 543450652925478623} + - {fileID: 543450653367867121} + - {fileID: 543450653322206274} + - {fileID: 543450652194710133} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1940549179 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1940549177} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2070839263 + SerializedTransformProperties: + Position: {x: 0, y: 0, z: 0} + Rotation: {x: -0, y: -0, z: -0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1940549180 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1940549177} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b06d5bb8c7e0da04581df0b9454b81b7, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1940549179} + _networkObjectCache: {fileID: 1940549179} + _scenes: [] +--- !u!4 &543450651936747434 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147729872144} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: 1.12, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251231121313} + - {fileID: 3745622626888447183} + m_Father: {fileID: 1940549178} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450652125852686 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148455845556} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252116546565} + - {fileID: 3745622627236409195} + m_Father: {fileID: 1940549178} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450652194710133 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148545714383} + m_LocalRotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + m_LocalPosition: {x: 2.88, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252030116478} + - {fileID: 3745622627167254800} + m_Father: {fileID: 1940549178} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 30, z: 0} +--- !u!4 &543450652583335128 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148124831330} + m_LocalRotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + m_LocalPosition: {x: -1.97, y: 0.5, z: 2.16} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251642795219} + - {fileID: 3745622627315748797} + m_Father: {fileID: 1940549178} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: -75, z: 0} +--- !u!4 &543450652602714994 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148146278856} + m_LocalRotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 2.0644817, y: 0.5, z: -0.2750473} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251624459129} + - {fileID: 3745622627296464919} + m_Father: {fileID: 1940549178} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -50, z: 0} +--- !u!4 &543450652925478623 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148720704613} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: -6.24} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252391687892} + - {fileID: 3745622628047445434} + m_Father: {fileID: 1940549178} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450652972982883 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148715813081} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 3.47, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252393218664} + - {fileID: 3745622627999660294} + m_Father: {fileID: 1940549178} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653022639826 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148767525992} + m_LocalRotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + m_LocalPosition: {x: -3.41, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252346439385} + - {fileID: 3745622627950218679} + m_Father: {fileID: 1940549178} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 100, z: 0} +--- !u!4 &543450653134138458 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148679833312} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: -1.85, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252164172881} + - {fileID: 3745622627838768959} + m_Father: {fileID: 1940549178} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450653322206274 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149398439672} + m_LocalRotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + m_LocalPosition: {x: -3.1, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264253052722249} + - {fileID: 3745622628187260711} + m_Father: {fileID: 1940549178} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 80, z: 0} +--- !u!4 &543450653367867121 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149360249931} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 4.3, y: 0.5, z: -5.39} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264253090158330} + - {fileID: 3745622628141829524} + m_Father: {fileID: 1940549178} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653399068808 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149464853042} + m_LocalRotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + m_LocalPosition: {x: 3.58, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252991336579} + - {fileID: 3745622628110642157} + m_Father: {fileID: 1940549178} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 125, z: 0} +--- !u!1 &832149484401935230 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628141829524} + - component: {fileID: 5334037119529810698} + - component: {fileID: 4539702728046028757} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484422433229 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628187260711} + - component: {fileID: 5334037119567999417} + - component: {fileID: 4539702728024485222} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484504966407 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628110642157} + - component: {fileID: 5334037119632301427} + - component: {fileID: 4539702728081932716} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484744800725 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627838768959} + - component: {fileID: 5334037119921013153} + - component: {fileID: 4539702727810194814} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484780124140 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627999660294} + - component: {fileID: 5334037119956995992} + - component: {fileID: 4539702727635407687} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484836000592 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628047445434} + - component: {fileID: 5334037119963999012} + - component: {fileID: 4539702727616231419} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484864602973 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627950218679} + - component: {fileID: 5334037120010828585} + - component: {fileID: 4539702727720200182} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485284597501 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627296464919} + - component: {fileID: 5334037118315832969} + - component: {fileID: 4539702729415260758} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485295518039 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627315748797} + - component: {fileID: 5334037118292293923} + - component: {fileID: 4539702729434670588} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485643964801 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627236409195} + - component: {fileID: 5334037118623291893} + - component: {fileID: 4539702728952530218} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485684160506 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627167254800} + - component: {fileID: 5334037118713155470} + - component: {fileID: 4539702729017725777} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485958969893 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626888447183} + - component: {fileID: 5334037118971052625} + - component: {fileID: 4539702728738791054} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147729872144 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651936747434} + - component: {fileID: 1796835396} + m_Layer: 0 + m_Name: Tree (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148124831330 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652583335128} + - component: {fileID: 1116894006} + m_Layer: 0 + m_Name: Tree (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148146278856 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652602714994} + - component: {fileID: 1139518620} + m_Layer: 0 + m_Name: Tree + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148455845556 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652125852686} + - component: {fileID: 1582765032} + m_Layer: 0 + m_Name: Tree (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148545714383 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652194710133} + - component: {fileID: 1538803105} + m_Layer: 0 + m_Name: Tree (11) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148679833312 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653134138458} + - component: {fileID: 599706548} + m_Layer: 0 + m_Name: Tree (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148715813081 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652972982883} + - component: {fileID: 768984459} + m_Layer: 0 + m_Name: Tree (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148720704613 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652925478623} + - component: {fileID: 774807863} + m_Layer: 0 + m_Name: Tree (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148767525992 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653022639826} + - component: {fileID: 685977916} + m_Layer: 0 + m_Name: Tree (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149360249931 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653367867121} + - component: {fileID: 340750621} + m_Layer: 0 + m_Name: Tree (9) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149398439672 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653322206274} + - component: {fileID: 377764780} + m_Layer: 0 + m_Name: Tree (10) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149464853042 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653399068808} + - component: {fileID: 309548902} + m_Layer: 0 + m_Name: Tree (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1300264251231121313 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940502980620} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651936747434} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251624459129 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939839093972} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652602714994} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251642795219 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939856405374} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652583335128} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252030116478 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940245046739} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652194710133} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252116546565 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940179063720} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652125852686} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252164172881 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941452647420} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653134138458} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252346439385 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941566583156} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653022639826} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252391687892 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941529198969} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652925478623} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252393218664 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941479450053} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652972982883} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252991336579 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941188040494} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653399068808} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264253052722249 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941132409828} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653322206274} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264253090158330 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941086581079} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653367867121} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1622145354585426575 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940245046739} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145354683260148 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940179063720} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145354991313800 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939839093972} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355006573602 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939856405374} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355401168720 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940502980620} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355822291058 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941188040494} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355857165835 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941086581079} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355877528760 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941132409828} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356450381352 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941566583156} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356503501465 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941479450053} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356555595301 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941529198969} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356598592672 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941452647420} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!4 &3745622626888447183 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485958969893} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651936747434} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627167254800 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485684160506} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652194710133} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627236409195 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485643964801} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652125852686} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627296464919 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485284597501} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652602714994} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627315748797 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485295518039} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652583335128} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627838768959 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484744800725} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653134138458} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627950218679 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484864602973} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653022639826} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627999660294 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484780124140} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652972982883} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628047445434 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484836000592} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652925478623} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628110642157 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484504966407} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653399068808} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628141829524 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484401935230} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653367867121} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628187260711 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484422433229} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653322206274} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4024461403916522246 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941086581079} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461403946326453 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941132409828} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404014310783 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941188040494} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404595316628 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941479450053} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404623274792 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941529198969} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404644479781 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941566583156} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404831967661 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941452647420} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404889068025 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940179063720} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404965824386 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940245046739} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405354447151 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939856405374} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405365341829 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939839093972} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405781502557 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940502980620} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!23 &4539702727616231419 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484836000592} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727635407687 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484780124140} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727720200182 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484864602973} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727810194814 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484744800725} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728024485222 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484422433229} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728046028757 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484401935230} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728081932716 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484504966407} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728738791054 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485958969893} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728952530218 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485643964801} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729017725777 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485684160506} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729415260758 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485284597501} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729434670588 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485295518039} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &5334037118292293923 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485295518039} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118315832969 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485284597501} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118623291893 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485643964801} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118713155470 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485684160506} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118971052625 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485958969893} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119529810698 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484401935230} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119567999417 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484422433229} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119632301427 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484504966407} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119921013153 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484744800725} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119956995992 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484780124140} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119963999012 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484836000592} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037120010828585 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484864602973} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!1 &9013638939839093972 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251624459129} + - component: {fileID: 4024461405365341829} + - component: {fileID: 1622145354991313800} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638939856405374 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251642795219} + - component: {fileID: 4024461405354447151} + - component: {fileID: 1622145355006573602} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940179063720 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252116546565} + - component: {fileID: 4024461404889068025} + - component: {fileID: 1622145354683260148} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940245046739 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252030116478} + - component: {fileID: 4024461404965824386} + - component: {fileID: 1622145354585426575} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940502980620 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251231121313} + - component: {fileID: 4024461405781502557} + - component: {fileID: 1622145355401168720} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941086581079 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264253090158330} + - component: {fileID: 4024461403916522246} + - component: {fileID: 1622145355857165835} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941132409828 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264253052722249} + - component: {fileID: 4024461403946326453} + - component: {fileID: 1622145355877528760} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941188040494 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252991336579} + - component: {fileID: 4024461404014310783} + - component: {fileID: 1622145355822291058} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941452647420 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252164172881} + - component: {fileID: 4024461404831967661} + - component: {fileID: 1622145356598592672} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941479450053 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252393218664} + - component: {fileID: 4024461404595316628} + - component: {fileID: 1622145356503501465} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941529198969 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252391687892} + - component: {fileID: 4024461404623274792} + - component: {fileID: 1622145356555595301} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941566583156 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252346439385} + - component: {fileID: 4024461404644479781} + - component: {fileID: 1622145356450381352} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity.meta new file mode 100644 index 0000000..fa17c23 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 7643cdc3eefb15f4f8608cc633c55f7e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset new file mode 100644 index 0000000000000000000000000000000000000000..ef16e6c6fbe14eb326707c1a03f5fb3be4e23cf6 GIT binary patch literal 20040 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 My)zpexW&`|0^ine4*&oF literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset.meta new file mode 100644 index 0000000..72de6c9 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 4bb692eb322f5154c996f4ae7d8d8bb2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 6475696996188705980 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/LightingData.asset + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr new file mode 100644 index 0000000000000000000000000000000000000000..c5ff2d190856d5ad354281d58b126a873d80201e GIT binary patch literal 138601 zcmeFZXIK+!6fPQi@4ZTu77*#8NDEcz2?kK4iENLLgUkQzXG5kx@&73m04 z6;K2bwn(ueIcwtH_nvdlkMsLH&s`}@CNmQ<`QC4>^{#hK=z)V2lqeL+-P@Hw@bn9U z1;4XF!6;2wXMm!BXT*@d$iw7+TVwmbtwFh;^A9-h85DH(9N~XiMh-yj;Tr6E;w-`A z+y#`|xr-`iQS7kxKO@0^5&dd0d4#JQ1-K*a};CVC8enOKj!f2}{0b zo6D8h{`<(bH8+zh$<9RnNS736XJ=;G+S;1`HCX!RpU{@}Il_gR_0SprmbvB`xcRsE z!_!Q2wg1dSv%kO7%=E-WGh!1H6OK;4{?$zrj=t67j=oJ3KGhRGO^%i0@YLj34UZ-t zUC30p&3kwJ{5|_t%e5b?Q(JAE6r5dOrkrz4z%_ z)F>UQA=LXl9cq;B#(ff{;RZMA2&`o5vii!#woAcgwM$Lv8u~%%y1twKK@YlYDhYw`&DvJcY}*0q&r5E*Zs;x;(^t>y4&|j9J(8ZBwO8$AkzPD|2Lh1 z?JN@g5h@Y|{Cw9@q)saoDQ$z)#kN7ZzDBZIXB}K4S*~rcq4oxK8#?u<(zK}Aj*!@n zP({K3gVBZBiaY{??d_1(mW9^)R2XST-dm`RT~{7UQyaT>WXwWqj8*MDv)UMw+S6C) z`aX0~vTRW@x*ndBq@wA?qMi$%rxSKH;&zSackSg?L!ta!j(5r{%ev6U$5~K5GkA`o z5$4lkvSds_iOG7`;vJrL@-hmdxp)I=g_s19ar!G$_qI@bfLZ2y{PDOJK9q2Z}n2W9*zlLUH;&r&(>lFSyQmLlIcb)je z!I!bhb^u>;tIn6Zsgq{>i~NpgOkW3G&c)k(Pf{|K7*isdC6?~yT*2XZnB{Mk5pNY8 zu&|fERYbZcqq{>Ix<{%(afqJ&P9pt&OFH+i=lhd)PkcJoJ6bwAg{y4)@g-^arki!g zxa*E79S|V2{X#81uJmu7uAeVI$;X5*$!&U|N5|Xo1-Il}IYu0pulc!~@)7mX;&jr9 zN0qO`aM@7mb-oN}$3cAk1it>&8(QZZr}ad1cFceMs;=a2;FagE zJH}f-(ewhf+Ohhma*Vv2+0to=4=K@$^Hi;^Rdq6zIGm9)G5H~mM+e6f^g3KnsFEls z$TKa}a+X+PXJ=h6d@b~_NLp{#Jn|YOdWETv&_S_7+%cXmB`meQNO~Y}~BUtf5JmP0EWs-Y-V*-U>>% zlaE;}Rd(u8vzIHXv*e>`J;l4Y^Dun2!<&!E&vCl4udzyg^r3xDU*iK@((9XdZ*wIz zX%`wXVazy85>y&>#*0{oISloTL^!yDFqVVthqaFQ2?}e|Ceh}o6%tdtTNPtuo0)r+ zJ@`jArxmzI@TE77o%L0$xioA~T>f&t?ZF9LpOJ8h4bey4!dSNPMX?;kiZ`2~UbME9 z&4@&9`l&G2^Pk}Ek{9NZ=lk?ZWN9_cIen*L`)!`)==v&N=URvGF3-<25-W21^v|qD z54~m5y(s@`1@UXIFhqc8i>$W=bz3qwaz1=T|Nu}jihucIdG54IM{wyANEA{97Qp_^lAX`3X^f*0F zYe;zEmvQzJSp8ErJQrZpuqS#G(%^@Vk3-_09s~rpZe^GjlLxd+8L|%Wp|%*PqkZ>o zzLlj+Svz5J;I@XnF$@c2gse?D2sC(2&rOGihy1%u85S@wMns^JgTMqrDu4!HgA3MW z(8t5@B9wc3W90<}UL)8*M*c^`AXRTNFp$B&7W@|k{vxRkQfD(bFW-4LI?e@GC4I8Ul0xFi0NoljuHq_fr=>(c1(W)>93hNACkb zuxi0-Zi0eBL_rSv_;?uGJ}^-*#;*VvQVmTE4J54-mwS!<$m87rPF0J)hlhvPi-9~X zE3WRhalxVMGnFlb2dQ&H$cf;!&AxUCR<(o>R#(<%?!!9kXkV_EA3kgNRZTfKI0?nX zP#SyDFw-7q6k=f#WD(}mPNg28zs7Jgy>`SvB0jk%l~)Sw{m8oKI=<;MZyo0=hk&L; z`CB#e88)2dIeo=gBP@54=*@6H%l+M5+kFz}sne;}*>wEaz4rxVwXg?>>m~D0nnrUO zl!Ei4*osoED<~K~s>Lh3kT5*Xbym!hKV zgYQ zp=cO?bdsX+aaN!(G`_E77irRZYQ;2+178;?p_2SiS*=E z#FIb20Z}s%q$l6L4gP7>u7ATTPj!sDwSIz*q!hr9Zz}9#P*|(!n*OccNl?O{q?PIk zjS=pf-tb?2tn-D=d9SC_>}425>h^N^#?jxnb=teQoh4`1N0pz)FN)(!dXn+`$j?ms zj4xA_TtgS1qV=g+t)V`5=}ooEBgH_sT-tg=#RA{`IhVRFt!ik(8&uT3FPnx3nxLnh<()N3$Q0a|V5|7}!JW>+x|S3ttKJlj9p-{0^1+_jY;6u;0)(53~C1J5I`4r||G zeSSoPwXk(9R4A?R-}es-HmItq2B`+VMu0guIJgJc(j}3WNF)->W+XKHT?S+gS|P{+ zl>H%P4bK%8iolo6xUFHh^^JJV&{!M*1G7uogK~^y3Zd`CTwkH}gS14zJYwoBf}=x2 zQw|NDfS5*kp+=3h)T-2~sRkK_r(s&=nM3m6$eN$q_aalpTuzFdxu;6Tbr13hojE0R zs<}P%Vv&QaW!B5lklPym*Q&2ozsO@2^K#ib8#6E<8XB-wS4`{0rCi>d+@ox-ShUi@ z^eQln;N1K`G0TZ(+^w-Pd9+#~=Pdp<=a^f#pVjeXDs~P-j85LdS{1urK)6sWdpW(N z4ZDV*n4j8_wFQbs?~5yfqC)YBxAQTt2VDByY2Eo%58jvaKcLw2bn>KP+)~{i-_-|K zD|5!rx24+3_NWV%7OKTb=_`u=GpoZwv9faASVCV!LJk|Hve~8b$)ymyk7sAug2hGK z0Hyj`mbX#u!_pm87w|&PwHhb!iZt_gZpzH^zAo_JvDWxwtwEx6=+xX`8KhY$i`(}| z?s41!eeDWso0H}HtK=CfE4S_}N09E3z~e-aNQVJInCkq$KiLz~3<IX7ec1FQH%D&r^T#&dj+p)K_KRk!WOuGzYvb7C z*aHW&?~(r3#RIkG0henp*Gh&FqYav@W3I9fvA+FEe|I7J7$NbU+V=KphIXtWgf1b-kW*+>Xz@OC=8R42w5AA)L*ulErpj%W zfr|)4YBgD&xd1=_D_YjGet@Ep)-Ql z93UZ1XqQCVy9~hCLUC|6z%sa@WfCCdH%UT}W1N1Sem$cp?`z)A?cMM9$KoX9hEIOggiB;mBPpxdyKRh-K$>_&NEh zKT`KQL>cN;4ObK#g{*~XkSP_@nHOmb`IENGaTjhTVa2DtDKthmYYpmr)#{G%F~#tn zq*^V7>Dji#Rz3`y93@O-ptaRe&h+G>PR!J!{y<;ACT7N-x6n_`%;8P*F-c2MFPX8x z3}qg9ON=j-@fJ;dTpD`*VJ9R6)bv?qE7_*X8J|$xXjj*R#=H)0JxrOXz+XQrnH*9SU1;r{Ri;;e`{60# zQt%}=C{MrZWRB3)StS+`2fp%fn@kvjfFL@bl0-jZhCEp`0<_I6n0>3pkWZ&lM!=Q-Worrf#|?pl$m zN1jcLzkYXeiwFANeEVib&-&w&Ip^==&QvXv4^_p)yk+$|w2u?!Z5Mag@DJBDE z^;V!(X})1imB9I&mjLFksRyB&I_s?<&{J5KI+sh!T+OaozVq}?~Dm$SG6oqLQcIsm|#Jq3HWpQlo#7r1$( zf8^uGB%%Bxs{va?)adR$I+zBRyNO@ zzMzqseVE;!}RYeH~j7f>DXl~-R1f{9>brVdu zHimOlEH_pSK_jDH%Q%w zsYASfKe|#ITN|qsd+s{3?!};Ww%w!shF^bwqWdEDXOU?_w8Qn)Df+6X`wD5YIX7xA zKB_f!dd22&BJs(^m>*)TwQKNZp~#BKhQvQ2+(m3P>=D#nwL8fdrEoqmJt6vXP6n)< z2^%S`q2Ci=kMD)2VB{dbe>-6rmd2;qtgI}{%D9rxe!?z>kdR3TXZ)+I)P1QF*ns8fV!q|9ys;vxx1q6J5?R8Oji0s|cJIE2ohMigo>9U z$5;x{GVs2($Q^KB1A_^Mg~PM6ZxD?53GF^Pu=|8`06FV$>Pd%&?)}L}7IaB_B+^UL zX2i?Qa1Q!dgTsWmy1b3u-Q6EBu=~z*AkJr|#ox(Y!yZ@=53lJr@c+R&=ACTB#cact zzXqDxFF*#z!#L2c;n!!74#S>dF*sYA{=->x+Wv3rQvyN)4M*zpv~9Uxjs}$4X2q$F zdkr+@XT0=q70!{yEF8HblhtPn=gfa9Mv~G{JW7=!%fRbnoCPf_lMpLYK65^g zz)db?A?5vU@iZ)K;JeBj>_zL2r8gxQym1XsA^NPAmY=Vv#F<$?tv>FE$)K6leq&3y@X>(%>hAjb1s4P454lH9dW*5p zu$0lH(hM@?=XKs3?uxQWW){W=x--RHL#yDQZuasD@0=E`eD=2Tb01NZ$jF=bDaJd? z)DbgEYb%?ZAw$I~5vP}l>XYzZ$-)Sj7U3jEDGI$9UnyG)e^f|kHGAt4DnRi2UgSi?57dLRI;)Y)_Mfj8oZJyTh!dp~#r+;HIRCis7oEbV zQ`@w0whB2$ry;B$gG!flkAyHmjwIdRLr)^szx?SSwLE$Ei^b?A9|cY8gJE^jF8!|$ z4!)EIJ!oPNnt$m^Er%74Kh?y$R?+26O1Y=a!){;C&zpvwNDey@C9-tdd6y-EM9tN= z$Aa?@uc7*^WZD#!(|GC%rQ(&GZ5kh-zP+?h=4;-c2iU3y!qcCx>w06lZbo9W1& zp4yv|j`^@n#d8HNM4y{Dbv=8a;3_vHdR{%C$j-2Ixw88BQRQ$s%&f>TuWT{FC?Mzx z&4LImse7B~7^owkZ*}b0F~n(DS(z6(X&6KDGs6auLVQogZG}+3nR-=E1XPk5XL={r zt7ilpiikEkWK&dB)X~nivLyST*W25z039N*x|zft2?)ETP7HuefV&Ab{atE z9u`4@(?0}=dPO2ZlqU?o#Y-e2Adwe7Jo?Fy!vfgyK z5kG!Mj$t&QM268*PV3TCgCF5y{UKpwn|gdc*ULq+^C?k))92pn#Rksjh4+>!CUt=G zuvXaXu8&>}0?eGMY8gp4M}nIb1Aabx^Xy~8__)p~J9u)eWc6cX4mQkC zjOw2ho}B+qU=LlOmXM%-jbV;Y6tYAJIf9y&zp(IOVPV0F!jB(;@)HeJiyUWl%{z00 zH!SSTja+zo687corC-lG>?^sY61y=OBb>&?I*Q_-Fx2V8oQB|gBuC1xA2SVibSWpw z@^H8EtTBFPIz~AcML;WcxQ2YDeq&ENkxzN6DN2n;|6CVg*l7c9Hx++yd3tjbf6%Tx zkM`*?SpCBE*{O;FUF1~ecRd^(%=Qs9nx~d&v$zlZlaR7f1^oD_jVh= z_iPTWlf==SnF-$#w%9sYJD5L5CGm39L~-sUC~6&jA$cPqG~q4W2I=-h`c3SwH`Cc% zr@!||^6JL3&(98A75Ndd@aNgjq|o%bMNYk?J`U$tUhDX6u@34++pfh;zlPicvVcf7(E9fqNrm-b>E#or}R1jt)0YmxJ7?i8)c!^M+dNq11NI+P>1kEV}~v z!AN_{2?CIn1w>HJpxYZqGd+24FNTuT{Z#@_K=cEMu6g>j5x; z5%J%lFr+-Zu0Ag*u^UZ7!a6X_s-umfc8-;m9(=%q2Y|r2nHCd&NI*_OSa?_@ei_0t z*VnUatD&I?LV|@XV*-a57PL6=ej@Sj>t1V$q&*892CCqhx7mU=*K;%>M2NNQ%o(rc zGiMANf(jdFXIF*No=5BgIJRJ}+9d5h>ZCS9-vfsFJLxxoS+qZ>qt*UA$PW;GS z8M6tXUbyZUlfgoz5-OHxo`q94ps~eTU~!a9^lAbD-zmbW+3Bn$BI)Tl^#lSg zq0V>lMz3=O&FXhfQwvk1Qkqh|$ih%lQF=u^Gw6*B2sk;Q^t3@pty-aboD=3nJm4-I5+xsM5&mk_3_n?a+BsB(*1qk(Z@Ku@}sT>Y-WXO?C zG)TN9hAy&TPvT5MnVJ^q{gPs*q@=c{PGl(L;KGPwiu8;yMf!qgN>RZ!(brO*UhXk_ zti_Z_?z_Y`;!xI|8#trdiy8h+blJM z^gq9Sn&+FEoL8NCk|Wu99eeeyTOoBx>Byb(UoT)kwEN{-pk>F2oUju);a0eLG25DE zw}&-1udBbTxp!JmHzV|MPI!}fHKk*Y2T6zw>GcI(Mit}}X}n(O4*{AQSL z)Y5Y3a$6{|sDgN_f*A7}OSNN%b8Jv zBV%lyQhY0a`A{T%q8Zr>?ILKqGSj>^BT%n;^q$qw6r%wE$qpO`1Zlcn0~ut?A=_JvyZSFONn`^>CFyr6?VN3EPU@Wyf8Wc!k>jrQ|-v?{QJVL=kA8_M3=AT$@}GV ztsgb?*!JN!uYc04wq>lg;oipBjBuJ@%tXXLVPn%1=?jab(jqeta;$t}GndlQQO4WO zI|dxs&znoLOmV2wGJxw8UbYP{+u=goi9YXAb(OlK^Y(`HI;FFV#ShLYeaAM2=?&Z| zYnxvMMRnE|_zjCc)L#s~7y8ONPxE%rRKETRbSY`;$?g{tREdU`ku>Ph-f0pk|39}x zB6#2 z98WG9|5>=Rp&f8|gA}zD&Gr0S!b=00ZHc-Cmh1HUMA;qm6~4#3ii;^(ib#PS{gK7SidLmj=mb{y1Xkn(R@9!z;8h!qXFoDEV-#KAL2CiI1z2$4uTGw$vH_T`Xo zOO<2ScnXC4dVWM{BiZQ4I1FPhjLv?YvZcRPAVKTq>1abZ-@qcxHkO zcW9#%C^?Liu{KYm>Wq`+Wk>WNGm^uOQ~Q(?$8D@J8EVc+YEi*S_ibg7+I)BW?YRq|dVYH#>Ur=gDr%y{O-#zx8$?qhWnB^*BoOak4`I{qRFx$IkeNDa}y{ znwu1Sd8a#5!W9~s6)S5B3_sXi>b$md+PShbZO7UEIP1$PWu3uIil6EcREK>&zoo@} zskuU>`FOgTN`CjGV`pqrS3pzbkEX8iAG87z^?LhWa_`dpDf+xZ%lA0<`|!T=8XeZ+ z^0-x<6H!_3uy0U9C-WmcU-Guno;MkSq3(Xa#s9&KPzzylavjP)>oY<^PEJk^0z%Dg z?QTO=2@^0n*{aIcs*qH|PS?}pD3To2-By5MTd(Sm6ehpsr0YerxDj zS+CB_efLm%`(7}pDZvMXLWQY4lm6i`fjNT|CbRgHmO00Z(L=K496+U6AbzJr z&fGTdr|H$JS1)^dA^>Y@ES3YpAnb@GSJ~L-3)tXbP4rH7Y@whbC%1T_8-Fa@vz7RL ziMDKFb@clZT{m>ui}>TklX%24Vk^uq&2Fm&Uoc*NwxET?j_G&pU6QKQ*W%e! z_$B<*^ri!%rk?H}i8eN*KHkr+!Y}IO5)CB}w0|}3d(I>G8wA$S$n$~} zFEEB9vjSejAJseCDEMl{EB42R#;1sJHBo^mnwuW2F3#PE$*iC+;b1q^)2EM2R_9oG z9h+IfAYCM!3@_t;Q*L%z9_LzT znlkuiZ8^=?L8I`NNrA{%Nn}(IWt^KYxlX-)Q6`+PK92a73+Dq|(C$4F**y);Q%Z9V z?KzNM>3#v4?i(pybAe=7RXAZikge*;>|mf9#XWDN#o&5TC!EbJ<_U7se^>Bj{>yOq zG}~2%5O2tObT67*J~%0mOY0dmx-b{DRhZny;j=LAD0$k{$#lka2J^*qmot;zjFK5g zw_cEG%KDO`LQHar`bxogqeye~F>l>|vAz)|@0`ZemHY)!0~G^Kw}Tf$ojE*5vx|>{ zV}J_BS}G`XCg*1EBXv`dR5o~c_#+eYeUsxXIt^BV&0mu(0P$&m(X^E_01xOffs zSNZ~C8jkL}2WB+|v@Z-W@;=|rEHAG^egVi6Xz(Y@jn9NmtWC9e0zKB=LV#?=R3#-w zZfXjIJ23Wp-xsVAv{Mo4z-KVwjA32l3TSwMF1VB0yL`r`7SgB?!LPt8ARcLQ6{)4h z1U`hlUnpH8%Xg3@X$kV94`lA(cO-pMRxy$R83HAM*uwFgLi z4D?S;y@s5K+v4W7P&R{k!oRxu0gh!F3bH&R|7~q7eM+bel^uW|<`d*rmozA)aE&rV z1-M{``EK#3gJ+-|DYS#vrkyg%Qpyfds;J`5!bmNAQ!1zyxbYC;YsyE*CKq9w*yZNRrLK#4vE1(lKxbXh(7LMoH;^Fq5FPl{ia@HIs0Y z@Q%jNw^Dhg;Kx%GOOhRiv8YWx*0^fv+=g657n)c64kgL>$<~u6Q40O74?ReojHFAR zR}}nF2Tq&6c_WV~_N>`N?+=I999-=f!p#feM3>9Um#~*|Y)GW<_s9wcWeWCBk@N}R ziXd6~Lwa)N()Ta^r1eLne<)7>{&~8ikvjQT{4$a@W0y|@u zbx(hJrzanD`oi~)PDQgD{R#dx1&1FgnYOk*R2Cj|`gmde<iJJpTvVWM?{baV#^si3q?qz@eA?Ikls;;gaquu6h z)rFsA`;DaV%WE*xuK|z{C?P=~d_aSzA7E*qmN{41+EhJvoJ!vDUu24)Q1%#*0v~`rNHf`IYQTLNV1>NO z{TuNi=Z6vKZgWWZerlN~jU36BfnpZqiy|Vj2(seILMA5#clS`3Pk{0JaRuUAB;ajs zu0Qk-_}KV(WIGV#8E6vbnkEfGv?NpnIfN{jZ&f_qKZaoeJ}qA&L>cn}?k-_@Q6IhN zYVhGZ2})>{$F$&UG9OyMbi`^sz$(vm-M~`GdehpBFc&q`3OEW7=3nIwn_q0EIzCK4 zc0Oh_>AHY1wOj8s+SKQiHhr}5CP%zI2x`jOtjzI`hF?A`%nFK;(JzdtD|}j~pJ8q7 zVK1v9&&WKtFhoqKB#Qo7SouU(QY?{UYNVZ>Y(qh$KJgFQ(w(I8gu z{N}S`uNe4_F;?TED=*WOf>5q=_7#=qQgkIxbWmjRyqp%JC1qBZF5IjNN>=2;8J(jq z6jaNXLw3PZ`H1)GhQl8kpSL{NCIj;xL)^IVgQ_(WklN(b2oe2|^P(rP>OjP@?$0yd zfHhjWso%fvBincSrhtJO=C|<&4CGtc2dRDri+0fYAzo+xV;hTe%xQ{&ZIQ>Ud#tZ0 zKF#l*peJl3Kc>tTK$^DW?;vdNPb9G32N_$A|*Hub%FkCC* zirRhl(2&>qxyyxsy%DD2y+x6jowGt`6ItzzvTcn=C%N%|IE|V(+eaGFh9d54BS>w2wh_0dPzn@(XOs1;|+G!x|Sp;GBSdwQ0HK3sBZ>8 zG!1;pZiyTloW^EVuNTuV=9-$wnpF|7I%MXUX@fkp(C#ewo$len#^Jh7uYi+_Wi*3S zz0qfm%s(u+?Be}GS;|H5CZ`HNmogi#TzC(fh1YTMRirnL^C8weYP5o^P zOYxeVhwz>h?YhS5&CV|zR+do9%x~5`<`bD}EDfp#V`ViDP;B!v2`kI0p#`Z?!_o{A zoRluZye1UNAD-!D&ujn9!%Vf5f(02;sftp0a$O44xV@`viXumIu0RQ8$mVw+k+y={ zyx2zUXvQb(byGGmeVPJ&lxiRyV<0uEhZ5R2MkwVm>4}_rELDzgK}1*R&RgNmOH*=9 zQKA=3C7t3lfvy$IRdfTbT+vcWq-sj;MZO4K$}j6H% zvH^7f^?54@aM=0`p#GTLeNgxq=L;U+x%OaxpsL1anyVrU(Z@KtRSxKa4#w`A-4?`8 z$PoPX-!UMFFnfd0)7k*3Cd8f9 zH29@J&7=>Xj+b8MiaoFP{~RDpXJiCGHRA*>c3gx$K6-ezxp)0q^)5U*0ivY}Bju=j$!5~n|ZNl?#=KJ1oecJBlB!e?rK0_J| zg>nWW_mQ$k^d(F8Kurg8x=smukIsAKauq3e1kz3 z@fGnEk`gxK(S<3pZg~DRHjp|P1Q^c8)*icRY${#D_EG#t!lG`{1jv5*dY)V_P!NYcEy4YcZf21#`{rBd9N+)#_Sjp{7JT0tt{d%X1r$>>xtAVZ>dz*VTqV1H}7) zaRdDL?L6lAF=WSsH~S6h<_I?uQXbi*<3TIwD|jRgYk`%NNcQN{~8(9%?rfNl|tn)T=9?PGica zxo_}ZkBT-rM$b!2Ntvq9FfK~@ejI8)uNqz!?OjVD@tlb%SGg8t+sn&|c6F(9r(s1q zxa^ZJDwn@ijI*#O79kb-r1B+-^W{7eILZR1*Lvm2Y4X%+FSUfhCs83u8i*tgVs43Z zVn$Jxb#bvN{CG|{W`-9pS7Lx3_z!0k_=qP>sUZ_dCcX z6MbMIG=3Fi1`wcy-XfmuQCqx)SU@#&0r7z3RHf~&;M*P)+7%Uch(`PDxWugxPM|E1 zAyqxo)}{&M1uRZA7-1K0lCuPtc3V@k`f9@f0Y{9{9+C2PeafW}* zSo|R&92DGt4f==-B<~|Z-K>)a(jx>F>}ihi_GaXtob0nZi-jHwMBIy@aD7^k16EHr zBZ`ROa*+Qb1~S-0@7Eg1a^R!j>|F4l1D%|xhgw)(k?}Hvh^45yo!~(mOCBRFd&Q?; zEqIIyPAa~dhzj)P;~jtTX8a^H{E(kkn{+fPO2^d&i*1s5(=#P*iA^rhVX3W0IFL#Y z2M~Q0100%KdMg2u(`u%Hc6hgUx7~c&Gy1e=DXzye?p3<<#sQv! zJB?f&)L-G$yo^FM>kO;)ta6yMx63@~<5U@p%_TFtWRf?TNhlRNvd&Qv`pek0RV4w6Y z=`Fc|C|^l*}pK=dAPf!#-#I-!BF^ z?ivSVr^oV^V@^E#JkG8_l&HpVp^<9bcR?5OQX=FS0UZ4BchbaHiN8o2KDDTjJh!g7V(>oM2_gxb(Eg!I+j zfo`KREHrbg)FEv|+R&6_D_4dcPMc}g1{QmLnr8-tazPCZXQy7TdU*;Q51X7_)znUf zhPm=d5HKLM)u3DgAqa`#$Sdy9F6%!WCIk!JxmlO~!Yf!?aE1pVj`FWTPew{dS+Bvfd78OhmM}*5pGDj|{)8x+9le zHRB}U1VU2lQ6H%W`Vz=#gy*{7fwlcc5} zL>Z3;g+uIoT_xzj#ei75A7vL!Wl-AcVv0|!EBfjVA)K({;x%-bJJTzEe3IKMZuq#c z6CleL-QgLxShPC+pooz{Hu_c~4}G?qyY2pTNxf($y=c@^>U8K;WiGOj&%i`3z~0KW zAYuq}r=d80^ReC`CdZQn2W!?FY3-+&@XWc2khn4BwyMlBJuLnHh}O#H6i$>%rt%yV zg7&x4NtZ(D#NvwWG))QU;moYHh(;lH3gpa&wEP|FJ-4lXhav5Eip!*#SwU#&NkKz% zP_|X=iplwQV%X>L zjcS*^SGRi597_7R!$t$z-(wEQ|5VbvijyKQLNw=R4RC=)cwt;p-HF`ie&KR zcCa`YVjz+ZfmX}Pma{yTa!AXa+78mR07mXMMNoyf2k5^8v%?^2G7$OzD;@gJg9mA; z;D;cRkp%7X2+jhL#A)9}Hb>wB0D>Mqq@&_BfD7r#yA>jCIS0;XeWZ!}$=X7i!pYlmfk$eYGl&+q?U)M?&BtTmbFmJ#zl;q_Zdl`z~1W7k| zllWz5en3edK*aZXK;ed=OD7q&M!Jo_NyWm^ z?O(j;>q7*pjNo^mmk7vgA)2HQD|ckfMsI7ZLw8=AHX?=V@A_znNi9vh!^$aSoFu@^ zb?{&+PqKFv%>J_qimy@v!zUl|KR)<-x*AvF15KpF?&-Vt?OyxbinZZQ%+wZNPi6NH zh(UDO|G5*41!%9;^wzv1C)JMl3ri8sa$(hrK-mpG#mQ(C+{v-H##Sf3>g#WxkDq){ znNtI;L3ZTERDHF0DXCObvyrDt+dZ$z`}c8&Pwl3Ufnj7{EU3k!1|B%VKi2hM7|Dar+1gh*a(N8FcxHq!C$5s^{D7<7I?dfX$ z@MJmuLDd%!DUwK}J21P+oPeK3d;-egDomB49d5@aB$_%ub#~u0cE*RDt;+eNFsk8k zj^4sUhPwVMBrr)nqikqaj@LcC~uH#(n z=Bql=w7|E(rhNI4wMS!$WVrqx@$jRcMO*7W&&MhyfNCzjTJ+ERcg{L{2GE8i(AihY zL)W(&t5e9c@oNXeGtOa8lzQ+Tw9q0V?I8$L)B>HF(1(z3XN9zma=)(VCWZ$hLz@f) zXcU4$JvIwLMgv@pWv(44GEm&WRQlotGUFn>sX<65QPL{(BY+r z^el~dlq4#@GcWoYBR`E7WHP3}r8%l8srOR%Q{Jcaq8OueM-}52jA-)FJaCDE*g9Wz zw_JJN2610%#`5nvM&`t#YM_?Vh>Z06KRh)*s23%!h6)DtSq`UZns=6d?)3nML9xgq zQBvm6D2ho^i?meU@4_RmOuoDo7%M+6eh{;jow$^4QvRpPq>a$rG8Y#0P_F^0H2V(Of zl(MMeC@BMKvm%^C5-q2W7;B2MwY5!LGTNo^a;I7*--{|mcX40TSI@2+)y+G;C^R`^ zc{}ri;@t9D4bBLAk?#8CN6#&#aTjT?E7*oNw*7JPrn7$yEw>fesmTRM0LE#(oOt(cvE|?Gp}hh68(wb% zqQK*Tf3hqqmV-%q2GH5ucG%A^bbXqA8lo`~QytRMA*x*nizP47b-uXH-)Xg8S9WeAP6D|)6-WFp|%Rb zrYy)Gb4yEurte_?VE?mc5LAJ!DVi`iDsp$ou0ctw%&<8M|y~GuiY{XUvsB#yGfXWVqlmfM9+*dGLwfx zD%b=P7)7y%7YmL*vObB8I<#U2-^*KG|{$A#UKJ z2M|GLA~<O4L0^k^q| zoYOoUSHBZxgn;2$otp_=e=yUg865PwVil*3bY&kyu5$qWaNW@F0}co2_F0^QzRD(~ z6#=GPxG+e2<^9mLYFM0Wm(;NDg+{RR70}_c3eE*Oe2{j8vNF5TF{E4D!t`$O`8lUH z$am)2oBx5!CUA)6lWFNOV%Ko=ZK?>o}Z016HUIQy^QjN879q)&l#{-_lRzF!OB zGMPlT-N%6FY3|yrAmo^2qh!iot`MlgK4>kqF8B8KM3C|F!`3IL@?O2#{|oRB`+E>x zAo^?=>31y|K2*q?XLi3`AAxvtj=upg^w+-WjJG3BC#fpeoHMonie%(HN^R>wIx)+y zECodwe)@h!0gzX72=X0<6hkQh+XsxAM`vN21>-GYWhpe$*n=;5agsG|khgXTdzE;d z4QJ#-lo!Cg%*_KK8GXS)xV8Z|W+X8kx!l3sMOJDhiI`!jND0g>Z6i+Pf(HGJ{PdQL z`3(lk6kAaTE1}aAbkra14C14qqv-hj!>%k!cOKqA*QMl>_@)H3<4y`b2*Q46QIL4y zte32#VmJ_abQnm|U7#;cG0N;KZ zo1m0P98-o{E*-C;58AdDvEveusob%IXU3IX<2O4`{$4`a}s}$HR&9roZ1&o$mWX z>kLgxkJzhh7hmBy)_a^H8_pe$Vnf$M9>=(J(^2{+{=g0A->B{M(*bHV9gQqf`8P#B zmekO^s}6Lee2C3a$L3H9R>)W20(c{yMD0tjQ8qwM*grEO&4+^lKHv^g$v6l`Jc!+x zBTg&FQrZq_S=c59YM0)>#le0UgrT47EVb5J>%-$n{MPfxO$t`v(U4`=PPt z<5&;~1cs-DV#}9Od!GRT7W%J}U^%vcAVlS|2#J0?Y-gw%6a+kVUS1=@jv+*IVl0Gd zP#n{T%jr155Ac%o&&;WIvO*|3VQgzN3~9N}dbx&po9SkxH1)Tb1ofxT^*DeHfUDFB zM6cXsh_iSDE(!kKhvfdBI28b%4E3f1w1-Ob122$T4#a63nrcZz{*e$r$Aztg!F^ic zZDmieI~VT$5BA=~9m+p^107~C#?DxiWh^n4>>*{}Q8BpIUR1As+41}7x#f2q84)$k3g&$Ni6zCLpp)$&O)QDqTu4fY%GM`Y z%_hl}%I4+ElapCkgqbf!>xGm~I)*3$iL{#B;pICTlj6v(fPBAl#=Y{Egpga;a#$g5wI2R>7Vde;(qk^=0(WS%9>X)P4)Y z$GefhWMp|egrAyvXKI*Wh@Tt4r=;L*Q}7%EoHcTpjd13NvdcQq(o`*QAd~^FX=-^mZU! zMP=c(+rj-W=TL2H;`Xu1IZAOT87S8Lkj79|bb)Fh=QJ|D4;phdX*CmjMS)rX&qtmF zU^oo#0$^l-Y<^9vPECE)Oj%heKB1y;&DKT^jAPD0KWBFtCFvg(#@SEKLdX1W$;p2p z3kZV+pI#1O(`!gzfHCa{lxdw!SGXV0_=}W7eL`D7R4VPEF%tGjF9}8DYa1@*`EkUo z))BpOi-_3IFkTFr0u1a0UOup1{a!5~6st$o@iNM4~i{@)2TvyUHPxF=l=mIf7;Y z9MbD6qs(ROQrxYvQJu^RG0>K0V&eCYt>yP8*zt#GO{;1%D=?c`ldvn0bI3ZV@v_2M zZs{@m45%O znWJUX$ac^U0da$%8LIVM`)**+bU0TYFq^A0H?x6&A;ObcW(310*zy)7H%B3_S$NIN z>Ha(UpTCngPyIMQ4LCTSG3ACTp<5hKORL?w`s3P66Vv+k$Lm#ZR;d)Kwj%fGjz?Qv-@-0Fuu9bq?@S z|Iy*{uG#J`1L97i3KWYbkXr&T11Wb+T3QN z;Ei@M+)P#}07kb!4;T8t#4-lS?!w6Dl~P6aE? z!46fhl@v>*XP)sfz2+|Ql?g{?##4Z0!)I${HrNSLZV2qg&9hxhT!)KilSQIvzC^J9 z>3RlJhwQo3pSP>oR-xliZQhwLPa{ujpRlC46|>LcrQw{33^a7y=2l}aK4O@eLn6cB zWUdr@78Z{QWV38xZlqUFy15En<=E=FH==81CjLdWiZJuvrIYdR*Ae_Jmb0cBPHSZ837cee{0&c()3Zf2SnY zME?=}XY)&7jD~JrG1 zNKIDGiEA+{NZx%nD2&*kvHg z;Bt^p9+w@`4rO!UGvbx9CV&%&pr+hO6}CmdpU4z=5PUY;*YZ0#H3V*@HGnw==Tw98dcq+2o@jsSc!(Yg#MI8s){ z=B+W9cxHtiOj^ca)+NObQf5HQ*>%~-g(JR~uABlLiaz(L5SGO|)oygphQP8EWo{vN zVR<&$ck)<8?ek2zO&u%TN2^+*2Ldw46`Y8GlfiW6xeIMbclAJr8%u_B|BvW>qqNZj z;8rXkTmZMC(*pnB@2?>rjg}wSYV>SEX_g) zuLGuz32j73xn3$ytrbvo;ck-?A)I`nR_;vBs&WP_`wnMNCq*P`gjW4|^zO{jaDqir%QW+8ytDcRVF zwq8cCbNJ^fKr?lMQW69=ctu7=bLux@l5IQ@V4-`SKCP_)Q4ruf1EdZ0X~6n| z{NCg?G~e|BI&|%uYg~|+I2}J;bZ2NhY&+tIrrq=jMS=qi&6{od=_N?j4lYIqjwcr88o`t^J9Y&g?tLV4qzD;epdodj={TdhL95bxn1=CY-?uG816! zAjq5vI1peVJj|0Z3l+D(PZKThox)kAp2lWmG~bdwgtii_Q@R#GNBVL2FmnBG3=5u)0`P!SEQZ&GDB#C)r(ams^U zr&j0h+%MP|f7Il5Q9uoDG&v?Kuk7v%w9xwhvj! zB(wAsP6?~NqPe2(%juDzQY1N)Y##e1g>4{(ts#8dir7`wn@p8-zcA5jtaZj!LjfzB z{zyD8{5=QnMFL;(pAuxdB@?z|HM{j^jlNMfCo7ZQnQR9-4(}_IO+zscW9$OiklPXB z>tXytLtWc7K4cKjFWyB#M_uoCk*6K_L;!*#V^~yBBE9JP+T4 zg`6{lRgJ!Y<$O@&Ot5x9AWleOkjRJWhU@Va*h?_wixFk9@s-whQrxHv?B_h-^a)8P4)Hw% z!GO?CpJZ_%EtZcgyO{0(Cd|{H@;seG9E**K#RPj=c@lvSipV$>K*rgIdMG09$_{?8 zy{hC|RS8R^@j|t)r zQJuol&A6~WMgdwxaib805YETA$CyTc`4BFU<;fqi_vd`blFFn^F92BtvVV8XO-w5g zLxrHbpXDcPt5*N)fe`joZUngk+C^@V9lVj&GLcOxEjH)ply#8WR4N~(8%^W{LeMnH zKPo*-Hu^cfRX&=?)Wp_=0WpGOy((42hI$I-AH>E{aC;InfSGTIzv^06&_~{i`M8Tg z#?kv7)OfGzfYJ6kn#=R+2<29|nO1hZzx`qEnMyZ|PVSlOB@f9Z&Fn3OTErl{%#_IgBR1bz8hM|5x{3z}}tq ze1Y#qJ=$u>YxTU&D!w)0DnJ?pi9?O z=;ED&QT#LU0d?%?gr7OnWHS4@0wB)Y2-(tJZ30KqcMjzazcm4!i5QaqdpyoXCipK3 z?aT)Fdm5B&`C}-y=!Ts$@|fq#v-1z>j10T^)d2+%)uHbE?yFmmhG@x!mcQ_V6EH|R;b zff;w`kdu0N-&Wyd2(0I!c_VifqCE>Jw;7m9XOztT$-eoiD+fhu1Ybt2ESnMPAsag@ znb8v}ZpjDsw*qF^ha6XSF)BNN$w86qPwCDZ*~;g3^2BL~(ryg{TNY%nDh0wx$ys-zpwvwArMPt3~&4#6#6;t+x7tqpo{T zc%693URzBpK+>a^2ckXbO^`DQU*$L__(y3)B|^9ZM08#h?Niu74OeiX5M9FM*B0-- z?|PcSPHQ%$CZ{_a1hn1XsK|u@N_Wd?X!ZOC-H86Vy3ou@suZyL{>im{~CN1nxRVOrbITJbPO7~U6HzLu&lrdt9Z zSpBE=-hH4cg=_(|W%{7!fl_gZa!^iyicgFZ?HalpS|a_AGn2e|VY7D4+nG2`vBAHe zgx$PT+$4*ddj+0tgJ1xJ0!pWjv(tBrBQ>ws^9-f4N;UMvBc)S% zhr?U93bXns)&bEiHWZ{tH>H(0gF$U$Fsi{{L8>%T#AuB|^f!J|8_9v_Z~-Ix_55gR zta_M-YD#ZsIMfs1&kvky;Twj4O@|_O68Z}FeURon2kxtL@hqv5acTYD2s(fa41Z9A z2=?`m;gR4Ia%<0c;Q_0I8a7D)EN5s}X%z539A1f(%q&?FC4l=PngGrEoMZ;&8b}@P zW}iN-oNmFZY?0)xo5ml592tFW^9B3)?cC8XBN?78$w5RW$y+U^^ssmyq7v;+dKPlp zYwaA+wRuhO)35;9(@u9DH&Pc9*1sw&hEp#re8J7ajV+J4AgC*##Umo-{Q#H1G#o7> zA->RHULzLg3)i({ zI5GEzg=$J(GpEG4wcyoQ_~`YY(ph$6?r9Olz7y(Ye3$UJfMfKdxjdzj~bU3pH%YCD}iYB%Xh^W8c@HMOXveo_hu}B84IB;UA=m9cLytUQGhwvn zk9#JY5_;~EsSPhH(P?C4-nB-1E}0Ws;QT2Bg->r`or9GR`vs+cQh0%y64-%n5S zc)zb2#S)r(`>_2#uCDrX2h{DSPHyQ~wLPYmursUAM3fU^sU+`!8=^NF=0Um&akUX+ zyWdxFu{;vIfhO{7jyX5Ga{kTD`8TsX(vYOE6V?rwp_Z+rnKSU{a`cQ-Zhch{WBnfhUI~z4L{Xjo!XHuRVq(Gos6EBmw zmorV+{Jlay@VnLX0d26^5`^I9!^9xQgrngO@agWLPmIqm8Bz%I^MR=9cXGSFsD*L< zpr2}LtQ0so!wuDCFh&7rR>%lkgc}zjFWUnwJ5g|GnX%s%Z6$GqX{064aRUP)F}zx& zG+XD|nj8*!JBG{asrY3mY2~npsveKXka9iCyhdsek+U3DM%D=a(3HQYB3%Zj4z!J_ zwX-}iHjL6CzKbQv+cgg&N@R^wi`zNr^ZFNz<~2?0fBkpov}>MRCM8YI45%APLXxJl z=Oiz5a+-p<=J0oR7Y>Ddy3BRuD%Z8EoM{hYu~cz(HhEnQt;)`BDf(1C{k;_ZQ6_zQ zx|<_Y#-l#%BragolrWbdSY}F|OW+;ilyTs;DHYGlv>4vSnk*77dC*hJ*I#vf$l@zK zt+!qnZtHX7yl81SpX1S?^&wK5u|{h_T&ywHxEQr(C8nu^;8R6(#6-4fX?VDLwz_(@ z*Qd6+TC6Rm{qE+eKRX zdAc25WnB!yY97DJO#A?)p3+wD;LFI{1oD`VM@NPqSiB!{U+w->#4q-@F(>Ws{sNf< zGA)MIYcgX?0d>$g;5xzdkQ&Odwi4U)#A$mQSWW42W(~?V zuvzEH;VROl6Jh&?6*IF@9{@lbSSeJ}`oZO`AoqEJQ8u^$DdN+cA8h%Hb@Sws9DZ^p zn92YT2@EZz_q{?pu^X=#IV}>WwQTw6{G9BZ%%K3s!*H_avskcjVepVhaip`SV8r*9#rWncFyTvBSXjgq zSees^0?_91ZwS=$9urdT^ohT$z{Hsvyd&9w#$xoy#GjdBHjJrA(lR01$l}71C7VZgMo{_p*Fkm3D>TS#wkeLWac<~;`aq`Yh66X?BF@AWPU z+`LvhNl=<={_gT!bu%cNyY*JL-jQNX38;g~NWdln;?LQSf6X67DSo_p5@r-p&7shF zjL}Cko-<&Ar_ZFg+If#@HKd5UHuiQ+P7FH_u{b}26aL3fYE|*=dZETbMMICu$SZH1 z@66r*_*42UUGP!mo2S${a&a|P+ntu21D&%~2?jw8fS@pjr+`Y%saC4Y?VmZ;mSyFl zM2SDv7P@(}T#0?6xuSb*nNn{5995(3a!#!aP%O$(x2ouF*nhkn(-SZNXadF@p~rHc zC;OhzmIic|qhSA>8Cg)|;_&cD*|9cseoEP~JMOjRaQ7eREX@`C`@kCxl+LOcrfNOqy94Z((j->I7818XI0or$`E@fvBz6oMI_bM z)}{=OLd9l=UN`#9fb)k&N2CwczG=}j3vf`#OS6$HDdVe{-8d+xEGc0m{f$GNoSv6! zAy?*D@sIA!nG>*E zrTo0Gnrv4t8_b3ZDRj3>&HrI}@qu&d??>{nLsaJ6r@w{Pz3h_+i%I;#{R z)DnNBXm2&>b6*mIIYn2uvd<0O`VoJ_L-5>U)i1$+ggy}d3jR?Pwzy|s7ExreZMU4y$0X(gfHLVs>7{BIMzmEq>9o&StK_quv0BHKYdK|2*K&57C)K%L zUcH#j*L(0-)3qkM-#zcz?N&ehCLG`L(lPwHThUzqjs;3`TPbyv^zVn*vy%eviR(3D+QH^G{<^a z)RmRMadB)baC31o4z5LjXW*8>B%lAGgNB`)jeXuf<6W@dsvL$vR1(mMN|hXmvoDii z@QRI#5@8OV$O+Ev8*n*Gu&AgFMl3K`I2At90E8%jHQm~qT0DyijyhNDKw2AWibJpG zU!&X$giFwk*K>@Hu-)T??&FoMeX5+X5~%MB;5;%M%q=m-A=r@l zZ?lR*9$Liw!bTP`qK97#vUqx;14J zQSrRkP2WK*$D*W!Z+NJpCycDlayvM7xWX(ffDdW;q}r75SA0H9@tVoGFt!8QeYLf< z75<*H^x=GZR@5ZDAQL7&yee`!9`k?|CND@S`xmq1D-r#RP0|Em$eI$dts#)dwd@K< zG|VROClP@Hzl2%qGF6LA{b_C2AB~jLT%r60m@dQYa~Dhs+3?D9V$YxQiRWc#BqS#5QkC}}bXE!V5dzfT<;F2dn+`!6nmJW4 z!@<#}!dW57k;OqN*Q~;`ilQg;jCGil0Lnc*)<{iGKkM`11~sxWNG5dAs~h6m!I>!M z*BU9t_uMg>xgUhhdnHt_#{pCv!dR>3qSC+mKcXMzo?EKJW;nl+692T!e0@Zsik^2b zx318cs_jg(iFQP3l?#S%3;e6Jz?Hai16rV#1gFWY!uOa$Ros>bRg zee3oHvI(n4c0LJ!uJ7Fah&sH)ub*lcu3ltq_Md?k+>TXSe_dKeorC>}8Gv9E%CUa4N<-~ zh^FxXh59-H-0syIgkW--jZ6tR46qZ5w>lx-=r*Vq4N>uN7T{OG0BPVls3KG5nx7Xy zwGws`@EHjSW`1gkgI)4|AOmLvGTMiGEPM!@_CE1IL`8c$liToqH;%EHT79ceFYc^T z1V@ec!=_SLUXna2?n1UxKgT#@o0H-*8r{u{Ki4(ff z1#yFLO7E*=f{}+x7j4L+Nw{VpB`oFmm)Ac!SWXpJM-XQ0l`5w1xCqAE$g7YP4`-N= zjKz|00cMWRNZ>eDx_qB?#m~gsmc?_pZ+^WkGi-9VVAqeLGeaG-`%*Ymt3gZKwSCLk zEo&~3NGbc<)S=f~b)o9_1UF05U*KvCQ>_!>{~Ey8r0Umc>1-DT8ED z7m8u)dH>&3QuS%!1=+PFhKHyH57B*}`va%2Egv!K;Md|)#ivT|@n3#q%{DXbUcCR| z+_OD(vj2X#F?~Gdc-&7MRjtCywcTvOD%GsI7W z^MRieDiYkfl?Rm5lP3cPA1C-h80EqE?l@qD;24CmLcgZ4`EaHm^lTB%E()Ks2X-5C z_%P8&Cl-V;A`HQ;)CdQ$^c+jHW=r@IdxRjS3w~3$5}=Lb+;S~#N=o>v_pYD{e^A<+ z4fik$6jI*WH=?3YLqTcS!p5sh5VE_+2e5(ZKsYaPs6fI9=%9+DNe-IJUw|{{0$jXp z{+=E%dS{b}m|6p*B&D!<8=~yss*oNZ$YAdVp*P-qej!{}I+^;3 z9=bMgXh?%F@RdVY9;0e?K-;-FHJ;Ax6>=awVf|?t2+uQ#A%HW@-V1B~iWnk}s zO5jJPQ-31>HwQ2ZJWEQ##J&SZrG&etKSZ~@_#S=!%f|taAGAd&m!++ncMysTg^@bL z<+|TN^vEDc8el%FaBesOhN`9z8>&(4+$&MM1PoSzzUIFe4u*8uXm6s|}7_?9zncC@t+yIT?2DAoX|Ags=Kw<`-<=h<5$spQB3`>YWknMccrhx5$ z^_WY$A;vevSioD=Xh4z$$nqdLadClRi*Ha=^=3ctn+MAQ^)uo>h-BYDCO1 z=sXw$g>D46$azgw0NVk`6a$U|CTB<;`*1D02=8a+&v(iAcC8T9W3(E`P;x!0|?RYDt8ZtXkZ@nQky z#5t2^PKcE`l-XLv*qTMSnLbNQ+HMpfCNrFvVQCfPW+pHKuQ&2jJ6#a_xIANyte!k&uK7 zvnt~US-0d+4aLvMOV|z&^4fK*w%2c~*D+6cg3DVIzJy!H{p(r08ebIdxaPEYb81i5 zcoXqlh5R~hontBP`t5}K;elVekCaq*2Nk(36*=Cqr!@e_BmpR;&QOK^;8LBMB6SCp zYz7S%KXY(i5)xIJ$#!eny|TN{CM2OvE@J24`OvL>M@w(C+x-x@D#sD-OQnfA)5L4S zVVXj*q?Oy!);dThgsUS~Gu*p>17Y;Fy0n+*X)n>E%E|rDoE@D_G+o!)Et@P>EGJX* zky7xF|QZh^AE?rEdXT|)Wsla+|QUvI0vx+B#|4dH9i2Q zQ(Z6JV3ppa^mjFYrbc<($vFsU@*u2%triV#1R)tTuLOCGx!F&5A({9}C@%-UU^A*U z9f9bib~e{xEM0E$hOv0VXtz;3yu1lU83M1lNlO#b<)(3P4nFGneVQ7agRu&OzM6JsR<4RIer?T?gs7xRy)^YI?hUtj@u0lQ%hKniT**U zX^crCP!}h{q#iJ2kyMi+n25M})4tnBKtKBgEcJ@#VPVI|pb~cz&^w4-(LWA3KFY4Z zs1ZI%2_ho|=Xvxj!vwqx*h=s|c&J4OZ2Ru@gg8+2C@CT+Uj?yUD8jEogA1TeuyG7^ z4#&oKMwc*kM$2e2`(dGMl@Pz1HJ!lufHf7u{y}@-mYQ^%lNEZClpP1+JMr#g?!2gJ z@@-4*nNe~@a)qD}Wu>#nx>yAmHHySNR+o`Nz_5VXCbP@H-ozf3xMr6W>w~p02EX$v zT2ngO)Hv3p{9}AN>u&fdxIs23mlZ{u;>;70?H$UJgcJk!h8X+Sxt1jwRQ<>e&!s|B zz9-k*q`yH8vJnpG&^)s&E49NMX1026UCb@(paMUovO&K@fGN}u^>nsm6Fx8pTlUiB zQ-I0Cq9jO!TH8SGcV$)%cg;oR+w06v|9YzZOI|fP_`5EW`BHB9?c8ty0~j4zXm`Nn zfYJ5)N*;^Cf>yOO8^By4B2SSmY)@&}dLZ0Hr7W#u-#w*g<+($$lI_~~9LPHt_eV}@ z%ah0PUpt!kmVd?`$o-1Tn0`OYPqlLh0NnvWNVS@H&Y#B}s9MuURGZ1?s^)q1hHG{7 zg;%dR8wbFLG3)tom;vIi;e^Xe&ZmN}d4J>$nLb%&J)PS;`DM-BIeIfC`7&fS*_E_a zDt+O{qT$a2g(IVpFa!=AupKjUW&+yOCz?i`c7tk-iq&=CKUV^s^Epf?9ff2-Yv9NU zpl?o8odT2^ilBho04pR6t)tsP=@ZBgG!Hj3TmgEC78=+X5vqH=%wf*g`-GhaGN8FR zMAN^GHc*rUi{7~d(-x8RCpdvYB%20)KvOe!lf`iBOE3(OwP76H$B$n}TJQDKgM$x@ z3-Sm1H9@uz1yX~^G_FM9Gi;;qH-PV!jufGg7%+%2f(ZTRpHRmvLC#81oLodHn+GfWAyX0ZEvs3m2B0{|SG>=CWgbs`1_MGRmh zsU~i4mruOoc=Lm;`-FWCrk(q~b4OiW;HrDIy`8jw5@`i=oq23`S!)-{CPb}hS(X&T zpiuV1nR|9cwR-daR@>u%=*z~`0l_N?R=DfVW89RR1{F477S03&rv!hY=YTS!z zd0^^P)bASub%A}0&5O6Ej&{9^zmHq0W4SSPC?(wPch}Lk{O1-ger5MacxB=|Jm%%{ ztjPNgc!86i%nFb?i<9p0ru3ZWt?FLq`J-9m6%UAo6#T#duZphC;@SOtElCIXe#xv| znl^a2P+EtRP~X_L?Yr8Fat~biE4h~N$8t$)8=xo1!WJX4aiSZH8 zzK50{MF|Bicf(8|Ho55pOB|LfW8!=64*l|L$AGYdo)!G2rES~E*?3bZ$F}J+3?&`7 zp)Ih^;X&Y@9UnQIWA|xG6y*h5bUUgSyQk)CTM&pJ7w(CW{?>V%`?`ykJKQP zgU~`}9GAbnoS%l722T<|zRB<7Cno^Sh7EOyRT>+9{{MPui`F{)kJb| zFp2K#1f&ANwL<{gYn0pzA&}%nM&k$IF8w>WxEf$K=vQDo)YQ}f=`+aGRH5608LV4P(PSz@@^D&wzljevIG6oCPtx1qg8pq_XE@)F6m= z^tZCl?p2;ij`nxg_N~TQ=AQAOPVaH}8j->5SUQXp&N#E=GXaSlB~$947R|4~!~#=t zW|NA&Wry*QJ`g@jWP0h8Gl{!SG13rBG!mR1DyRfW0S=g=jlqSu(uGplIhlB&tc50+ zGCg47Vme%;ZVV%|2LzeS8A1a1r_|Ze!@?CS5+$9EWqkN7g+wvHq4isqGRet|;GfAx z@q!P6K{+J&nZ7b3%o@9P?a(R5_@i+-(F&~|cf2PnTKQ8z-rAL^H+k;>u^#G6UV#}O z?+x!?r*oe_4|JQ_4$Af7G6uINN(}lJtA|%tZGJ0#cKhXcuart^#iYpHhuH*XQ6!bX zYsEG1f(1LyuXn;Ph|^91g|aaaDHCz~&~f0)*SsP8@r;k_qw~#k<+Ol85FDclViI8l z>iYVPq;*55FK>TDnWS87tvWSf^6uWm85U(Xx}j_1Lm?2G6LD=E$X>bo3P$TlF1Nq? z)$x@~)u^N;Bb5OPB0V|(xqkoA@dKo*Z+|?8%oZiY5Z1$Xj>7sm*w4qe!m?&4$Dpy( zz~~$q2C1Xk8YmM1gSojGnaN!f>CJ8WcOmpZ3%}*T1E5fEz8gF{`=;&x;nlaD9|pte z(dZpCdUqRT8kfVnC_n`%Jk|=E!FvC7Y1A5+L)e63Ry#iNIEr2t*ge4#JcIG1=gd zq~}xbrQthv(Ovv-N05m8zJVPlj(D_e-6z>`>GVQ|Y~@jYN0MxM;!4v6VEpUnuaIPh zhb7X1F#~x4>3RWfF51gBT9KYJxjSTwuE14-Epy(NXsM;g0C=0Mz=Q}lJ@X$%)1?`M0v zAD8PAN0^HvkDAB@6!EteU7vbJ{|qODDQ<>mU_)l4ys!oUaSz-0<`x|gKzehx$peO= zF%I)!#L9=esz48O6D%0WeIW-(1U(i$C^VtQT1gAI0MLs;^9InGnoAkkIG!ceOu$`j zEjnNyNaGpRS7DS5inq5z=uYik+b@6Y|qXO4(Z8H zUA8h=k4$V}ngEE!KwEzoj86kEAcMv#)-XaPGzBUX&}wUEVfGV*GXgQ;*UcJ5IU{AP zJ2qr;2QT+^0{Dbff#wiGG^EH51Tw&tf_g4IVWP;mC|W(rPs73IKIGDWh(cG`ypHn%QNe zHaUK1B?w!s3>YO#rSV(knq8LaH|K}FSjMW@x7-dCnHAI>qSd4N=miBZBvdrolm$B1 z!dlA5O5kT7XN(RtW=H9Qaa5=G03fZ{U@wIc&vI0AbNFffpoL7K@W~?;vS=7RQy$^j zb;Qn}9fZm3P(pARar-@=9V%DUNQyR7RaIp&aBm19Ak}4+lw9vImOvrshcK62Vu9{~ z;|KhS6OaMW?o}++G2gCOqa{Z*LkHswy>g+Ewz#FP@m{pK^SxXE`b!$N zVv=(f0GTIc{N!y6_p8@D`+S?YREmKOda%J4@L|$5A#W$WgauQgEK-uKqs7KSg*iSF zKJl!0tQtL{3IYqpztSc5I@_IBYQ|5NahpH>VykXh;R>pHXY{o>ve5T{1xOI|dcVk1 zt{2Q}JvB-0?P>_&^a|ZWe=x2x*UsWr)l2m!yenL#Vg)HK5y{cO4ZR_?`Qmvs?T>#w zBhLN<9fgz7O+azdCdTlkDj!(SzJ}gc~TpM^6G3 z+3jFo&0q?!l_F6TA0I^57j3+4QnFceUn9f3Kxrc#a)MbHt$~WNARky%bmc!2L0N!T z&dVT;O}i2x0}ui^vIIW|=q`+((BH&4_`pdzzyF|yNG0^;a$frCz_Ug))&ECNW+DoI zzhxT?7y#Sw0gVCEC4@!U!rm3Kt8uS*9vB2j`)^gWF;QaN+N+#yD?mXKVFkO^V3rOD zt|RWw$uU&!Ndw#p6qFcZO^Ui;Ua;Bk=2K1wB-X=cBEL*{VN?x>o=wNfL~EA;p`Si-76j&qPtRoSS$fqAKiV}Li3V(eOx+*VAUOO)dW zLS_V2`t#w8JxCODLlj1#0Jrk6+|4-0LU(2~t_)nm)ydsf#H$25FY_;iOV!F9URLhx zCvhLTj&~i8)>FITzk6lN%9e5d#&~iajFk~|p(xZYmizz)|IsG-zaVLuo1yn@4U>2k znrTJKuyZjBRy44O@_@ea4|p51?{VwW{(`?$Eg4g>W1Ra5_EyyV8UTV*z26jdh9|%n{9tbqUw{ZG2aPdk+F!xr75LlhBfIuv@eH=7m$-*Eb6r|42S9 zX1in761MtU$PHjd0}5pxz#{l>*PMv0%n{+qndAgQmPqVCb{jEhIwze?Kw@dw0WdXJEMi_<|uB1J>a^S?!D=ZB=6ux@w7yR*~OH?-^D4z@d} zp@%n=twKW$K=@)AG}O&=fOBlHo1bU`i(LZ|$E)#Mf@iZJ_w zV}j8&p535#W(?WH9$?EjtF?jPFkqd+K*N{{@Lrf8GpTP?Y@Cx}r&$gRP!-UQDm4X9ZJ}>L#J4F}KGtDHsw6sQGqg zgMvTnlJ|#qJI^uQ|2}>a>g*0n zs3rk;lyX0Wi4Q{vbnESU@9Zwv&urJ;vikjvCs9bzx7Mh+?RKHS!GV;Nt1$P)E+wVq z9^%6&oV#a2oue()G0FYOJ}>hVqc~7W4gW*~coW?`B__{pym7KhfhF(=%#C^RvY!15 z5u}d^`>3}uxxySN@lTbgh5W@+Cr3SgQ5OmYe%9sxTBTAgqnw?4dwajkXW~5Os2+MV z9#Cq_k<0GuQd?yt0Wh)*My7+rQJ@Ua!_p@9fv79WJ)OV45)}pUB+w8Gf;1cINw2hF z)hLSW%!oUTHIIJEYh2|c$C3NU>ZxNxd|=*V1%$+1{7uG2B{BRH%;7Fy!fjs7^^rlewkt( zXu{kcC77KGpsV%w!pb#QS-Hac-Vft zgyQ7PzRrgWY(a(3GezB9H6@uyBd$J?ARMM#Bx?$+p$_?&RyqZMvMtVit0Q z^RWEDN7>ZDy3S^ZyBsDJ8E*E2-czrsE0WSF^T0qf;w|UBcCi+SF+f=XMmez45H)`u zDYHWK)Yk{m!1#Dyq#qKdc$^Dc^hApW=)AlEs@2ywW&?odp5jcE!2=>bUd9TA7(6|r z;El5G>VWE=h7M$=S588C5~%u5Qz1Tq2&KBZ2AzDaAGG;y9~_W@_@6<^P44vur$XlF zWkB}Ov{;?Zmv3om9?l#bKd6b+!@w#M{G34KvL5QFnwr|5{1{VdfpdaTwH2*Og2}y} z%p}K2X7Unzl|80MK)by0N_O=tD4JfF(a1z|`;^mUtWA(JLBMrHinMY>-^p2Zfn19K zn%#E;n$fTJW-FgXj1p12C6L%)ZLc9L@kPYq2mb_d6HJm(gG)!AC1EFingZC|!t|XR zt|krs7Ha^M&Lp-;nPZ4Uq?qv?bj38>gLY7q@$h$@3vu`7gPF`tK#7#l63{j^ zdB7}ywNM8CwUeQ9ro{vP%e6V0)>{XkKcP`DI7EUpH)gghbM*F1(ve?znJ;OTEmvwT zrEV0P7RrVyUS_yqW@gz;H=I3(^4_Da%`qvAd?The?{aT7%Yd*T@Y+2ZY#}c>;;zI2 zlVCF37RAp0#qPQ9&pB7OZ?w>DsmW=ya4T&TW;Lr)x8;u7lOr<%%4Jk^5e{+;0x&vdTdbc3X9d9zu5lYO0zt&d8(Mo zqc4V}zAUH-{wxWkKfRd8#BpYjHncUOF4v8@WV9@(aK}a9hJ@Vl3c>S*4tOwk|N0%G z=AQ6kXlq^LIQFybb>?{IOVn@z2hjU5zrMr$*&d!=eQ9Utt{>d%foY;W*Uomri$*P_LO4ElX#Oc`emzDp4hUp#x_$6S@atXn zc{_PZmWT-!d~rf(Jh38MR_S5HZ;zeLlZQb~W6obE_HNn>FNA9UkymKqA1J{zHf|`# zRzX~MTfR}0U;);9SUHu{s3+d4By8^eEsP?_GW{*2|0JU1i-8Rt_kVqYy+FdmH=*n;a+wXeWKhgCEePmS8Fg#*Bs2cxmC>6NE~R0 zg}1T6wX6?13tzst%)N=5HT?-I>w9C77iqYXY;lQW<%ygR@SG3WQp?#=AM?5^?}z}0 zqZiSALqhytocooh z-`=`Z9k80V)pdF`Zs4@niD#YWy)n<|TlTvWRq2Y^OlDh|gD36Q1R|M(Jsqgw#0jIK zZ)vB#spXIy1H z#6)@?o!&0hYj3>*bI9t9|MNR33hK)6U|%iw+t`t)ACuYP#<@jFYF6KlvL0v`z1vyk zzb%4$pYfN;catxq)OGRGWB5|nt&&VHOYxDDmxPT66!d&q|Hd9O@~(~fZos$Cn3xH^ zhi`3L5bLMdZ2B=*cJCbN1F>~Ni~GNXlOn@q&djQx0$Rz@B}%#l|D%J)^u%DV204d3 z`KoOeUqKOO)83Xw4avQtjgos_@u2x8Euf9k-~Y1zO#!J#DqRLPW3<{(0K|ypHMHWS zffe6o%My_{+2ITPm3n#$n=fB8O<;7cKve>r3PL|>;cxL zy|a>FN^xPk$5!E#m~YU{-ehU8g*o3b-qVSF%3upN1eC=s-XAaAn4#JEd>a=sqs4N; zA}eB7*s;gZC_gBiQv5Bv-GiJ08B#UI%)}jI9t_8c*ujsa!vp_O^$~UBm=^p-R`BTVi%s&k%Q$PV}So>uN zbV@ZT3kHI+OlG%<-z&HQjJdEDly?f>q9JlY@op|##_|)AuUr*kBbMo%jmWkkr zAb1M~Y2TKh3G@yhV=X~J6XRn|%{4VaaM;OO%O=TY$!44JTa%Jxv#m&3X8cLA{MK+& z7TriPBV}3r_mOBuqEd@(#5{#0nmtvvm>}ptA~_UQ*b6Gy z6Jz*^BqqF39En&=taL2H=i<$zc%zMS$+-mCn*XsFCQLsTgCX0nFz&z`n~QiQh=)Fu ziowffC(9PwC={nCG!CkD4JvnYYBfGqd(Wly-cGCAPHQAh>p9%st@VD7?sB@p@^0Pb zG=t>~1Nip+Q{DG_=9i!7j%28fJl4i$=q^7t7@=>!Mc2jB4gNmWUF0@+A7!w}jahz* zS*9DT^Jp!@3I7`2`qyCj>Ea@f;VrshH{AgHbaDBQ!6FZ4IZAhd$6%5N9yJ{KW3b+p z@rMP+GrztEvz}qtz0PESU1x=R_h9}${X<;j`NO+TN00u&t<&N0zuRxEGsF1|S4P(P zwlBY5rx>oUkMsQ5wLYBzPwC58S?|sGBV`$uj4vAyZy2i}$S zPF~XwyoPIC4dJsvvGAG7t& zjIY?CKjQ0j_=k7${K3Pj5n1otvj`u5#20yV*L!$P->+i~u)hsqQ7&Tt=whGfVxP{h zbI(t58({AmKIh(n8sSnK*{uej-@^@9{&s5coDnb&RSs3Wc27!P-^kbu?!%uSZkrY?(e{+@eA#;pSv zUi)@BrM8~vnKM4EH_AN9mV+~Ad(L%JCm&xB?1Swu~XesYydYC zZC{^Qk0JEtvQKsues!cJQppu;Yj=gb=N()E5?dylOM?WO(h6rvgZQV{JF8S%A%=IqL%uquk&~=OTf}@{ z_2}s`F@o8PnaJ;839-RdD(Ma)%@}Y`xm4^_e8dbZlN-1y1U=+mI=LS zx`o)5HFiz*#7%zvve7#Q9}lgs!DNE}gQ~Xxh~j;tMwgZp=@Jp?mQDc)rMp8yVkzk~ z2vJg{yK@0)kW@tACoP>K4FV#qD9U{n|M$D!9hRBd*_qkh**WhyPrdK3^dk1kSTOnX zdw+lY{x$id{qGOI_P?Sh^Vs}z$678y8ovKEl=}tADeSMz7SDcbxwXIhtxyY`a6r^LLrp{NFch9-S@Ae))Cy{S2Y?4alLr`|65qNn^9EoU(7LtNk23QlwE- zWRBi7_Xb-)+LF-l(gVTMG~2B6V0`tx4c>uwdxn1l`n^7!G+IVoVx@}G-t?BFd#0^V zNB3NwgZa6V&R15JB9wP+vMy5C=jt7u@pKKg1eJosbcs70N9h{mpBxHTiN~5}BsA9R zT}x0~fjL^m5PTfX&?*^29{mc?;#{NasSD_p(p+C%DJdhSfb#@Oz9<~|e^2E3LZ?rI zEgAhD@r9*Fy3gf~C`q1aJF*fha6oAa3?NpTWzVZR`|J2o6uA3)db(|r;+rj=SJfmH z9US%WvvIURCjhJ6H&YTQx>AaT$ShL_Y>v93$7loilafVFn2orTD%8j>4(+O{;@-A` zEC1yDuREq^BVEnW3v3xameG3y#;-%!8iK# z2)%N#MLAzz|Cg(1y584gO63Ck(wT~#Nhlz&%M#YppO7w2Y!J;*D;oXk9sfdmT=FW8 zlFoRG1z&P;Vr{%Gl9df+!bxoLg`@fnB5Iu8Efx&+`)Zi>4 zXer{VZl*Oy(bMV*HtRBv8b`b}f7(V!N*Rrb4^rY)V|guKz-=(d&z)si z;p6HR-M31Z%*R99qhu>ULrK_Z<=trP-=-Ev9qDP)h(pjf_A#mC~6T4)=@Hc=O!MZxUkotK(W*2@&g*8WSt% z%fnKBg@4uFxr;x#ORM%q%Cf}%HIHk{Qp>j-Uv9qWi72>9+VV4|$3}?phK|n&Rz{en z8?gi*QFDvnP7B|f`-fWSKI44gaOQmd$;SD48W70F>EFl0Utr}84DWGrKA7_jy41+~ z^?Zw7lF{o&NyXTk5|=SwTC=`no^5zNN@!20>)qWr zSNMF{TWGU2e!xrG>t1@Ds>`UeaPrZM!Ryt9vT^&pKS!g5vj^GhRKBZh^!{e~6aS+3 z*4fL)3a2DHaVIJc@ov59^HcYy)=vix;a(~Y0LjmtfAhWmB9_pk6FWx`WH7|fL2i;dtRxxq=`x) z+)_DO`sllnV56@2zs!VykJ&?G&GWl{@)QPaFYmxDl`f%Di0<=0KT-Fwwxci|F^=5f zFM?3O5UrAqW0}Mq9mo3JIxcR>U%$!P??I9b_#TUVInB$brl*s(X)PjcZITD{Xk!3A z_AK2uQ@d9ob%(W)H8#q~*3!7bdCkViMs)|>H&sndRTYoH@gAmEWZmKU%(^?K7y9N` zvNsub5v1x1q7cfKcae-D0g_{@bv7DLLN#uK0$?JOM~r&u70{| zf&z!v!4uh>;Z0!?lBoO<$9f+sxE%WF7Cc3xNLF5^FoJ57fkL+$k4cDUUYZWRd9}K+ z01r8p6)gF=n7FpF)SedO8c)-h3z$6b-f9yGtn#(KUf@I@=0s08uQ^Ac!*NMvsAMc# z^YS>odZ!-eZL-^m5xUp4Ig!jsT)M(+NN%g|QG(D`2#cXTDYxrHA!wYhFn zOMj(6(`_y$8dAb=1;m*-C$(dfw()f;{C>MGrOty7+`=_>3QEyjTSZC4Hmlau%rr{> zS#ERP)>EOKRsLi-%4XF^rGULLE}Z)-cu@YBo`d!>1&iJ7B;uEidEI=1i$*w0G&d0q zhLkZh!D3;gkh3jj5W#hvP1SBRRko)USmm?lFCO&6?f?gwExl;i__il3nx1$%^r5*g zNi%69pA=s+soS=P$7Ck%uHgELCQhaB%Di32>z=HjSgFIa;j?pDz}Cj^;q!BG4DtF! z?4_{>vVYDu+J2n3|9;X;eXa48**)7kJkC2Fc8xBMpJN|26*Nwd+V2K`j1bZ|?Uj{q#c5Gha~`M{A4f826+vE4Q~9 zpAfxmo~3Ukk!)NqsGY_A6p0=)vrR8Lb{r&#x@L`XGlL=&;p%$NMkDKyR zMJwb6oc)2jU|a-1#=d$iuho0@_lbgzzUMQo-kp;m8LYH{b>Hj~uUBFjgX>?Xi+vPJ z#tq1lb;bjE8D9X$@uIN^l(xpJqp$i%Ndv1&jO{R>ZKcF()K}9%C~ZT_p>F2avx3qh zq8zvrkFm28j&bC3KlPr%yY~LU*50g#&jFuix3r=kT#&$4krxoF(1q9W~XcFw>- zhBZ7c=*bT0WRn^{YG^!tF%{Xx#zw)DWKlF*O<(kdF+B|&^rFOB zSJ7-h;TCY1j^5Wb<$RM0G-G+2UL}_2U?rB%EiID(oH-W3rOf-JN{stNJ||&jXV+I# zWj2(fj8pEg)-$WbYi8$zcu(v*Z;}^hPVG4QZb;f6Or3Mpnl_+%@c5;keT|#y67@8r#r6w+E9pNg!wa`g|1+OsaQzl-p2XZ(B>wtVrg4F` zXxjY(q?5eq9lb=9T|ulXMFTH6YCOJWp>UQ7i_I6^Gv3SpWH5?<# zX$D+;HVy)#OaMg=IYMh|Ef&+Z{FxxYrG6pOub_C=zVN%73SQj#2u6_VIqn zce;md$P?v~I8-fD4yk3VNr6#;p^XUrmD1gq|02fylr28f`SM+2n5Xk{w;O!_ob~;4 zG2E*uPtMbJ&JU%3pntzEb_6?J#us##hI8klPu~1>`E!0G>U~YyGN(>!rld-9A+t`? zGNWBA(0)+FKJBdJQ1)V$p1~HBaVmDpYjt%->L6(KY(`d0{xi`p#?_IhcLP2?xxOCf z$I=*Y8TVhIN9wDoQNodJVP6?4uW0t}I>$HZJimB$rKDeK#9xkQ6ucvPcOzb9HQs@F zg`*ECco_A z6Z9z68AAxooAyu8os9^c&m%GoP*C!0l5D5s0J6`C^;SCQym}CDV@N;}3X~ZeAPBw{ zsJ8^PG_w=Y&_mWA3ttG_939|eddW!p(q!N*1D1kB$*b78pB(L=1>z8v&vhQEWYT2Jht(m$Zu*WDG_=Ot<`(X^DowFS4Sp~FI0^3XNFf!? zd+$3ctU5}u7@+AR-XLE%P$Zfd%XzdIjigt6ef|C%$1FRN`PCia*nC4Bq58P|;*fwkGm-;dDtTL;KFqd&L zCvhfJQ3i@!HHce(V$5GseZIRD&ZLD>Ei6(M&9Wn8iOJI@bh=#1rX3q&Jm5)^ESqcjH`M$+h-T*Hdb0T+t}-PzbJE;bZhjA!zE4 zFh>rbZnO;mUmYHv%Rb3OWIh`XI{Wkc`~iS(VQ#Q}mu$z`S0sq;bL^$0+2WpZTeHef zk8I6ioZ2gmKi#jH-#uTMr6;g)58ulDMjt%> z)*$VbyjRuxEjGHd#mFx_>a2>LMk)Q!rBYUw@}Pr*=Fw%@>|vmronx8o;co04SU=qX z6lNvV0`|sczDi^cIUr~K#8DLkiM>Uf!anpPn_DuJcM3`uUKEUc6;9Wn$p5stoN$Lz z*z=_l$C2mDI~1M?!p#~&8V(Fna2hX(%wfHm8Cb3V2K==F=RbKOuhj-b$qw&(5+s`& zK|RJOjbf#i!b{YF-aJF#j(?` zGTB2Ls6JoKvG_pEe+Y!e2K_3#rJJS$a_0<;USiy5d8I&tN;-X%KHA(?AtnZxZI{cr z61YsPdl-$~E0U;^r>SaKl`5QH@K=hrS;-LWZw&F&MKe?qn!k>kO@1~zMxfW{{)ZbM zYrK-QM{pVWMa39kZ!M#LB4MTD}htI!J4Pi2|n0xRh8?aDNm1{m z$eOm+k}s>4@m-4%=Huj7i%prPtTLi17ofUrK;_5Scw?x);6?l0<*d66uk)Nm;+!Qz zdYnSKuQ;YP5a)O{g$#b!H6m>`SN!GW!{= zfkLrDD#xRHS6u8yEH-Orxf`Qz2%Gi1uzax)Es_6r9@+DOLae{Ta#ukd~Cg z%u`b@ZwboJFsHV?aEaGq8MA{am17xY3?PQjC zHP8BHCwQ-krN?I$uG9ATf!Y3RZgY+L=Tn1QOBb| zP>S^)-~fsDurW_&*`=_PD0?8mwt6hZet*|bf*pAV&>h+kO6!#%Z{7+7G#BoMbnT-V zgAP!R{%Dp}7uqkr*}T!vxWdKGnV#1CHDJZgE~=*+x{m5ca~(ac=>>jNOwrroTj^;{ zKvb%Z&X`wrqXA}Vl-7hHo(R9dAc#a*Jp-ja08in^xsvAaPb4S7TPhc5af#4@p*6O} z6Vc3{}a6P z>z+X80Vv^v%*h8*S{rq>zM31Hy;4~v$eq8w8tb*0;~IJa1M8ldLmOYu{yxqt1x6%R z$}cVBib{Czc$=^z(vUK+f=j%dAW6S0FTTit*4(^Wo=5n@G_5%uHMI?Q3?C^qKMA26 z?Z%gR+Dv4(x#}m@bmj5-5OLOoEH#P<3hW|I{e0cH+@(VG_C)pPyf>UscMHXl3fQ43&Isl>!~^9lgd$bmpvleAT1t)YvuFms-0!Jv);5 zuG>@#b_fYr8!>fZsD|U@jsbT5wy_{u)V#E{q^b0 z)3C>Whhaf3uDMwjF;mRM%d7T$isV_YGd^m$GZTF66MSKQrqM41r%h*l7iayjT}wT7 z1vg~R56{k}&ku!y$LLqRE6 zSrmCAn`i_UB%1PGy~D93!=TNlh3QAYsi~7_M|;abgbi6SF}T69CY4=M zH;X&23U@wmjHs^<*@PZ7cQRee$UR#d>XJ5LffY)X$gW3oPC~Y?7d%=brO1S>8S$Z z?9+;E9*AX>2&D~tlhqnK{e@QZ-Xc`dcR0#;srt9a6+#_vUCRA={b|RU_*Fv#cF>v8 z?H*>A&2`%U`It&suUqI|KfD0<|U5|>k)X==UrxPC%Yzk_+@H7^gsq%KX# zW$4%CbB)~s4|kb?afO9xb?1n&ol%E96&8Or9aw0_#JBDv5B5uD=pMykojI>M@0X91 zkKine*x%4ejaBY)61}0>U!dt0)5>@8pz5J$ul?39eHu*Es!iGtj9eI>VSh>HIG|Es!RoX7N@<_*#paRyjmrM~(tU~^ z8%|#v_UL7nx=5Z+zlIopTn_lK`!S&@p_cujCFdjmq7x;(HlO2_;VQ<*FuL99g9YWk zD%aZLPRvuBGcsB;GUDeOc={s2Ev^4cL`$zqB6D#4FcDhaL1{oh+Mu3(zRE!$z(Tm> z_A(#_s7)-tv|jfmNX4TE;1;Ym0B>0#pz1260!YRp@=~-|MDOwf$PK-l0cYR+0*>C* z9FF3`@w7pEFNxC3A(equ0G3eNbatW}JKABMUuXJyiraGy%B8%#o>f*>;TstlmUBCI z$D+L;&_$wUw=Fm}E(|jpRdknEs=a_7;JFm~_TdfwK)JMn>UAMn=!i z#SrWiIY4U?^TX%vm>v>2Kg2}W2Akn6^mx@I0aXxOkS3+QtB1 zg7$!=5&##>2F8TpdXfjUpVMAI5zidVU)_OV!sBnV_sn_eqxcXU16ErrAdM6STDj5Yme~G z9$aJN(tgB*$1_A*#|I2&EFKIdeWZJhEn#TO@^-jdbR|V4U$th9T}So%XPEXxa+hT` znkE|5P~%ToIn$Mkl4m?h^u)(P)MA>Fy7@}sNlQ_OosU{Y%Ur|MRo5wI+$1$Favl?H z9T*}>a4i{W$Rt=!iu=hDOTY#p@jik&uav5;ik!e&Q-ipYa#qXo zj!VpQDCXiDLnt5mG7Bv7*OIXlH?Ky!6#@|}N{;Jx|EIz{uO~T|yUb@GD#F_^=gusV z>Xn+(H_sBX)z&)UFBVuu9k_?M7C1~3IE=$yvH!dZu|OzoPbkgM?hw=N(8_1HHrNhR z^MeO&X5$l5o|8k_I$RzA&;_^5L3PLZjzN_&2cfh0Pp1`d?RV9jZPD0vB*v&Z6d zrCMwJH$P8{%H95q*4lNTz`g)zn+)i{_kTfy*(D%Gl|X)i@dhM#bevw|#^*NWC+6H* zyXNO>UfG{fyZ%i9JR@}Y^6Y5gsHKAT9QDpfWCXMUn-8h9IBBQ=#i=6Euv`R+y(*Sfn{3We{&~7jIv#IOs|yV#?-J zuUVq2TU@U>SvdNu2V?>D6#G4^xJawC*igGQPkV0c9W@;8Z3^BB_4-Qnwaj~1a(XCeHQ@XErb+W)_+`o=C%6~wodtb>uzfOB0_gB~v*l3%r9ABtQ9tH8& zj{T@V4NUot{5}4b-{bf8_7cJIy|%Po@7Njd@c&wFt+q(-H1T>`P)Yt!{+xot*Prj; zA6a0-9_y{X!x4X^63Eg{rWMKFmGCj$!awU^UubRF;X6fVhZ_~C3k}9#dfp zRI@y_(j&wvvaql)!%wT-X-3V>C5dR3I5nWkoU%|*EhZrk<~?}nH&}+8ay)qSkXy#A zi9Wn4oKB|kI$ya=lfe`I65Q2ag=^7XJpI}PjpD@76g3E{v0ZG3U3B|Y?f{;6;Gdxg zWue7cUv&$G5(4|M{#cg%h)yy=bK5|(Sij#)w?q$>yd9V|I2TY|*4HvSJI_W|Sy@^C z{`-=3r}c;D#1)aC1IP;L)Ibp?QIWV7R{zD$7b@~rX35{36eKd z%k%zC+i{+(`8)dE(@#%CKF$iG?8>?)p?8I{G6Ow=q^DM0xJ;c8C!;ZlFsj(cnhdHk z#iZeuetX-^A?2fnZ4lk{ns(O_tTKzI`dv1>J?6;w&uKl)ksnCvx97DNKSVKjbQ8yl zW;MG?JvoP<*rDt>`tJ}NSutp3LjMHl>dX0?-%sA1FN?onTo zxqdUAev2V*%g@H0tDgMPam(KxL0w3O|KZHTgVUI|)?5R2MoEFg(G&h3royUCmhs9j zwL4;eH#y#JQ$D^&U9orCO~SLn_w0q>awUfgOH+Tec)wMKM+&t^3dVLtOL?p~ksHdF z1m#VWF)HxJel+`|0|o6{wh<%e>l0@lm0THd!9scfnKKzKOH2A0K8gER0_aVhh7JVorQ1-#Cc&iwIWpggp3v-FCBtb zQTZi_a6&jZ@wjYq!rASOMNQ-2<3DGB5g3%vsikGM7Of$TjKaRWd-j`zfk(vOeMw;H z!?V|Da5+r_lAaTzKdQLX0KlsA!?P+9auWJ=`X75yEiEl=dq-n&QHPqG8jvX4(F9g~WdE!FK=>XYMVOu!8Tp#!9u znVE*}<{BfoQ0sL=wN-(V4v$WPKxFr#gupQb7Hdr2>gg?wg!})S>5u`$`UE3|H1xdJ zy}-xR`v*r}Tc45=JRe6skT(J4g(Xu8L*GHC&a4Vp`8&O8fh#l;F*kR;HA?u6)n;A(9jKNndvUF zb)Fb}U5N}Z@2B+Q?#G?3ptrSJvIvVYyj(;1@-=t%J>y?PTu}^H6hdj&u6+&Co710i z0oM`DfPXnnOTCNXOR5_*$&E2aj-f~FE5@c3owM6zW##1t7N%uedlr^N+ya&i)Q^w9 zSaq>(zwJBhCl_oxXy2xq_-<53b+xi`r=_*EytU;+QuqA4#bEb*3bmWL#eekHnsdbc zPtJj3AW;Lj2hwdk)Pl()?_HQyKhT2#>}5^0T=(;4^YfP5{!H3xLE6)$M7J3`-E{}% z3)l7i6gA69(}k|!Yz{u6J#*Mki;1{(8wry0BaZg!9ck`lFXix)zO}smv`zj} z2ivo~wBu#=9=y5aAqm_*`mp=2`|oSYbmEErHy334LM`+S>wB~K#k^i`)zubOzlcXP ziZX2qb4%Zsb`GwoRgt~(Po~0*Fi>)@rtB;ef35$>xI(TGMOLGqbAmnsCaO zywRmgF71S~T2aNVE(Z7Ez|h|S$U!-AnE^=qT6~_LuQXiKoSYnVs^eT!&e7nkAI(rd z0?)Ocs)cAa_|$!WK>3#FNm2SxwjR=Zf(If^Bj{|132*|KyjByOoGfu`=2OdPZEX~y zjhSFD)*ycp@$zFzZ2GhWD{qIt`& zd^3$y2lslX3tML(`H~F5O)3g&Wq2@m#<0K2!Cz(TbWw=7i5)W%W2tmV8`GBDZ&%xF zYI)-dsiMMlkt;e^uk7|Gy+I{i*5b5dL0FbEuDJxQ4y9hT@{dWyyhT!-(I zdW>;*h${QBcyP)It8ls}ar>!_w~DN&-bY}BZ%Zp4d;IzNP8h>t`%28+Z`Co5*lIo% zNToLRTx#ATd(Rh6ZA+#_lcW^}c8i_)lxkcQWhOe972GrerCrp)hUEk?@{Y=c(sdKT z#>aNZhcnq^r-VGt?RB-%1G)_@JJsbZ0kJuI$h6uXQ? z(c@=mFa0p_zC6U~!($$OCDyGc=d18jLDtR8J{ix z8S+)@H-|r)=gTI$^#afAgMJfEsl_m9v zeMX8U^>BU8Jk1*u&`LgJ{r%IZf1N?Zo&nt=fSL4v;qU*z5XkHFIk6cD{q_#*Z>(ft zVFJDlskY&8VV($)_mxyjZp52n@89D8!Mh^c_-^O9}n0GIzu z0)c`n*2*`!{9j&KmcA2C6OG=mqc8V&=6yY?%s_iu^! z9$a^$TAaddGTtk;NMQw&R2}_I8||{v9CSF3>HLaDRXOD$Ucmxho%O;=Hw`^qw+lhI zCZV}ePsbHsG}f)0qho^3b8LWt(~HrTq(!bnR6UW;J{GopNZ=68m)xqK+yjedRUP%` zm_uij#S~YK7K;qL>7_13%$vL~VccvD3ZDzI%b2r9!B7BB#7+hfdUrMx9JH@r(H(PD zQ#DOa*V9MonYp$@<%Vv3#pppx($=>sV-F~kHJ&cAi?r5lh~TeorSzpPPd3wU88No7 z&?!~M6cz1Z4y<6CX| z(10em_J+)PgIdN8gHFq;-I9QQe>!1KT03!duuw7W^06_k75 z&zmT29!uCFS6FE}czin!qxNP5T~<9}iY9nrgA^;-2J2}cZqtCAdrhu$o!rz6M(4ed zN|)fJ?+EeVOk%?bEZs9=Jpqi`drTs#)foBKO!leLM6VWb@9bd--#`c}gsJ*)QS>El z`HrH-3-q{jP)Qsa$)7lr*fYj6U3cjoxa2jvXAO=z?#ACK^hI8~AMckS8z3!Cak~s@ zVH?%0*YC_^S{dn7D^j*OSbaRY~rgoS)h^VNgv=9vD(au&-6wtNRONbV}5DUg(3gx(JS z`L_IXf8}P1-RFh5g@Erte7n0BmJI#j@X%@2F&KAX`fw)~)$ZSn9`TQrByXW?A>L%y z)_UKKLyIRa5y81wBCjj2g{MnWD*4JM4E67`Opm23vy{7ykRR^> zjtU+>9vO*%M_434aZmr1kNgDYTY@Gce{M-ER?N?s3pfLm%Q&+*ztDfUL_{<+95gI2 z7vTR$y-Ik1GlSz7;stv(W5mmwB5p30hunjYUkF!iab3O}k4F4`H{MMgRgz_#)X+A$ z&P7_=!4$jM`8O!Yh1N5jO@{m|r&#Gy@RcZ*fd3M$m4CU4f;9_r8l9m0Ki8*@-UPiM z^iVkAtj7!8QZIb=wP-xTQn!d2@eFqGDN#1Ft0r<2Q=R@qI5Eplm4&FE)3@Q1#E|52 z44EK7$ip7y>V+8Ti5fV>*B$ZfHA`{JNE&f{KXKCP2ITV8h9{XSe&VW+EU6Ed~ zY*dX9VHrl2@(kl;nu95Vc9N@1Twsq_vUYCi1|g+^opFedDik*9_^5FkaAT2LQW434 zTz&6P*zPbu>EAk$eUJH2sDIV6=VT;^zPl;9`;Cj4x5WgXo8Z^yroQIL5kFc_Gwmhc zafzn>FcQHP*U|?dR!{#roNvG%EI_Nue%VE6UVCu<2Qn2-&YztBoyRhZ1xJlHmUAQL z&L`siN;lC(JZk2x6#rna);OOxi164lemSWHzBxJRx&QVq%T%3VwGDIzU{cqb`UQK1 zRzH-+irKRyM-cCsSeW?ZE=c$dAVfN?>ZyXobkmwRRbkwdB(>SQpTY}cxkiY#NL+Jas%(M4+5!hsP@s9)uZ2r5y5=&_|*JEMY`9g4SI?ju0nA#=VN5x{6Ss zj-a|6{n&%2#fsh>UB*$0##&Fu+2Tm9cabsm)Rc+D-kqT9DRd^aA-LWpu5lZQzfZ-S z&%@E>N%865ap)7E&$I_mwRCa95mw%~=0uvo1DixgL`S&kSQZH05L7jTBZ)D_7FJ>K z*Wha%q*jtmgcMXP7z`nea=sl`?$TRee8F-FX%^4x7_1g*)EJ2Il4x~aV^B zxsXg>T_tI{T6)!8a?v-}uj4BddbeTt8d4os?_xzFoT{;?ga7VbE5w_DGxK#&abe8| zbG?$_!urs$6!H$s>%EP#=_o#LG5GHQg|>|`Wz6Gl^e7dw-U5JV0j6wt!8OiOA?gWczcLp<2$iKQqmUnkKNtdtte8X5vAQ$SCc9*QAVmER`qHaE)~W&zMR_pNs-#ZM67%W z?@m30bPNj(#-8=fU?zeVeT-(qsF2U$+`%$o(ZO;SsvTD*oe*UA#IYOlOR>9cN^vO7 z{Yixguo)QiFLB6dGv#$)W-`3G&Uzefjg!Y!QCZg4SN^)JrHwi~v~R28btss*zo#Ar zU9P&f7I3_g^xdf4^7ob0Dq0^u|H|Fvq5f!zrqV>(En=;bD_R)!0%6W@)+5H`&GAY4suRoE{ zKR~SUe#E=e9PG0aLdf?K&u#H)#p3O0ZIXtccpApRo>3!+CCXCVucmUMk_MlMHIpK6 zBa(Az8!R!RSeG##b1P!*sCHq_t65Qstn%ui2=%vaPe{IMc%2Imr(zUtpKs`7BRoRN z(x*)qrbXP$2*%qxP;;b}(zjFJh6_=LKUko=Wl7n|jb%{w-l-*b!o-~So)tXk`74Qy zk~$}jJ10&s(R1{pBQV!9__6>(EtoAAf0MyoLR&%`M-Yz=k2<7X+c?*)gUkwW64D}X zBS%_>+lMrd;@&6L#q#PHttR#-HYOItjl>cTiK&+4!yhGHAgETogidlY}pLBcu+?9zIk&BD@1W zZLWLAmtdBT&!Jr*?H%G>B!Yp7uqM5> ziN$m2Qm}9YmSeDlH%T1Ae-mrH<60Lch1~T|m+CP^5OJjVI*9a5>@&=8OxzGWQUV=J zazwctw-x0cAq`GChP@mQtP)J)n85bJJ*EY0qG0V;_&>3y5vu;!&x4g;U8=<(k-IL9 z@w?+158?pf?T?j;5cS8p6N1>oHNJd+c)xgy3v0GR1{#8k79nOu*dE;~#H>KmQXmdZ zi?EZj<0P~kk??d9w)4|QK*M1xJ`kgWqV1_kqbm@ES2OLEJtCoP$#jbOIPkwSi4P%2 z%@vIqQm#;AUD3hU*5|s;gWLK3rn@)P3=*?p+ha^O=~`i9=nyPscZ?zItnA!fU@6R1 z*FV5`BUQR?iHXTVcS}sa<BksvZcg=e+9oX@|7mOCPb%HIW*gJl!AjqT`(S z6tTw2%GOU;h5Z z<3E1~=|B&joFj-+oxU##37l%k3hkfoVzQtAoO5lE-LI(x!QF+e{`MY!O8?rq+OYJ& z-#>eZ;r50fHQ#Q!`pz-#?&mTW`+I%(sl~P)kOSizUuw?&`>nBOT-G(lmfqG@PO1MC z!G7r*srp-oRKEKMZ}vOU%po!Abc9>G}*QpVaKu_d!aNHxQI-BV*j zD6MV5Nuw8D6Ui8qo-1JvDu z`blLbG(+Bi2&lfXGNZY^NoW3~D4wx5>WXo-YD0kVcebq6F@EsNrhOjub1r-#lh3lJEuOc9 z`#jtB-kT0Z19tg8ri9k^WXPL736ie zuiuC2R>@B^&QKRm*L%T{gcZdV;w+e`our?)Y^O*49E-I2CJE+VDv?5P7Z0w%1Wnk2 z^@-9KOTl0JggZ|U&e+e`cgg>ff5AWP4EmJa6JdLkHm1u`i2O=wj1;x9`8gKSOs1c}fpmKUm;C58_@IB23{WLzF+7GcCqAfadGZD=!X4_psS9_^Ye26 zRUp*mMnguyz4LRHc!?YFei|)sgQuZ`m=IcF$xi5%PLNJk@q_|)_4tG}VE|To0vcZ; z8taDv>ItjyD$sj~TKuSE!f2q@MU)0Ic+dfv)x@@JRZb0=tf5Wjcuw#5D4oqUD0li- zT^Mi*nRTaMWwrabl6Cc=5_wb~$_zQ3RMd1-=^kt8LFu8awi2-dq((u723!~YB)(*w zzN;k9!sxMB`>FAxLk#(D@MU&r8?mnlg z-B@BFW)Ww-^9}J=bhL_n%5n9?klD%v)gP)U_|KRHC}{9PmvnsCFzXO|=3D|b!G-3u zL^wFJJmg7u-HNOztJG?FF1|XbCSm0=BzYf!U1H{Aq~AjNS$AuQ!;xG)-`+&=qp)Cj zQsOmDQu3>sMH1s38JZrFs3A7&PA{a=7)2p2%X8gQ?btZ7^3L}WI8N2&)i#JpA_e^l z%myRy5ijXYq_!J{8I}F|Db1VGUwev2|MM^`+Ih#(%+}ggzQWwsX7zSyGj;dJa1S@$ zma*W89`k>thGDVd%pN7IlbPt$gx`=!GW-KwoZT;jHpbuNpaVvWy+T6#uLAf}2NwHJ z9>^zbf6Gm-se&FC-pTVnxc2^NXs!O*<0?niZ&h_+*A88=8{n;326`USK55VGpB`|< zHFK@Wqp~}(+H2oSv3LJ3`@!wM6E2R*HDy08&F}xZ@@-{^_VCS-(C!<@JB{x$Z#@3( z9y_!lNIlS>xfHXopU2fR*rfXAZ5aC(qK$M<~)Z{NO!-pSPHDJnlqPEAc!K-tFsuy@eStu}gK zXq>e54~h`CiUq)$*|7RTwQsjg6--9u&Q{oamyS$n0sI4rDvBC%*pwX6UCK>_ zEAJuEoJ&F&(#q>Q(f}CFN+Vf2uUM}h-+H5-d1o{S;-dn|FB`C$O-kpiF^2|y) zxDk`ewCqT^qQsq7LZ-}mrq?ZbuJCimR+~U%P86(jnLGTxCbCZ#If0-Xr&jn&C|52IgFn_Hm&B|{jS1W=yUWuGwji|c7_dxvp_eQL58;yuGh}O zUMS4JjEPrYeG;z~XEd@+>Mz;d=K)0vZa_lIP@j}w0_{Gu=qQo21YyvB>4Wz7$I}PK0@}t2keQG&UT2aL z$0haB_9(Jo=-O}}G468(t=?bJ04UnEyQ$aWcKn##2Q=*KbnVmn?vH^yN)t&hMxv!; z`ao|w1Rdez@D>hG4dM!`&0>J42kiPUS$dIt8u7a3vx!NpDz9I^uHrU;Dnzslv@rx3 z2-AtE$i))YMAia(0B9EzB>G%T4K^3GiN@vIs>iOb4=(hg7^JfDr@{4Ke{w$RxJHG( zjU{QJ&|QQQ3XjLpwI*mVKz9K}6tEFD^j`(PJXAALD(UE(UQo{F8Uy6`Xi&gLl4p77 z>_A@1J9(unCqd|%LNBQ5%8nv{fJkSQ8T7=@ULh2(|M# z@hhzFn>w_GZ;;{?AdF}TaS`z}1ZrV*n1Y+pm!iUI^KNRh5yVtb@mT1WRhJG>`7`o3 zrRaAXnYtfFrei`8$Q9x6WQwbr(wSRURFSd8lx%`=P*;CfH%k+to`q=>j!*HDQ?qaz zIfwCrNKUStpdMv#+fKEEFe)jyv;4h7#o=X>Mf1Qap5uFOFI(^VQ`?hOytNhLPkY5T zXKQ=U7V?z|<9vCQKJGSd`8RG-GZ&4Ipm{9MgvEd;?+4FL@ou5Q}KN`%O8$Zd+xwgLhlN+@0-FjeP2#YZ7xm3d8 zPo6)IvWDNe%bN{6*`Juc$x2*=pQ1le3(2efi|KoaN!k z;ahuqxqA;kWDT7!YMNQwoBp1Y%Y7${+Q{|}l!3cxBZaNk(>z&)<;C6mFPYHxqyY>E7t@JQNvd9++JBKW6-|(Cg}d`N%@DFj)==&_7q= zsDoBJw-jCTnp>R8=)WuRQfDXdu8o??GE^bdnN&=mPf}-+IVi-2u1Odi^G3hJ*Ei{X z_n|l0Xy(EI;v0pD$q;lExmCpYQbiXjQU+;rj~+#}L*DpQAX+I#^|V1^`2RS}#d&*s zdk4tug3W%@lfiyQQ%T3i%SJZFQ^~Aws4LPG^aHX3e}E66sR{+jtc=i!L}?kYM=oRv z2Fc_^U4^uF4uBz4-(DpN)gGt)1$}1N;Xom~V;QYdLFtwZ)$?12P=P7SAq+*lup5+` zpvAB$-E%qEegZ`-UTNrWQNl5(xCWQtKIn6F?Ie`>YTAEomdHwIo#p#ft3W9s1ZuD_3P#S@HWewMCSG2sfZucY!jedut^*W|WBsI~1iu=cI9@^xh zu+SmO%Te7SLL_AzPdju^<9LQxUS5%^ao3!0yoL&N-&YuWhD^On&6=2ZUAPb*UpV9{ zIV!ACM_78S?9OcRUAos-L`-k!QL+&BtqiDP^B8=jo?^5leC`}ed1;+Vm<;!(`zwBR zoX#V2A?TXI-;y=#V8mRr!B`8g-HIwUAsbUWUT55=z6sJ$=~;V$r69|E)CHl=+n1p;B+Upg#~S4gX99=2H@ z>_@KJ1khwFvw1w}KBsAnv5bL)k>#88S0bjAZ}3I8!lOlSyE3M^NnmB3+RObzJdl{| z1%nksvI;kZSj*4<>e(k+#v6^wer*2eH}dP3#6z#9Z%u!`;?XG`y*PS)aQUO`^;NC|9_bJ?m#LZ@BawdvbT(D zU3+I`Uancjwf80pA)%5zvaT&EvR76jlD#W?r>x4TBq`Oqe&_M|{_*pc>wWKYul1bg zobx)b*Xvk^DGqVnFV=(LLzhUl*!M8?oPo-&tSws`+zbED@+C7 z0uu(iipkL9(!;-B#t7{Q>zRxKlT!p&jg)%70r{YDfT?T_`Zqgc3N&mkRyiP_m)nA< zxfzD2=fRQM5)5I$H;F$Cc&nlqs11<^Zz3^u{qV#F3Vc~0N+5Z$Q365R)9rHzh$)eZ z@Z-bEAR97p#W=B_!4yyEeJBKNrRx73GyRK-B&v8~1|(eIqXD1|q%hNXEfB}=gcW9{ z0SDE?!Hi!~JIkHU_`nkHHYuDq4Dr)+9fGOl{dJHTZf=Y^7#z6RI}e`o zEkp65-rgZJr0_e`h#OM;7bj1h{H16$HQ*dQ%fDc#1M&j4hG+zjl7IN{QVw+;QIvxZEg4$MYU zK$Vd&g+Vivn$j+6&rQkk@RX+LXC-Nr-3i_bh$EEV5S8zqTBCIi9ut@BmXMU}HgytU zKK--J*!~)UMx;6mIc8Ckfr+@Jmx-Z4yj#59fi`M_$UbF#BeJbb>#}@Gy0DFAF^-g# zxQvtmqcPK+VCaX=8m1?7&q^|uFKT$Hj}xsjTnJnW+4W&3n?=_o<(P|@H$5@kiA`!G z)$k(rxBeRTnu5DiWH!CtS*2&qbxj4RQvalxY{R{*SytQ6){E;VXNe|fqo8ZBF7NQDt8ai``}x5qpAa_DAH~+AiKV5wHrJ)P z6f!1x#wA*1$ECBEpB%lL{&Snu{$DTi+kJ2Fl#ccUMWCR{jb5GO5Kl@E_t$;a_toOw zzFre-U6@FAW&G6hFSw?W1lwU*KcRp0>J!mj`@5q4df%$}o)J47V)yn`oHbZVe zoERwKTAq(qOWT-IPTgoyOl?z5!$QECTv1LPhTDynkTmRHHLNrQ9sEn55>3h&VdNd& z*3Xi{n0zeb)HC%uyr^|)f32=gWET()7c0n5-?x%FA46p*vOBwj1R~x4LJjeHfqTK2 zZ1guH#9CNC#OM4%tp$JBb4Q!HzPMAyf@<(5fC5qh3dp|tQ^veWmu7IBy5NzDSR;XC z9lSaY!Xf*#K=KjTTn$9}v^k`Z-kgJ+TY*}=0^Tj{i;#?(zr$q)YD?VzN==BsAjR=5 z)d!V9(s376j)FhPAZ5TyJ!23*#b<6Fi-Jj@p0r^mA6_(`cesfonXfco00CI^w=Z1G z(gvXRnmGb(R>(kQi6BDTiUO;bc`U8&cw61^3uIhx=UwVY*H&h`Qp91LoMa^UwFto+ z9k-=<(0-}2wwlf&yqE7sjP)j;vOTH3Ln*It9g(|~F)h7l7H|19QP8Zq$Cweh^`(h} zf{EX@S}!${W?UwHD<_DK4A zyHkZO|Ndp%!bGGUC{KyV#w}@hq+36F`Y5+#S&DIfvMb<4$^8J`^|S%i!GG+ZMK*XB zn-ni7A85#91x6@eVdSwdcs}ra;Qfql##pS6u_<~a)lPPJy@|j`74SF5&(YHG^(!?L zzgT-LEAyPPBhGH(^`_is_W!7#x~bgSKmI(N+a!FC?;0z6{(zmErA*TG^;-eDL3Hup zop6X?jg>(-hLS8vLA$t&DPhQJQCO*Ch%PejA-C%s8B3~Zu5Hkdv|Q&J`nY#SY9*X& znAddi7&!~Je}Qc0Y1T*qlBlXi6rl+-AJ5u?3;X+ZQ08p@K^!d(%UsT+xh{NGOns)t zxA0k+55T^*Bh}R5EzTit5)BBfTj_2LNf~bDP^K2?Gn<&>7>Zwmt5X!D7c?ggl`WMm zS=?jtnpr?MvX3B($;<74vl&pu7Upx1y$F!o5ce2(v&=#NIymu$>3Z&HYiS{w9qL~_ zF6VLX#MY|+9nlBoQ-Uln{(S_mG+?TFZXW}@Il~4FRJa|1pXt@e${w^(AXq-Z?*Rn? znXRWx3zGRfx1n)DQXVVvtPfk@3YEpiNd+Bj(5g=XzQ8OWU*H3ZC8V@g0d}i9D=2l< z3!vgf;oX9gum>t|^$^h03$!cHD?%vckDwzhfy6$YKR%N+Vr-W69yHQ(SLGt9(bgv5 zaY<6cY$a&*>>csEWJ99ME@9Nx966I@1DW(AkyuI=G}$;MBRP2rBe4gGg%qi3eD(JS zWQ3fvRK>DrO;rhc({rEpEpf}p@*_#%Gu5AB(zDha0qq90 zjdp2%sgF8IF0&n5y>vJ80;>}{aCxk{rwM#EDDI@(!9QGte_9Q)ZF1ny>4cUvXUikI zOHABP5exa3qt@P;aKM4kxSdh_DU-`am96%0k*0uAQwCnZv?-v}H#N-E6)QjmoZs%s#e{FqMNNJl(Hw`)EnYe`<-FK z?+!Lj-z6+?Z$+>56_V^;_q{AH_Y8E0h*B^NbcfY;1VwX&Ivd8I#OS}$v_Vy2dQ(4S zXxVu2{fr`LH~s;^15s=jK^!W<^)>t_ZN%jqIfct1vZ9T3U#moY?y(WZb=Wp0*|M#; z(uRjFw=c8%GpM~Na(p{pmODBv?c!M1rJj^Ky1eP&Y2(N*eQe>F_rb!GK*ZIyvAp)O zNebbNeX_h_2U~fEI(A@mHbsqPz|cM+Y0&JwVGA5Z#`en{MZQdWxkG;fC)c09;fO0t>0wc(557Ap!%a4xXBg_t4JarZ z7deL-ca&3I+aQj9?w#@a&TAUbcf6=Tm@hQy4fwv1$2Id=fD-|ZTgmGD1aN@|ARcl8 zrLQi_2m}_E>q{5-O}G3FFy$XA4binf2T2oowL^&UPw?_T(>Lifuz?(L*d{Xmpyhb) z6HAM4izmVOQC5jn0|w#r)Qr|3)`)q5umiLSp@fuZ-UdrHKgT&UJOm9Rg6ypBDvy;DE^|JTMi_#)Qrs= zV*GfoEbS8gz(k>uRU`anlL6k;8Z{zqqlao2;~!}X&2nd`5Gtb)K#o*N zT|a$77_hT>w$|=W)zu4P)D)UdE$1buDKB(3?~ZkK+W$%ZDr_FTcSsKD=@uavziF7I=g6zHUXv zc9Vv@)@9lApQt;u@(vZ#&aSFb@&1fy>*7JnoXrlS+w=0XFK%%@`|79n6*h;s2XTdK z57t_q?JqgUv`55LZSr3jHi&SO%^SX|+?T(3K0p2a4eAb_jSWF0^&qoa=*u$~|G>E- z*xbJIT~!d3?>rl8Rh{&)K3VFD*pSq?)mI0R0jnDJ0rQ)^Nf19~=Hp`VceGYDmT$CxT4HCl)a zgf{Wzy%!s3Yi1m-$C#prYz2IwH3EHWxEhRukN5tA+zI329WY?qJ5e|}O1$lh`^ye+ z2k^zO=J`KHw7S*LmZqLNSk(CJ>Nk_c{f2icgnYjBjq1<&t8cr2@Cw9N#0y3_;`P6W zh|3zZXz;2KL)Y37jVz4#YC&z02{IzW?;4!rTt&Zfw5YWsTU{W~d#o1) zlWz)MC7K_v$2Yu*^tWmJr8D>VM^H&vw|H(m<;{(SBV{O%6;rWWTtiimyGxVO^+;6t z7B*8&#;m`@hbnhX-T~OhmP3lpvEF62 zqBmehZn$dg-RDFrQ4et@35@1v8?5(KE$?6?vMahvf(?A1m2STxuenC1nKxBC%$S=qFxNicfr(z1h{xC8YQ2qFnP}fst7gDQsrXs%y zz5n@_g)PO+R2eS_WL20kO}C!4HD0kbUeNYa4)9ZU=6^zs8MI@C*34xWM2IzLoatzG zY}CFK2~SS~OVs{j+y)H}S~a$?X7>X*iT{4irtEpmrW9;EqJGLe@YE2~HkP8~JXrX~ zyZ2R9h~1^Nc-DZY4D!-TAzM$V{()HjA9QA%=Lr;Np(A*VI10wjF8%sMni1IBe%3p= zF~UAUEx9qqE;4Isp~vtXX87bjZ zat1*(v&(O`Gc{MQoO%49kc39X;rR6OyWbM&*4d8k)`YP;5vbevDe?&#kTkgLF>Q@Xf&TDJV z6DUJ_c^i!Xj)4!82@r-L3NjQ@1ZDydls`TK(FeFH=-8pa8z}&=^qRD%jN$uh*40<; z!Bh`CJSv1&J}F-`Tm17Kig8)i3m5Tv0=gM0ap@aB0TEhc!ZtIxbXsDvY*Q!41CwE> zTC!rx`%E+g#|U>PHJT?2)ni(0lih5S14cN8;09&M+i9IF<<9x2<(BRNM?7yLz(j71 z0>jyPXGOYiSVyimRX*`HH?MD~@^jXI)*JgIW>?ijsWGz1fzZpJUM)jVn#*$HSKG(z z#h?oTmZt}|up#B<@o0w$pYay9C!%q+b6Ps?>9YmHzB5hiGS*Z}Ia(Gj6TWj3UR`Zo z15OGq4eV0*08!v^$BSSlw5`^--%anI+{?ZasT1SGd_j`(T&Glo1KW#Guisz-$OYc*s6Y2J z+rifl&SgLzk(t>mA47ygs}HYw|_@WpCz-!r26W{d};ZZs#)4^M+=mr2s!7 zh8hv(!Vps4K3B>RrMn3?#u*5uVnhtm96Tn9AK@}Uwg2bmN;c#!TwUvR>;u6s-EXpp7f=^6exqjp@#Sq-Upxinsrv*1h`lwa3Bj^(1_&_i zD=<#?gmBtY0E!AUXRkcApI!OCILil8h$6(&2+Ydi`xpi1%?j^rRq(G3vOy=6X65}! zcT=QRCD^-_ZYp{QNn)wP(5j5%_yUD93=-9N(jQ0kW_fK2AwGU!osv;~C<84LQS!KRk{Q&;u0V1r6d_W_V33{J@s`G9tjRcFK^y+w0W zK;2ySj>c&rYg`tZG>gZJSQ%Tu=T4z4v?T0}PG{JqsAm+H95nZ;6qJcf#d<)E0Lgv-==)Qg!abdq?cRjr8vQe)@_-Pg~ zfuTmy+ogBhw(k8Yl^`}~%^tKP5o8U}O0rkkH5LpoDAp{?IV*~@V01Pq)32rn0R|Cs zJn=gT;&%)h2^5I)^uc4?xK zg#uduMG`wXhkL&`Y=o)wqwrq&cmMVY4M+*#e$c4KJO2Ck<@TA3Kp3>uz>IC4m)NWx zllPQ~({HY1Q-g<@(?@*3>nWpDNHnD@v&hrdp%#|g_dVZ&T_+tqnf1=|r@fY8j|r=G zo+qG>?$?ZV(VR)c?OveU?15S$vNX8jQrj5fe{*xLh(LW;oF}+K)2n4ikTr;CqfRMt zut;+EN=0%2fgIC%IeeYL4nG39@m>$xJpF^($ilG462$lO_tf3@RESS6L{87&_|Zr} zSmp!?uTvnJ{9ZJ9ipF)A?jr6JKypwb1B%fhOkt-X!0v*Lz{52qA@5sHNL~!CwBq-@&}49 zHQlerfWT-07>V>cP-Rug>TRnEmN7g4s9Za13aVHE7jQ-M_?AD8d=D_UyJQZNV(^oh zG7rp*r*b%Z>p!R@_j6&;_T{Z(UqoLV$ngc3*neJqEAe>kMLbz-%!}L9?0X7da=*Nl z$Q>8?X#kPgGaKsPP4w%hhhHQK^Sfh@Af ze%*4c??;!`;-+%5*1dlD5PK@SXy<#u4LLJalng!T&+8tx=ewaQm&hnbBk?6Iw2RpU z{6*>b@g`;&Yl?zwo^V@@aJIF;?~!e_4Rrc*gnD+Bx+3~9IjBl3++-j01lV*t40b!= zW}|Gr`@gg{m%}ZPyl$ZOO#gg^YnA7Z%f}qKN#%~{E0)B@mc;d#o*|JKYD-eP+z{!#l|B%)5H?$>;ct;KU2 z8OBEgUh|eA_SP?F?cP`~t$!X%RuHRxzg~Yue79cm&DB$aw-vYqT*b4LrG!(r^A@rO z`^Q=Z9`&z;7N*gsjvHgXDxOpJ0C(zFSKD8_dYSn^@Eq&;U*9rE+E=~*U-F zFlthATJehDNUIl!VtpC*Mz5DbN&st4MnK+xAPH1QPpGvrZ{!2CkuOvx4Ze8tP#lWu zVKMU!OroaS2*LGrFDz~I{GYT->6R_@Pi#gBY;`>u7@u zc_$B$DZX(ZIEsWs8Fzq781cNhymiaVJBI7Q5~e8+)hpG<*|aClEozS~s^rmlzZP25 zoH)m-J#MW&%$3D`>BvNu=o@w^Sl^xdqNshSxMUkw9-9k)3~}U7b9L|zMW1OUN!p5X z>Ao#VpPgt@PC(c_P2IpFN-Vu0GT<^AwNuV?smhDcXlFD(jz+O=P>M7lxel`sccpdL zxSdwpE?BGXnf6YuUGPFlW3{NO%SPS6aIQJ*wd*JEiS;SV7O!%5C1DgJUh4^E#wCDo zzhf4b#sWv=#`x8ZE&L!&WoG+?i!OW5mP>>N z#g{dQ2fwCL)jjQq=xAiCPv`u4nA$p#2vxV%I zhx9SQRmq~APO=2_f4eHg{md5K)!l|*zgUgCB!CP*^Rc(hrx$^Ws(Ty7xsh0B7;~tePC) zMtpu1JrLTO>}K`VLu?>WA!QQBEB*@{%1o4`;04mnL6jAnjIKQgJR8yx)c;+xd1|EyrVX06LvPBUH~f&PV@L^mT| zI|E){1752{uUxwRi`oYQ8L0TzG+$Bh|MV`Fi9&eoMtT=GM*rN}p>~#$_Tfcd%ftZD zL0fmv7!4^l?CC$#28vOq7 zZO+FD%hk^WLhNbm?6Fs-s^kOsD zkBk|sjgb$zQBU~55U*_-aP=9t|fz(jFQZ}b7iu|BkL&ShcbEZLCf^Lm9zJWHW zzrn$VKbm*aPR7Q9YCgdKdoVijfs?`+Ze=Mv`;`6C)xd{$pQR3#Hzo%!%wK-oSiSn# z)nML5Dzvti5oe9F@~H;(-md6l;^H1Ff27g-{@}UieC>a`w_dP{$s1VL%+EfyNMn2? zWuszoJ-}0e@mjFwb@*Rp(5Fl^e zO2>YK3waod;g)XWHfP2-#NP_goU99_%lvDXd-6t(m;>&NLXW%Hevs4-FymjlaskA$z9bsjl&YkPt^7UIIG@ZV zn$SwPkUGVZK0>X`*Aj)Y7S$Iv_JlSU1+AvB$0nqIJ(1z2GpWO$c!s?xhTLqvUM?9* z=2fQMyM;1U3fq>z3Q!n4C%aVMO)C0X4{xk#HqJ-gKDOz+VhoEW0@YGKA7nZQc6b{W}&pjh= zCO1MmcSRhTvk@(0k)?g5a^AdnaM~dxlbY#EOsycAx;A=-;oKz(-(t7mEYTR^Gv?%3 zoW=xE6zYBRkwU7Td13nmBfH&N#P7({#VZp7cTfHLzAE{Bl}YvbUDfN+PrEEWNW7-$ zzWFKSjp~!OXCGJ02l%R8jCXQPmC7b4sVclSyjPf>mCbEOeD%->G%` zpBmt!C?fe&#w!2&pa2Q>?eo9?{+^=#=^pCte$YAIE+d(r|NeHj*0n$X?X6aRXq8`B zW|=rUb({~LZ1)u%_ug(CO`N*Cy6k(ob+)c=!++O#uMf{8(lCSDer~Eby5&e*zAKvWWTw%nI?)3cQTKbE60`$>)s2r2E54n;z6f7AO->-N;38`3qGA8$MR2JGmgN1D?IVVMlvfe(b)W`1qC-X_c?xb*ba zuLxElQ=Y;*odE2<9O*=8<=}`KFujP}EwE~Q0DF(cjS%K5Vi7KkfI~3tm`^n5Z>#Mn z8%nk~GrFalG-5I}WfjzFXJx-N5Nq#rBeX%Y--_QwltIM7OnKB3pL<~Xg)TQVKg7j! zuZrJ;{LErF-`!8-S?EIZ{QlmVC1;@52|>e>&R5nTIAq!&dl zy2a+Us7%i!vqs%<2`%TmSixyxP9s#C*4(5r2D>NFS<3p=E)9>-e~H(+JASf&SneIY zc$t5(`z+a7w@{1|W3MF1{#^paZ&BCZJ-9&jUyoWL{TB`8edAW{H}h?bZ9fR(9X=X= z{&;CUr`IFbtM6Vp=Ns|Q+t;t_w?D%zUI+bScqDy;(F2&ED@FUH^o%gQ$ne|<|EhLA zbN|OJ&iS>KMY=1))vn2+>tmC4VIt3AIYaxVwMw#b($h))Sn9or&qFk!>lgd~8~nLG zw!S>+9$sQqbM_S#S^)Pn>A^pcH;72rX#D5ZcHI zQ85q{Z+k~!#v^ZtF$~!PIj|?r8YstTBYuODFj9`ppie;SZ69U_ljox_WGDx!ulp~> zwShQByJLWN!y9iur8!Zmz^JZjZxe`CLvW^M?cK}FK3!P?tr@bM?tg(Mi%!#fF81gkV*tC`0r5bV!sDhH$nDuKqBoi=3{4`dA%(n&fN}k$V(WMgAfDN zJ9j^FgJ|J3Qm}*0TbrFAS&itj70L5=NFF^v_FQ+?)zzS9%Mc>Nl~cwqWZ+u283-g$ znF-0*wPWIhkbXsX(`i3`u15Q8UILfWLAQZ=f7dF1UdaxB=6YDc^g589fpn zg%Cv4qh|RlyQ2;lQO7!Jd~KT<=e_#^ff@!bhu{b3t2)2a|Y zi8q92unAth+GJHg&9ml&L$LVGnF7@Dg^T*q2*G7KXnAB+SI;e%C?kN@2bAH6T5UR) zmWdS8kFi73XOm*0T*h+9l8NX_2b8^1y=zPq zZyg*@-*VQR7m={3c@xIb-vZhtFFTg{we(ZiRXL10<|=PIV`tVy*hVcu_yV**N171)khlf_f;SExehvV4@u1Y zN1`^r0b-`E7bNE%H};&nIg(Ez9{QKq^7`j~I@&u{-OPsy>V9C(&g7N@vHfT35??v4~^)E zs`nb~Lq!XBE;7Br&3lRK$PY>hmC;=UOa?{?1{H-G4=XldVM=B@NWc0Kf$Oy<0Pz9W z--!}hPx-L9dqM?)af03wNLAFg5dOzPQN{MJs3NO;Q;sC8G(t?g!4ETOJVFB=@0^)L@Wi_gq46skA9G-lIc963|Xa<9(7TFR3y!#Cw^fNWltnOc1h&k0HHX<=+q&y91{xFE(q0rh*Xvv@h5TVC}rp& zec)gKMXYbD1Mkl+7Ycn)#~!rS*3{HM$Fbc_wjcq2AiN%oFs(t=y_uTz%mbOmgAuPV z&{aBNoC0(ex6}+hCMdH51QVYcbr)@3g_<3qnTi1&1&&w;Pk*`-W>~cce7A%g@h3lj zY5VD+&2Y{ksYQ-qd%++?F#A9dkBofmfc(M$Uy)*1>L>N&%Jp-JD18^x1KqSvpvJo0 zoT7|FS#x2qNSrRp35;xCFo8h5PbA4PvHs`9V4qbh%YrTX=S)}L_Kw%eCpoQdK7Eqo3vO*o{v*6 z!x+oouV50KCa0@TSHM2Il#{E^!aEsg6M2fVlTOKy;R44ZlIXon^|>c?>LXf^ule0~Qo4(~O8!E3%c8`YZS z_b2Y?zs$QH)PY~S?mqSEGyHjhT()NcxAAOKtg2rq9Xllp_9s7etAyjvz1G^5Rc+Sl zkX8IZRV(Bj#_}glX4q4z`2H`J)i{OX(gSvbwreZOpSfU{)9~#>mFj-kV6QmZyf_P4 z+19wZi6K|g{X8P83k0;^*RCyxUOnTc^owIP?#SyhuK2-uS7*(i`b_K)U5~ zVKSAVQUVedOwOK#s~Q9G>LeWiA+Z2l*OA>L+nw(NfmED!vSwr{(5N%RA$^dB$f^~$72f`3kOUUpOQYEPM z9toGxndO(!@ZBH*&{&ZX6ix8hCUAoy|EeMEaX!2lf_1&&PlO?<+af^G>S=s{gK^`@ zxH7gmA0ZT2*R~^NzL~IeQ*ywwRMJpE5aLa~@WDZi;ELElOJJ(W4)D#%wbCaD>DCne zO(hwaeH#+ml)>JFs5pQ&(p-{{*~?B?oO&$bE`g|>0GgVDRKL>6+y6>i?>q5aiBkm> zJr#8%{6Faf0%q!kgPN;3UeK|5J{&QfpLfgA8 z4L_EU%!j6zq-JJwB5S2Y^O@H&Xt*0*pdko$WwQ61$&#dpS_$62;$ck~|K7OGc>V zEPb5)TkcCJZRfY|4)XF7d}1kg!)4UGXdfIcK8_e4rfrt~dtLC07wu$-%uS=lyY>mE z4qg48)GYfEUKm^ssOb3;!9`fLAO2Qp?$u8fV#AkzMHc2=y=VjO`r7Y}A5HN7YeinC zli2X!xX5rhTpfoQJ_^}$WwuKoA0B+W`1>z16_kT3IYPV2kY`C)2w#~ zBw+1SM3+k*IIS}D=Rr~1>fCd8fO*`s0fB>Xb1+pThpiR@HxRhV6oZlmpz1o)`nYxK zGYp+omfIHryj>;=ejlJ=Oy(JOi%W-uiqVtShk$LD6HL_6KM+jq2Exk~WuTw&l$yiz zM@d&tSlmFb$y)%gk0JP_%+~~R4!mMAH#$SPe<7FMKX{5+uSzsSB`JMVk%|X~KU_m@ zA-Y}&3w8(MpIR$Jk3r8{HoZIKv`=DyykJ6gGAGB7&}Zc|?b~3yq6LNUc`?GZ#)xiz z7yiDeMW==4u{if{1c`UM-5iMfc)z0An98E@FSF?ynTRQsDy!)U%--^*WoY|iOzKnG zZ5b3uw2mAe${Rm(>XDg_DLu%N0KqRmSGqXl#w4$DN;7@+oE%{-^F~7sfh7OoPD-Lj zENyNQ^aS~d;w*VLBHQfO*HDhE6a%Yv{48aCgkQzknN6+K#F&VQ?XCQ*&2eO*#Rp$8 zAFBr@&7MqZ`FrVuvd>(!Q1&AFG(U|Yt6;FCORgbn`83JM`a=cd!{T16a_VW!`LL2o zBxiQ_*4Oi)ch>a3-(efA=C!EhwKdnNi%hXM;ou>l-|bBZeBx-!va*QfNJZQv`}?KqCLc>mlmwsAL^+1_8ZnhO4jUC~B2 zN#!RJJ9t9gvn))EcElC+-R?_WdsD#qO$%MmJU)yC;ZU81oTJ9Q{4>oQtUui7?bev= ziv>EgNMDjE?9sLxFXX$2aRXyoK6jO1=wqFJ_l0X(rqG5*Ml+S!d@!& z+oMHdRHC7=S^oGkXx^-=0dVw`F*Q73P8%UgH)Vs#KN}=l2<8Jl{*gdS{FIM@UX@Cg zyVBCLrKLqr@V0kIe$Q=##NiDnQXy+f__tD-Ok)`y)dMTMGim? z_Hmw!Z~*iCliXPcqeuo*X#enuDCEM?1jLyGP)D_5{zq#KUGCqfA1<35!ny!Q2x1m~ zMn#B?Bo;9$DQ#2h(y_t}mm?lNu<0Cdgb-GeJv=w5T#}Ho zUM*6xq@1p#1x1G%cAHrbn1F5ltJN?e{^X{||oN{WHU#QH)ZQzpBS8HXW7;;V{a#jR6`hJLc(L<W&bUko z)l144-5_3xfXi8xhUVmZ6!p)_vd>&*0;^CboK@2k3vTMw<{$O@xTt1FvNM5zd`5S$ z`?`=f2FftKIdi4zI)JIw8^#8S%V+$`uh?E%U|hMho-J&1M)ZN3eU@kfhw&z-`}nD% zKXmjeH+rI$1~$)rn-lfo;bVKAN=$S8C~Dl5`3KP%rThLG*Z)RQyf{3|H2djc^DXIP z!uY-0M1o5B5m8(oHoqvQU%uC14g1X!{cq!tYuV~4QN`}U1X|SR=)CxC>Is|r!E@Y+ z`8ghL%zhvC?Y~@&5?HbrjGF!70A1$w+k_=u=3mnF4R>xu3;x(g|2~@ZP3^yh3Ifp@ zq`4+LJhVP=j!mI4=_8csdJuiAMCqjyG7?cn|(jx>Zl11|}S)RiqhyA(b|9247y%K;Hxk}`X?Ovd#0w?Mh zsT*H;M_d5zkr_S^jU!_DtBPVJ<_`S};;OwglV2)dN0^42fR)ah$lwwIgGIpsmJ(#> zYT^eQ(as??X-oLIVO|Xs%lC7`(#+Ts`5Yptg7zu;}uaOyx0T7)Dtvmq$#3=ABH2ykO(Zx0Isdt`%mlAj2sm#-VHRm zG;6v`WWgj#OX;$PvY+&y=rkZHYwF4H7N;8MpTX2DyQQTSM&J! z!!D&~Z%1ifXQj%j^z#Crq*$G#EzQ75QC?@9w*F2m`7Y^P4e4BU!n+>{Ym&5mk!Ixk zW#4#ohfP#snTs(W&DhVDOTSBbGs*sBlD#oVXS0g-saP!4r-XaUwDKD~O7WF!o|pR2 zZO!-c2$X-`{+anzfy3;Tz~cd?jq4@%WG}wrZ`RH&{vOBqm*Y!cxUAdE~H<^x-`C#pC$qRbR ziNEskjTZ3{YcpL)fBA}SIqtvo-4yCi#h*9hze&&=x|s|Cx;wIIE0cVFebuYFE6H~f zv%Rf~+NDN|WLPNhR*MDQ$}ly=V-J)tsGwBrCKPFkh`JACgryLAjZ4b9;3<)@qy``Z z2nqg!6OJ+t%LVi{IP(1OAui!ayw0Ma*GvKIB}v?pMTOenv`{=cA&UZ1)9ru?qP^%6e~59Z_t?rVz8T#aPt|`hFb}1 zfj(`Ws$^LDU9GF{R%;84B^&v@eH;u;k0T)1##IKSfFCczFzCvRKX8W8d42=RwMY{>?V;jdq_et#5T^z`>Om@+{x3zDV1qSv4?MyTL7Rz5?&`^p3K zvY^K@^+0re#vVpyiiiZGAL)f*k}(N$4N!Y+16Nh13HsoQ(xY2oqm&P3OAuoohSM3a z*Sa0n?;5?o57qeiR02I1qHP`+W$KCRp+H?xx~K|{f~rD6`~ z2nYfd?*gBKhg!APmx@exPiKS8i+&0VL20+46FU>x*d4F#gl?fc+B^a-aXwLM7B)0- z91$g9fC+&K#c2wn3F~zhF~Y|->N?M+y*S((f3W%%>oZacDisE3Y!M_~s~V`%n~Wsq z6Vy4MzOFtZeAw?jA%ECNL*k*Y9`JrI_XTCKHzap8hHEyeX5-3i>nKsTKVCH-s?yi5lf*@*pjI)tZlGhv&;V zfdx6gHvgUT4Utvsgb{#60N-AuL4CX~#Pg3m*#G!g`&UW-VK8*EueM2a=m zEzCSwXl4{=3C=Y+KV53vvVAlyeA*?eTHn0PRM1bxk6>-7})?_bW}+oza*eet2VoFhviFZXIj_01;&PG*oQa=0?{u_XBAEt@Ya zyd~hgbtC)3TaQbI;{t9jo^i*g7(c|Hk1n!55dILqe(vpek|;!Rv0f@dfx__xS@H!c zB)OUJMqJ^Y8`_3K&YKz%701nuDbV#U{8iP3;C!_3o%_+3bB}%^DQ`2S78QDf=R3_; ze!BJRx>;HMFHanl+7H?z$F|Vbe#LFnkAMR(|UdCK%bP>?P0Onju<>HkJ=p1vnP}m#xtO zH1xB@RInnz+Y*K)*=0@8QzpNcVQ^&52$M)&|Jn7w54?wtQUX#DNGIuA`&i_%&o0a| zLoX}A@-@qBM=I#-Uv`r%g8$N=EpS-_Mk=VB%;&&s4OxM9&3q2(X_zb`i%r3=rgjb) zejuM9N?*8t1l%d~AjiG;1}#cKrs-9{pxgKU-Ubf}W4I!jAWVo%z_U^LpwXCuN$cUb zAqc7j+{(6CwLwS^TFWK_z0v|u5|(7SOUW2c%f39BYEJ2*5Wr~4?tbRxVM&&m?5rGo zcbA@6nLn}e3u_2##6?LDB*hRmS)F6Xzk?$EOu5Xd37ZD8l1B8|r2a~bhz056Ra)mx zKCMtpz-J-_1wy{;?wF~nJHI^3&s5znWThWJ8eVV$3>&b&1ee)EWMsT>d_4gRoPH}gGT2oob7i_AP$p&x(sCIgp|onToS^9%X`e|8d|~NVs&PUYwbJD$4t7l z-+g}!Xyt{2Zt5A7R~gCh(_KUXY-x=&d@4C7;{;OxKf+85r}E|aCC}NJ5yMre9haZU7^tL$3;mkFFb_I*og`c}f`&QwyX?hyV;& z3w0h8A*^7YJ7Ivf_i1$303$TX#^4_6_ShH-W;?QhfiY48%4|Zfw#a8&{lwk-=H7{r zOlT-6YS(L3$STo4FZin{O@lp4cTNC0hxNik;wIOT72`YS>4h(rgXx&RwL85oo6ZE! z44-#vMir5PQ2vrmDJ>Ps`xBojMeU4JZZhiAvW^J;5I61rHLyWm_6NFmQy3XFVHry(E5>!(|f?Y%#DWfp+`%iI=$Y6 zplLq2lOO}%r8}B9?s(IDeWs}@5`N*VDnA*g96yDaEC`r%#J0 zwa50l56su(*{L@M@3EMJRHPAbR{dJFJgmh}qufrByT0=-4D(lZjfr7t#)^B->AUS{ zGhx_JY$nEUDqALv?`rUr{gA`7ezR zdk|~TTZ3JLQ8Fp_*eF_6mNl6DTHhsqc+hkGcb5jMjJzlDU#PiA$Rau<(y<2bRo-R2 z75^4x<6-}Sut2Jz{$1>bvwZ@$FN{d<{OWM*P=8RrSU+*WYMG^J z<9EK?mytd*5;6XY=VEZce-^TUmB`~-h>#N1c`xeJUX5_(LA;4 z93D(zPEJW{Mi|R+>7igWIeb!e!q5*1T1q%;k^HBoYe`qnp|0q!M+*#W(fBVk4S#tJ zfxMYZQ+lDl4S1eD{GmCCl@AyujfK+xTifVsr*>2F^3!7jC|Cd=feiSN?4?*ZPjwSq z)2u|CdwXjjMlEJx$y@c#$=hu3|M!N(BodaEF>n-2LiY0v<=ibT=jY}TmN^>;#wvvI z;2MB}m<+T7sQExNnQw&G0PHCMqLGG5ej9ayG9j2(R;r#bqLA$QouD@jO7hEWB64?X>q(zBM)TG>*joK`^d@YZ@KH#ax8oO{aW?7h!9d+)P%^ztW#-$SonjN5lXtTzAd zHfElCtwFV0&t8MA*Y;F1#cT2%OzIA-+YOI^2xK3r@tE_vNZpPzei%#>VOUk-ZZ z&+1N@tHDXS{+nCl&HONaE3t`pw3Ih-BqynwJ49mMb!&dvB6H@@u;Z>!|5>Y7A;S3q zjzOXiRb5wp-Y9VZdmBffB3e4zu`mty>l zy5CDOlb9KLngjmeFC!4Rr9a|RCIdGRE~=5$XPd2`b(4iy=)Y@J4F%gOP$U=tCvMru zU=6fVb4ktq3z*Bwmp3pvL`{v3Z|eST)cqay#yzUWnRm1{NmQ6{{*=5Z{ZxTOdk=~9 z|EI{l0wE#0TtU<;d<6;5=9S$7bvhAPFQU#P50*9sE(zKIz)0(eh>UsyPPMmRK^75) zWj7~~Ram)m$b~~e^~m~FSL;|U%a>qW2L_u#Wex02HV*0S7`(CvEb{R{Rv@u3_zS&X znEFR%6hS{;wSV&y7=s#*1ox=tp-T!DooCl?IJh|uIj?6imVsp+N)pfHFn|5{IH|mU4 zt)KYbYfiXScU#XodDla#ew8|1|tq%vC4cwK|0r@PK;f^ija>Uma+KpiO+lDIc?q<>=C7*-zM6kcX4B& zik;W45f#D{wB@J*?J;%qBrNO5k<+A_W|i%7-C~G$3#79&sRl-EZcXirK7s6DnETu~ zsZ5j{O1`=p$p^ zIVQm?Ob*m~ltQ264p~{y59y9qx-IK5bFL(1hK`yg711j5T}|ji3g`b17v-aOc%?LN z+Tb_tU8;XHui%E9EaHijU6;}f=8%aU@>`z>IKe85x1Op`QGfnCyyLa^XTHk^^zZ3Z zcY7Al#2&{sZ;A|z8M=oT)r)y_;V*Lc?gE)2!0 zDGs&+l__D0DOoQ25=Chp%t_wI=b!l8OH_vn&o&rmV#Y)biGOKNMBJ>?)ID@0)nbp0 zKG{#4Ci>W8*Zz+Cf9*~gy5fSr0*zP~`Z_R^ZZ2h&0nQH*tEwO^}6fyX;tN9MMBHeR%8g%Vo#uetB zRxaC`==EU;S>z<+HR*+~Qd6n}VOn>TJGb;~S7tnv^i!qoIKHFji3a1FbkjHK5_K@_ zTHiFNmOURVd)`a?@91e0mX_SFG+hxNt~6Z-O`Gv1;aEc3`Gl1mxe;5etFvBNyP7^R zexX2UXoypErN+OGCn3)h=S6aKLVVKecp`9Gb`KM0-Ko_rEwbrM*U#@7OIBr@6DcS6 z34i(+v%N4_!|Y4cyjz8mSANv&4$9(%D-!h+QS&APmWK`bxBpoh5M&T5R9PC}_9cpw zSTD1op5I2=M``kr@I4E5WS@`H?(EWqdQah$*H(GSofi9>q%S0QK0vG>IyLq8OG=;V zS_?gYCsy%}mXuMOtl9;n@00Eq5tYS7jxGV9|Xrkh(^A+De?Le7?Eq4vLtCZhLg6xdS9Y1B;MTzpw z5(hU+Xm1uT-z>U$2BY1Jw@b9UDwC!jyLUL{AKaJN{h=u zv2S&uYk4~LQM}6)Wy?*HFa1d+HifA%TRKXYA0JopelF0_bR2Wps?(lS$L>-jiCmWt z-GKXQB{6fE-IpPwr&bV+*>qgyRH^HWD7$~ec6OFu5>WjtVt0p9aS^51_)6wsIO>MT zP1(>ehEGhRK&~oV$L9kYbP^?+rlfuMOFVqxk^^qG?Q4zf@P(>~3L4EH9KV4TGBOh$ zb}!tvt<|;wY~E|Z$plL~;)Nc&8ao@VS61wv^sXE`Q|z8LQ$)ye1|u#^y7AB`eWnQll5NL&OSU4T=6^Q%=2D@;9CcxFUY>Eg3zX>|0G`! ze|f-P{|o<#zTV#Z_<^ zTH|?*@_){#mm|6YFkh7Q?4%v|$Fd6>1XlY5+}wOerW!LwB|yLk2OYgvr`3?j%p&_h z81(tafjaJDOM0;_IgFfyw+CY_TExPfO}%;M^XbdVv_q&nHp20W-de}wMlr@2v8P2Q zcXbRo)_A`-_a<6gNItsKgMYQs!`qD;MXqJ}fST!nt^gu;nEx&>@+wKZa5-X_ zY+DTr$Wtzy$Y0jOMiGbPiD1Pcc#-h<%*HXaZt1rjv_d}D+%($?19p>h6!l74DSo`t zU0yFYO1Jq$Ni(dZWn|oEplKk$apn6PmV3lcf?FLe1HOBmj=l8MCIRlOcw5^T%Aww< zwb>IrBSEYZzbSa{!RvSGAt_nMG&enSn@u;Hk^^PdhAUGV&knU1UYXR|d+7SUz)ouW zSn@u%DdV=vel@puu{Ay7myJ8SXC*v@Je7R{>grN1YkGE%Hpj*4r(B-vW64-0T1pT# znf3ZFaiU;YtPNIJEg*~B^S!dfj#h~?rtgJ|Ra(F)QqOx0_qY`zXhftKCbxkV} zmuILAOFg~;b+DB4YYTR^1KmbcS(pL^wby~x`I%>w>Xh`q9^u6E`VT0~h=-jddnanm zw+4sWc)oZh^Kz$&$RCIA;T7J<-NSECzdEz*FLpX+W`!)90lrup1%kW2up+ce>Yhhl z;p^iW)z2HTFM}ayE~q~p3{s70*e>9n+IBTr1?^aAlX0Sr4fZQ_+bcV<$8IR4ToaT( zd8;37i#X%KhX32hn$lzKm+T5aRYf+Yiq#9aRpuQNWbtkOU^OYv6byfZn6{#tqdd0> zzHrR}4?3WqK>KWv41OCm)_pb}g{GL?41F&X0%A}_Qi;;HljI}R$ zG1v|iXzUhOtw28saV}Lv*RynO;I4%7n$}lx)el+#p%@8yhxWkm(*G4a>R7)T`~q)+ zcb4R+i!8*v)vp!hf^yXXzd7rJN7<>rq;mW}Rp6ZjhO70mlfd5&;$~+^4+fkN_mnU| zg$x0J8?QkaX#&3~zt)d2Ng^Psxrp{UxQE-8`BF0Uk;E(G2>Z;O*a!i8kpi2{xEROS z4Y8uaqtbYFnr`T!u8YExTgd8+GcxC_zDPHni!qa!+~RXT=7WaMH@IoPG+wDpy6JG& z1%U_B1aj$WteA|Sq^Xv#RIDS8k^auU+ere4lx*lr$#;=oZ*GrOSNZF2iI>Krzn$e< zK7h&)?$qM0@Ewz8?_@0qH=gM-`8Pu6NK+pqd`K2s5VyA6H1jUeEb zqE{Jw+Ft;&h6bDjp`cUfKM zPW#9PQ)+PZKK8yntgevAP9yosD4D%R;;Fj@fpmf7mDfEG5ctvR$deTXJ@TO-=i|$PeDc zn;X2(om9-5^lR!-a;raT+0q`lA%n<2I)e@pJLbtg3zv$kme4ld{jCDey6bk5`UTu9R0uW_6G z$Lhk*Qwe{+vixIV^;Gn?#!y*j;-sNoWTqbTW3;5B3OfGNJ5ALyK3A%P753(Lu6}42 z?$;T%G;bFah={&_O?spt>dU6ma|Ht$RRae12C#Ai8XBF_8BP52AJB)qY7FY?MFjn7 zB(^OGC_lI?NPNbuw+BS=xjTXwfEx^EB0fUHVf4hrN*nR*m~D=`=dpMbnB}gG zy(@Vz(E_4f)0c!ysfAAoxjQ0$Pw)(}NmgTgN-$CJy`?;TOOAr(43jXbL2dtd6+_RJZZ zK&hs;QZwqbQ(7<7x2q4FOPD;DM99aT1&Mr@-krsLwgu~cVATfn5bfCQ;2jw8itw*3 z0a+K(n8#gMXU7Y?yc35p4PL&!ETkd-Ut!ufk9$?DVYf#Q(zau}S+GDew0$toEk^xW z@^Wo$i*6t+paYJ6VEz|d4Zav*QJgCO2jFo5)D%w^=YUPzFZX$feHWhP)Ns0(s7?W6-h#xoW*=N=hye)G~;$5IkvMOa;<|9de zodE|`wMgQTn;IlTUnEE+V3cV`w5n8w zpGr18l5BcnvqxfKY+VNLq@YkJLIMiqA>b$+@KlI>@@ebG){v+FZ7m^Bvq3Dd&&Fr1 z|Io6j>mRS9M?)G~S|_~fySrc&S- zh2lf8Q6eIma8Md8BS=AMmL^+cQDUVinbK$~T!G7QBT7U7*$EO*q5=dN6iO3Lqu6{X zloqnn6p%r&DHxQXq8wX0lR`i#u+hdc)b8gU9cNBKxZct6^!dkQr#ha0JlXO5WXH$W zr^lX#z~|Win=#1TL~BUM(J0@&mmAPV~l?=LGrHqJZR4zu$UbG@MT1HG%3sq5?8_UOLFc^F& zIv*dME`#BFhYG)Xqc3>(-H#>(Avt*p7qfe^4X%d_T?rXAt92S1Z8h|@e=?js+FAjo zlKI#PWv`=w4VCe3K?bs(ewp2L$!@y5SBfj4nNs1|;JHtc(_w5o;wK6*8eEky?g6Ry0eQ@s z#1H#S@`8-%>_ZZN#)nxpc{YPI+euzZGhK2qopmuDb0N*9qh`}#*+lRnWjf|W8cT&U zrn9hjBEeG-3EVv91XszkX|R#_T|UfakZ0532J)B-c}j{jd!PL20eSWTY4!p6(?ipx z`zx~exf%L4Js{gS13}v_WDZ ze)7+UsIhM(B%N^cLwJhJpJqDiOrDim&ld%vB~3}fCDPaf(o)BI0y4-;Y9F5<^$cmLlROJ=&md1plV>4@d#I&! z(%(`jaOA%P3Fw4qp$t)QfMmIY^b?-y+|dIs`UbB?-phZJ#E+!&!_q_4QU+>D3ZnV| zk*q%mrAk8LIY*j>Om!fa*5E~*Wc~*`dcGrZ!c}|B)lv2$U|!+#c};Vs`K*1BE3<6#VC3C{AbNs#%!?Hq&q7jk~=TwGcc+_?xz z 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↰RMp`L8>#SRr#gewo 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 H4G{f*Rtx1g literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr.meta new file mode 100644 index 0000000..24b03d1 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr.meta @@ -0,0 +1,99 @@ +fileFormatVersion: 2 +guid: 8f9333d1d50759749b86feeaf8c086f3 +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: 1 + seamlessCubemap: 1 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 0 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + 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: 2 + singleChannelComponent: 0 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 100 + 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: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0/ReflectionProbe-0.exr + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity new file mode 100644 index 0000000..269dd8a --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity @@ -0,0 +1,3448 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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: 112000008, guid: 4bb692eb322f5154c996f4ae7d8d8bb2, type: 2} + m_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!114 &16174716 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149171090216} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4172921150 + SerializedTransformProperties: + Position: {x: -3.1, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &21877550 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149175611002} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4240178974 + SerializedTransformProperties: + Position: {x: -1.85, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &134092196 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149153736944} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4022205918 + SerializedTransformProperties: + Position: {x: 3.47, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &506336288 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149525981516} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4223270686 + SerializedTransformProperties: + Position: {x: 4.3, y: 0.5, z: -5.39} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &681283498 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 681283499} + m_Layer: 0 + m_Name: Trees(Networked) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &681283499 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 681283498} + m_LocalRotation: {x: 0, y: -0.7071068, z: 0, w: 0.7071068} + m_LocalPosition: {x: 0, y: 0, z: 15} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 543450652058643369} + - {fileID: 543450653720097984} + - {fileID: 543450652669645364} + - {fileID: 543450651910458001} + - {fileID: 543450652830724776} + - {fileID: 543450651919309379} + - {fileID: 543450653608079946} + - {fileID: 543450652970055944} + - {fileID: 543450652415889240} + - {fileID: 543450653193892854} + - {fileID: 543450653692504466} + - {fileID: 543450652965335488} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -90, z: 0} +--- !u!114 &743347758 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148689251194} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4177246686 + SerializedTransformProperties: + Position: {x: 2.88, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &763797222 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148710745010} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3954834366 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &877696326 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148825171986} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3971594750 + SerializedTransformProperties: + Position: {x: -3.41, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1072264674 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149018544270} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4194171263 + SerializedTransformProperties: + Position: {x: 3.58, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1080970248 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1080970249} + - component: {fileID: 1080970250} + m_Layer: 0 + m_Name: Waypoint 1 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1080970249 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1080970248} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1521876172} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1080970250 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1080970248} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 70028e1b1708627408637ab9a3cd6bd4, type: 3} + m_Name: + m_EditorClassIdentifier: + WaypointIndex: 1 +--- !u!114 &1284073654 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148156618210} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4273863999 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: -6.24} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1369662610 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1369662614} + - component: {fileID: 1369662613} + - component: {fileID: 1369662612} + - component: {fileID: 1369662611} + m_Layer: 0 + m_Name: LevelLoader + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1369662611 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1369662610} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3942514143 + SerializedTransformProperties: + Position: {x: -2.5, y: 0, z: 12.5} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1369662612 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1369662610} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 02e5160f1a9b4d047a6cbdb3f094a86d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1369662611} + _networkObjectCache: {fileID: 1369662611} +--- !u!65 &1369662613 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1369662610} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!4 &1369662614 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1369662610} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.5, y: 0, z: 12.5} + m_LocalScale: {x: 20, y: 5, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1521876168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1521876172} + - component: {fileID: 1521876171} + - component: {fileID: 1521876170} + - component: {fileID: 1521876169} + m_Layer: 0 + m_Name: Platform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1521876169 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + 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 &1521876170 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 83bc8ce733133754198df883775fe67b, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1521876171 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1521876172 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 15} + m_LocalScale: {x: 15, y: 1, z: 15} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1080970249} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1649773637 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147583332627} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3971873631 + SerializedTransformProperties: + Position: {x: 2.0644817, y: 0.5, z: -0.2750473} + Rotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1780989355 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147714523385} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4173069182 + SerializedTransformProperties: + Position: {x: 1.12, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1789832573 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147722457131} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4273995711 + SerializedTransformProperties: + Position: {x: -1.97, y: 0.5, z: 2.16} + Rotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!4 &543450651910458001 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147722457131} + m_LocalRotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + m_LocalPosition: {x: -1.97, y: 0.5, z: 2.16} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251242449562} + - {fileID: 3745622626914980340} + m_Father: {fileID: 681283499} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: -75, z: 0} +--- !u!4 &543450651919309379 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147714523385} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: 1.12, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251251432008} + - {fileID: 3745622626906128678} + m_Father: {fileID: 681283499} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450652058643369 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147583332627} + m_LocalRotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 2.0644817, y: 0.5, z: -0.2750473} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251109747618} + - {fileID: 3745622626766549196} + m_Father: {fileID: 681283499} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -50, z: 0} +--- !u!4 &543450652415889240 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148156618210} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: -6.24} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251878133587} + - {fileID: 3745622627482932285} + m_Father: {fileID: 681283499} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450652669645364 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149018544270} + m_LocalRotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + m_LocalPosition: {x: 3.58, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252631011903} + - {fileID: 3745622627766045009} + m_Father: {fileID: 681283499} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 125, z: 0} +--- !u!4 &543450652830724776 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148825171986} + m_LocalRotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + m_LocalPosition: {x: -3.41, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252552786595} + - {fileID: 3745622627605080525} + m_Father: {fileID: 681283499} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 100, z: 0} +--- !u!4 &543450652965335488 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148689251194} + m_LocalRotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + m_LocalPosition: {x: 2.88, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252418961867} + - {fileID: 3745622628007602853} + m_Father: {fileID: 681283499} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 30, z: 0} +--- !u!4 &543450652970055944 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148710745010} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252398516483} + - {fileID: 3745622628002882157} + m_Father: {fileID: 681283499} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653193892854 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149525981516} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 4.3, y: 0.5, z: -5.39} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264253197038589} + - {fileID: 3745622628315850899} + m_Father: {fileID: 681283499} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653608079946 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149153736944} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 3.47, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252763976257} + - {fileID: 3745622628438534447} + m_Father: {fileID: 681283499} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653692504466 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149171090216} + m_LocalRotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + m_LocalPosition: {x: -3.1, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252747704729} + - {fileID: 3745622628354142967} + m_Father: {fileID: 681283499} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 80, z: 0} +--- !u!4 &543450653720097984 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149175611002} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: -1.85, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252737680587} + - {fileID: 3745622628326289317} + m_Father: {fileID: 681283499} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!1 &832149484144961477 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628438534447} + - component: {fileID: 5334037119321186225} + - component: {fileID: 4539702728275739502} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484184002895 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628326289317} + - component: {fileID: 5334037119345154363} + - component: {fileID: 4539702728364688868} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484194823197 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628354142967} + - component: {fileID: 5334037119338529897} + - component: {fileID: 4539702728392677558} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484567537273 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628315850899} + - component: {fileID: 5334037119695527437} + - component: {fileID: 4539702727884620498} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484774665351 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628002882157} + - component: {fileID: 5334037119954031859} + - component: {fileID: 4539702727638763564} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484804547663 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628007602853} + - component: {fileID: 5334037119932537915} + - component: {fileID: 4539702727643480292} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484939159335 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627605080525} + - component: {fileID: 5334037120066363219} + - component: {fileID: 4539702727509394316} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485083379643 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627766045009} + - component: {fileID: 5334037120261849039} + - component: {fileID: 4539702727334699792} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485328751319 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627482932285} + - component: {fileID: 5334037118326170275} + - component: {fileID: 4539702729266183804} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485845462566 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626766549196} + - component: {fileID: 5334037118824515154} + - component: {fileID: 4539702728885328525} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485968465694 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626914980340} + - component: {fileID: 5334037118965753706} + - component: {fileID: 4539702728765471669} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485976391628 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626906128678} + - component: {fileID: 5334037118957828024} + - component: {fileID: 4539702728756489063} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147583332627 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652058643369} + - component: {fileID: 1649773637} + m_Layer: 0 + m_Name: Tree + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147714523385 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651919309379} + - component: {fileID: 1780989355} + m_Layer: 0 + m_Name: Tree (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147722457131 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651910458001} + - component: {fileID: 1789832573} + m_Layer: 0 + m_Name: Tree (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148156618210 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652415889240} + - component: {fileID: 1284073654} + m_Layer: 0 + m_Name: Tree (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148689251194 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652965335488} + - component: {fileID: 743347758} + m_Layer: 0 + m_Name: Tree (11) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148710745010 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652970055944} + - component: {fileID: 763797222} + m_Layer: 0 + m_Name: Tree (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148825171986 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652830724776} + - component: {fileID: 877696326} + m_Layer: 0 + m_Name: Tree (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149018544270 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652669645364} + - component: {fileID: 1072264674} + m_Layer: 0 + m_Name: Tree (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149153736944 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653608079946} + - component: {fileID: 134092196} + m_Layer: 0 + m_Name: Tree (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149171090216 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653692504466} + - component: {fileID: 16174716} + m_Layer: 0 + m_Name: Tree (10) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149175611002 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653720097984} + - component: {fileID: 21877550} + m_Layer: 0 + m_Name: Tree (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149525981516 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653193892854} + - component: {fileID: 506336288} + m_Layer: 0 + m_Name: Tree (9) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1300264251109747618 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940380560399} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652058643369} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251242449562 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940530957623} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651910458001} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251251432008 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940521975269} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651919309379} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251878133587 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939891603710} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652415889240} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252398516483 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941484641966} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652970055944} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252418961867 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941489358438} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652965335488} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252552786595 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941622396174} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652830724776} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252631011903 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941785326994} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652669645364} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252737680587 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940868649830} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653720097984} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252747704729 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940894281268} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653692504466} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252763976257 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940844451308} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653608079946} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264253197038589 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941260735568} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653193892854} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1622145354905184162 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939891603710} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355406353003 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940530957623} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355414155961 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940521975269} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355555570515 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940380560399} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355750251276 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941260735568} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356041608250 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940868649830} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356048613736 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940894281268} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356132981424 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940844451308} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356266240718 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941785326994} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356394764882 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941622396174} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356498440690 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941484641966} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356528326970 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941489358438} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!4 &3745622626766549196 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485845462566} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652058643369} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622626906128678 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485976391628} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651919309379} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622626914980340 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485968465694} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651910458001} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627482932285 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485328751319} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652415889240} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627605080525 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484939159335} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652830724776} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627766045009 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485083379643} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652669645364} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628002882157 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484774665351} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652970055944} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628007602853 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484804547663} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652965335488} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628315850899 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484567537273} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653193892854} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628326289317 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484184002895} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653720097984} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628354142967 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484194823197} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653692504466} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628438534447 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484144961477} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653608079946} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4024461403818014209 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941260735568} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404223813565 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940844451308} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404241095781 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940894281268} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404268687671 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940868649830} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404358629315 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941785326994} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404452501343 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941622396174} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404587635767 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941489358438} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404600745215 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941484641966} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405119892143 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939891603710} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405755740006 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940530957623} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405764591540 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940521975269} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405895536222 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940380560399} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!23 &4539702727334699792 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485083379643} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727509394316 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484939159335} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727638763564 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484774665351} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727643480292 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484804547663} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727884620498 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484567537273} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728275739502 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484144961477} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728364688868 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484184002895} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728392677558 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484194823197} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728756489063 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485976391628} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728765471669 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485968465694} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728885328525 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485845462566} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729266183804 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485328751319} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &5334037118326170275 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485328751319} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118824515154 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485845462566} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118957828024 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485976391628} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118965753706 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485968465694} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119321186225 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484144961477} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119338529897 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484194823197} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119345154363 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484184002895} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119695527437 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484567537273} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119932537915 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484804547663} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119954031859 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484774665351} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037120066363219 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484939159335} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037120261849039 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485083379643} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!1 &9013638939891603710 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251878133587} + - component: {fileID: 4024461405119892143} + - component: {fileID: 1622145354905184162} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940380560399 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251109747618} + - component: {fileID: 4024461405895536222} + - component: {fileID: 1622145355555570515} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940521975269 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251251432008} + - component: {fileID: 4024461405764591540} + - component: {fileID: 1622145355414155961} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940530957623 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251242449562} + - component: {fileID: 4024461405755740006} + - component: {fileID: 1622145355406353003} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940844451308 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252763976257} + - component: {fileID: 4024461404223813565} + - component: {fileID: 1622145356132981424} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940868649830 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252737680587} + - component: {fileID: 4024461404268687671} + - component: {fileID: 1622145356041608250} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940894281268 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252747704729} + - component: {fileID: 4024461404241095781} + - component: {fileID: 1622145356048613736} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941260735568 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264253197038589} + - component: {fileID: 4024461403818014209} + - component: {fileID: 1622145355750251276} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941484641966 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252398516483} + - component: {fileID: 4024461404600745215} + - component: {fileID: 1622145356498440690} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941489358438 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252418961867} + - component: {fileID: 4024461404587635767} + - component: {fileID: 1622145356528326970} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941622396174 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252552786595} + - component: {fileID: 4024461404452501343} + - component: {fileID: 1622145356394764882} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941785326994 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252631011903} + - component: {fileID: 4024461404358629315} + - component: {fileID: 1622145356266240718} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity.meta new file mode 100644 index 0000000..76d5084 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: aab2581cdfbe8f44698ef91d36f2d6c6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity new file mode 100644 index 0000000..a71dec2 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity @@ -0,0 +1,3448 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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: 112000004, guid: 4bb692eb322f5154c996f4ae7d8d8bb2, type: 2} + m_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!114 &95425705 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149116643831} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4159880063 + SerializedTransformProperties: + Position: {x: 3.58, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &108939185 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 108939186} + - component: {fileID: 108939187} + m_Layer: 0 + m_Name: Waypoint 2 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &108939186 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 108939185} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1521876172} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &108939187 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 108939185} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 70028e1b1708627408637ab9a3cd6bd4, type: 3} + m_Name: + m_EditorClassIdentifier: + WaypointIndex: 2 +--- !u!114 &326714650 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149480978502} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2952494053 + SerializedTransformProperties: + Position: {x: -3.41, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &422957713 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 422957714} + - component: {fileID: 422957716} + - component: {fileID: 422957715} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &422957714 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 422957713} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1383041480} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &422957715 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 422957713} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &422957716 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 422957713} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!114 &482460140 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149503556792} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3350984549 + SerializedTransformProperties: + Position: {x: 4.3, y: 0.5, z: -5.39} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &552945233 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148633991967} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2548822829 + SerializedTransformProperties: + Position: {x: 3.47, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &594330602 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 594330603} + m_Layer: 0 + m_Name: Trees(Networked) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &594330603 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 594330602} + m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} + m_LocalPosition: {x: -15, y: 0, z: 15} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 543450651859724068} + - {fileID: 543450651802355761} + - {fileID: 543450653638095693} + - {fileID: 543450652088940807} + - {fileID: 543450653415187196} + - {fileID: 543450652352743558} + - {fileID: 543450653155402149} + - {fileID: 1383041480} + - {fileID: 543450652013682271} + - {fileID: 543450653225952770} + - {fileID: 543450652407348635} + - {fileID: 543450652781764868} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} +--- !u!114 &918521586 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148864302014} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3745431333 + SerializedTransformProperties: + Position: {x: 2.88, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1115572417 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1115572421} + - component: {fileID: 1115572420} + - component: {fileID: 1115572419} + - component: {fileID: 1115572418} + m_Layer: 0 + m_Name: LevelLoader + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1115572418 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1115572417} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2817146743 + SerializedTransformProperties: + Position: {x: -12.5, y: 0, z: 12.5} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1115572419 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1115572417} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 02e5160f1a9b4d047a6cbdb3f094a86d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1115572418} + _networkObjectCache: {fileID: 1115572418} +--- !u!65 &1115572420 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1115572417} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!4 &1115572421 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1115572417} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -12.5, y: 0, z: 12.5} + m_LocalScale: {x: 20, y: 5, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1161649283 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1161649284} + - component: {fileID: 1161649286} + - component: {fileID: 1161649285} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1161649284 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1161649283} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1383041480} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1161649285 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1161649283} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1161649286 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1161649283} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!114 &1326242419 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148198401825} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2814360559 + SerializedTransformProperties: + Position: {x: -3.1, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1355673456 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148361908796} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4150409007 + SerializedTransformProperties: + Position: {x: 1.12, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1383041479 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1383041480} + - component: {fileID: 1383041481} + m_Layer: 0 + m_Name: Tree (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1383041480 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383041479} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1161649284} + - {fileID: 422957714} + m_Father: {fileID: 594330603} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!114 &1383041481 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1383041479} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4159894327 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1521876168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1521876172} + - component: {fileID: 1521876171} + - component: {fileID: 1521876170} + - component: {fileID: 1521876169} + m_Layer: 0 + m_Name: Platform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1521876169 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + 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 &1521876170 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 83bc8ce733133754198df883775fe67b, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1521876171 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1521876172 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_LocalRotation: {x: 0, y: 1, z: 0, w: 0} + m_LocalPosition: {x: -15, y: 0, z: 15} + m_LocalScale: {x: 15, y: 1, z: 15} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 108939186} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0} +--- !u!114 &1611292399 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147544449981} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2405857215 + SerializedTransformProperties: + Position: {x: -1.97, y: 0.5, z: 2.16} + Rotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1686616503 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147485934821} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3758029815 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: -6.24} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1873854674 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147673857438} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4151277565 + SerializedTransformProperties: + Position: {x: 2.0644817, y: 0.5, z: -0.2750473} + Rotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1931423709 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147863917195} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 3887539063 + SerializedTransformProperties: + Position: {x: -1.85, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!4 &543450651802355761 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147863917195} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: -1.85, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251365230650} + - {fileID: 3745622626486145876} + m_Father: {fileID: 594330603} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450651859724068 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147673857438} + m_LocalRotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 2.0644817, y: 0.5, z: -0.2750473} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251292411695} + - {fileID: 3745622626965437505} + m_Father: {fileID: 594330603} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -50, z: 0} +--- !u!4 &543450652013682271 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147485934821} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: -6.24} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251207392852} + - {fileID: 3745622626811757882} + m_Father: {fileID: 594330603} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450652088940807 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147544449981} + m_LocalRotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + m_LocalPosition: {x: -1.97, y: 0.5, z: 2.16} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251148400908} + - {fileID: 3745622626736415330} + m_Father: {fileID: 594330603} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: -75, z: 0} +--- !u!4 &543450652352743558 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148361908796} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: 1.12, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251940751501} + - {fileID: 3745622627009270755} + m_Father: {fileID: 594330603} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450652407348635 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148198401825} + m_LocalRotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + m_LocalPosition: {x: -3.1, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251835841936} + - {fileID: 3745622627491555070} + m_Father: {fileID: 594330603} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 80, z: 0} +--- !u!4 &543450652781764868 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148864302014} + m_LocalRotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + m_LocalPosition: {x: 2.88, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252516540687} + - {fileID: 3745622627654302305} + m_Father: {fileID: 594330603} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 30, z: 0} +--- !u!4 &543450653155402149 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148633991967} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 3.47, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252210799022} + - {fileID: 3745622627817175744} + m_Father: {fileID: 594330603} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653225952770 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149503556792} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 4.3, y: 0.5, z: -5.39} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264253216613897} + - {fileID: 3745622628283563367} + m_Father: {fileID: 594330603} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653415187196 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149480978502} + m_LocalRotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + m_LocalPosition: {x: -3.41, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252973900535} + - {fileID: 3745622628094261657} + m_Father: {fileID: 594330603} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 100, z: 0} +--- !u!4 &543450653638095693 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149116643831} + m_LocalRotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + m_LocalPosition: {x: 3.58, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252802380614} + - {fileID: 3745622628408256552} + m_Father: {fileID: 594330603} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 125, z: 0} +--- !u!1 &832149484106557122 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628408256552} + - component: {fileID: 5334037119284092598} + - component: {fileID: 4539702728312570473} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484488849267 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628094261657} + - component: {fileID: 5334037119648419591} + - component: {fileID: 4539702728065553368} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484527154061 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628283563367} + - component: {fileID: 5334037119673093113} + - component: {fileID: 4539702727919310630} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484731855914 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627817175744} + - component: {fileID: 5334037119875173470} + - component: {fileID: 4539702727855575169} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484962952331 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627654302305} + - component: {fileID: 5334037120105483519} + - component: {fileID: 4539702727491375136} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485354149908 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627491555070} + - component: {fileID: 5334037118367944800} + - component: {fileID: 4539702729207811263} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485534567689 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627009270755} + - component: {fileID: 5334037118531462525} + - component: {fileID: 4539702729195174306} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485731023824 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626811757882} + - component: {fileID: 5334037118729237412} + - component: {fileID: 4539702728863444859} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485806709896 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626736415330} + - component: {fileID: 5334037118787736828} + - component: {fileID: 4539702728922433571} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485884994219 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626965437505} + - component: {fileID: 5334037118915037919} + - component: {fileID: 4539702728681690624} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149486093278654 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626486145876} + - component: {fileID: 5334037119105115594} + - component: {fileID: 4539702728604940565} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147485934821 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652013682271} + - component: {fileID: 1686616503} + m_Layer: 0 + m_Name: Tree (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147544449981 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652088940807} + - component: {fileID: 1611292399} + m_Layer: 0 + m_Name: Tree (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147673857438 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651859724068} + - component: {fileID: 1873854674} + m_Layer: 0 + m_Name: Tree + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147863917195 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651802355761} + - component: {fileID: 1931423709} + m_Layer: 0 + m_Name: Tree (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148198401825 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652407348635} + - component: {fileID: 1326242419} + m_Layer: 0 + m_Name: Tree (10) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148361908796 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652352743558} + - component: {fileID: 1355673456} + m_Layer: 0 + m_Name: Tree (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148633991967 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653155402149} + - component: {fileID: 552945233} + m_Layer: 0 + m_Name: Tree (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148864302014 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652781764868} + - component: {fileID: 918521586} + m_Layer: 0 + m_Name: Tree (11) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149116643831 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653638095693} + - component: {fileID: 95425705} + m_Layer: 0 + m_Name: Tree (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149480978502 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653415187196} + - component: {fileID: 326714650} + m_Layer: 0 + m_Name: Tree (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149503556792 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653225952770} + - component: {fileID: 482460140} + m_Layer: 0 + m_Name: Tree (9) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1300264251148400908 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940352917153} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652088940807} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251207392852 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940293384697} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652013682271} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251292411695 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940445880450} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651859724068} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251365230650 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940636766103} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651802355761} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251835841936 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939899832893} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652407348635} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251940751501 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940088508192} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652352743558} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252210799022 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941431182851} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653155402149} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252516540687 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941670700706} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652781764868} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252802380614 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940814959851} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653638095693} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252973900535 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941171922266} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653415187196} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264253216613897 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941229101476} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653225952770} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1622145354707820668 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940088508192} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145354930181473 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939899832893} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355266793675 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940636766103} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355457351646 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940445880450} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355517743613 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940352917153} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355575637669 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940293384697} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355714187000 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941229101476} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355805903366 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941171922266} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356094839735 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940814959851} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356413962750 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941670700706} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356585257311 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941431182851} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!4 &3745622626486145876 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486093278654} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651802355761} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622626736415330 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485806709896} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652088940807} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622626811757882 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485731023824} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652013682271} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622626965437505 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485884994219} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651859724068} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627009270755 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485534567689} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652352743558} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627491555070 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485354149908} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652407348635} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627654302305 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484962952331} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652781764868} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627817175744 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484731855914} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653155402149} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628094261657 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484488849267} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653415187196} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628283563367 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484527154061} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653225952770} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628408256552 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484106557122} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653638095693} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4024461403774018549 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941229101476} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404030428939 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941171922266} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404194584250 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940814959851} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404479562995 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941670700706} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404777800786 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941431182851} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405048819057 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940088508192} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405178919020 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939899832893} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405647703494 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940636766103} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405704512211 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940445880450} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405791853480 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940293384697} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405866687728 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940352917153} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!23 &4539702727491375136 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484962952331} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727855575169 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484731855914} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727919310630 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484527154061} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728065553368 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484488849267} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728312570473 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484106557122} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728604940565 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486093278654} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728681690624 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485884994219} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728863444859 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485731023824} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728922433571 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485806709896} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729195174306 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485534567689} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729207811263 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485354149908} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &5334037118367944800 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485354149908} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118531462525 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485534567689} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118729237412 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485731023824} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118787736828 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485806709896} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118915037919 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485884994219} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119105115594 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486093278654} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119284092598 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484106557122} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119648419591 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484488849267} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119673093113 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484527154061} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119875173470 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484731855914} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037120105483519 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484962952331} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!1 &9013638939899832893 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251835841936} + - component: {fileID: 4024461405178919020} + - component: {fileID: 1622145354930181473} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940088508192 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251940751501} + - component: {fileID: 4024461405048819057} + - component: {fileID: 1622145354707820668} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940293384697 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251207392852} + - component: {fileID: 4024461405791853480} + - component: {fileID: 1622145355575637669} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940352917153 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251148400908} + - component: {fileID: 4024461405866687728} + - component: {fileID: 1622145355517743613} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940445880450 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251292411695} + - component: {fileID: 4024461405704512211} + - component: {fileID: 1622145355457351646} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940636766103 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251365230650} + - component: {fileID: 4024461405647703494} + - component: {fileID: 1622145355266793675} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940814959851 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252802380614} + - component: {fileID: 4024461404194584250} + - component: {fileID: 1622145356094839735} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941171922266 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252973900535} + - component: {fileID: 4024461404030428939} + - component: {fileID: 1622145355805903366} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941229101476 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264253216613897} + - component: {fileID: 4024461403774018549} + - component: {fileID: 1622145355714187000} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941431182851 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252210799022} + - component: {fileID: 4024461404777800786} + - component: {fileID: 1622145356585257311} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941670700706 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252516540687} + - component: {fileID: 4024461404479562995} + - component: {fileID: 1622145356413962750} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity.meta new file mode 100644 index 0000000..d02e836 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 969cc8a8a67c58b4da9baa82a65c66bf +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity new file mode 100644 index 0000000..a3b2ebb --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity @@ -0,0 +1,3448 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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: 112000006, guid: 4bb692eb322f5154c996f4ae7d8d8bb2, type: 2} + m_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!114 &73418659 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149094644465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4267175901 + SerializedTransformProperties: + Position: {x: -3.41, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &79032482 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149099580878} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4292313039 + SerializedTransformProperties: + Position: {x: 2.88, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &240188781 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149261385787} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4202135214 + SerializedTransformProperties: + Position: {x: 3.58, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &444883191 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149599788453} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4292869852 + SerializedTransformProperties: + Position: {x: 2.0644817, y: 0.5, z: -0.2750473} + Rotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &665252281 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148612189415} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4292280013 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &893044127 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148839479501} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4283924380 + SerializedTransformProperties: + Position: {x: 1.12, y: 0.5, z: -4.14} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1155450199 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148027619333} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4269244300 + SerializedTransformProperties: + Position: {x: -1.85, y: 0.5, z: -3.188} + Rotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1198518658 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148070534190} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4208951246 + SerializedTransformProperties: + Position: {x: -3.1, y: 0.5, z: 5.64} + Rotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1340267954 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148213997822} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4227760124 + SerializedTransformProperties: + Position: {x: -1.97, y: 0.5, z: 2.16} + Rotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1365027452 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1365027453} + - component: {fileID: 1365027454} + - component: {fileID: 1365027456} + - component: {fileID: 1365027455} + m_Layer: 0 + m_Name: LevelLoader + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1365027453 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1365027452} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -12.5, y: 0, z: 2.5} + m_LocalScale: {x: 20, y: 5, z: 20} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!65 &1365027454 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1365027452} + m_Material: {fileID: 0} + m_IsTrigger: 1 + m_Enabled: 1 + serializedVersion: 2 + m_Size: {x: 1, y: 1, z: 1} + m_Center: {x: 0, y: 0, z: 0} +--- !u!114 &1365027455 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1365027452} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4294868718 + SerializedTransformProperties: + Position: {x: -12.5, y: 0, z: 2.5} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &1365027456 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1365027452} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 02e5160f1a9b4d047a6cbdb3f094a86d, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 1365027455} + _networkObjectCache: {fileID: 1365027455} +--- !u!1 &1521876168 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1521876172} + - component: {fileID: 1521876171} + - component: {fileID: 1521876170} + - component: {fileID: 1521876169} + m_Layer: 0 + m_Name: Platform + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!65 &1521876169 +BoxCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + 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 &1521876170 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 83bc8ce733133754198df883775fe67b, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1521876171 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!4 &1521876172 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1521876168} + m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068} + m_LocalPosition: {x: -15, y: 0, z: 0} + m_LocalScale: {x: 15, y: 1, z: 15} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 2077277857} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0} +--- !u!114 &1566503149 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148438650299} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 2128572079 + SerializedTransformProperties: + Position: {x: -5, y: 0.5, z: -6.24} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &1995669880 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1995669881} + m_Layer: 0 + m_Name: Trees(Networked) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1995669881 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1995669880} + m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068} + m_LocalPosition: {x: -15, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 543450653255075615} + - {fileID: 543450652553224895} + - {fileID: 543450653468236417} + - {fileID: 543450652401973828} + - {fileID: 543450653634928715} + - {fileID: 543450652840796791} + - {fileID: 543450651675761637} + - {fileID: 543450653068596829} + - {fileID: 543450652175734529} + - {fileID: 543450651593903160} + - {fileID: 543450652543719060} + - {fileID: 543450653621000052} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0} +--- !u!114 &2024537105 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147957547359} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4200071150 + SerializedTransformProperties: + Position: {x: 3.47, y: 0.5, z: 2.42} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!1 &2077277856 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2077277857} + - component: {fileID: 2077277858} + m_Layer: 0 + m_Name: Waypoint 3 + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2077277857 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2077277856} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0.5, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1521876172} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2077277858 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2077277856} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 70028e1b1708627408637ab9a3cd6bd4, type: 3} + m_Name: + m_EditorClassIdentifier: + WaypointIndex: 3 +--- !u!114 &2139679702 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147938612866} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 1 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 4227333823 + SerializedTransformProperties: + Position: {x: 4.3, y: 0.5, z: -5.39} + Rotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!4 &543450651593903160 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147938612866} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 4.3, y: 0.5, z: -5.39} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251559267379} + - {fileID: 3745622626694371165} + m_Father: {fileID: 1995669881} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450651675761637 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132147957547359} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 3.47, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251544787950} + - {fileID: 3745622626612772992} + m_Father: {fileID: 1995669881} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450652175734529 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148438650299} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: -6.24} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252132710154} + - {fileID: 3745622627186543716} + m_Father: {fileID: 1995669881} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450652401973828 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148213997822} + m_LocalRotation: {x: 0, y: -0.60876137, z: -0, w: 0.7933534} + m_LocalPosition: {x: -1.97, y: 0.5, z: 2.16} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251826272847} + - {fileID: 3745622627497189665} + m_Father: {fileID: 1995669881} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: -75, z: 0} +--- !u!4 &543450652543719060 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148070534190} + m_LocalRotation: {x: 0, y: 0.6427876, z: 0, w: 0.7660445} + m_LocalPosition: {x: -3.1, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251699713695} + - {fileID: 3745622627355430385} + m_Father: {fileID: 1995669881} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 80, z: 0} +--- !u!4 &543450652553224895 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148027619333} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: -1.85, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264251738448564} + - {fileID: 3745622627345938906} + m_Father: {fileID: 1995669881} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450652840796791 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148839479501} + m_LocalRotation: {x: -0, y: 0.2164396, z: -0, w: 0.97629607} + m_LocalPosition: {x: 1.12, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252541919868} + - {fileID: 3745622627595237650} + m_Father: {fileID: 1995669881} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 25, z: 0} +--- !u!4 &543450653068596829 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132148612189415} + m_LocalRotation: {x: -0, y: 0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: -5, y: 0.5, z: 2.42} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252232848982} + - {fileID: 3745622627904294200} + m_Father: {fileID: 1995669881} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 50, z: 0} +--- !u!4 &543450653255075615 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149599788453} + m_LocalRotation: {x: 0, y: -0.42261827, z: -0, w: 0.9063079} + m_LocalPosition: {x: 2.0644817, y: 0.5, z: -0.2750473} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264253119842068} + - {fileID: 3745622628254358650} + m_Father: {fileID: 1995669881} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: -50, z: 0} +--- !u!4 &543450653468236417 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149261385787} + m_LocalRotation: {x: -0, y: 0.8870109, z: -0, w: 0.46174863} + m_LocalPosition: {x: 3.58, y: 0.5, z: -3.188} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252921895562} + - {fileID: 3745622628578132452} + m_Father: {fileID: 1995669881} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 125, z: 0} +--- !u!4 &543450653621000052 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149099580878} + m_LocalRotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258} + m_LocalPosition: {x: 2.88, y: 0.5, z: 5.64} + m_LocalScale: {x: 0.75, y: 0.75, z: 0.75} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252818937727} + - {fileID: 3745622628425370641} + m_Father: {fileID: 1995669881} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 30, z: 0} +--- !u!4 &543450653634928715 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 919132149094644465} + m_LocalRotation: {x: -0, y: 0.76604444, z: -0, w: 0.64278764} + m_LocalPosition: {x: -3.41, y: 0.5, z: -4.14} + m_LocalScale: {x: 0.75000006, y: 0.75, z: 0.75000006} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1300264252824412224} + - {fileID: 3745622628411390766} + m_Father: {fileID: 1995669881} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 100, z: 0} +--- !u!1 &832149484123702011 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628425370641} + - component: {fileID: 5334037119267043983} + - component: {fileID: 4539702728329569872} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484134888900 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628411390766} + - component: {fileID: 5334037119264181680} + - component: {fileID: 4539702728315703663} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484301635342 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628578132452} + - component: {fileID: 5334037119428851578} + - component: {fileID: 4539702728146922405} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484623778448 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622628254358650} + - component: {fileID: 5334037119769340644} + - component: {fileID: 4539702727957362235} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484676105170 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627904294200} + - component: {fileID: 5334037119855492006} + - component: {fileID: 4539702727741515641} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149484920698872 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627595237650} + - component: {fileID: 5334037120080662412} + - component: {fileID: 4539702727499551571} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485199882032 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627345938906} + - component: {fileID: 5334037118195060548} + - component: {fileID: 4539702729397736347} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485226148635 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627355430385} + - component: {fileID: 5334037118240095087} + - component: {fileID: 4539702729340004272} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485351137227 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627497189665} + - component: {fileID: 5334037118383544255} + - component: {fileID: 4539702729213447008} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149485594133134 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622627186543716} + - component: {fileID: 5334037118606113530} + - component: {fileID: 4539702728969922085} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149486150801847 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626694371165} + - component: {fileID: 5334037119181906371} + - component: {fileID: 4539702728410643740} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &832149486203162218 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3745622626612772992} + - component: {fileID: 5334037119200851486} + - component: {fileID: 4539702728530242241} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147938612866 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651593903160} + - component: {fileID: 2139679702} + m_Layer: 0 + m_Name: Tree (9) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132147957547359 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450651675761637} + - component: {fileID: 2024537105} + m_Layer: 0 + m_Name: Tree (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148027619333 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652553224895} + - component: {fileID: 1155450199} + m_Layer: 0 + m_Name: Tree (1) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148070534190 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652543719060} + - component: {fileID: 1198518658} + m_Layer: 0 + m_Name: Tree (10) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148213997822 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652401973828} + - component: {fileID: 1340267954} + m_Layer: 0 + m_Name: Tree (3) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148438650299 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652175734529} + - component: {fileID: 1566503149} + m_Layer: 0 + m_Name: Tree (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148612189415 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653068596829} + - component: {fileID: 665252281} + m_Layer: 0 + m_Name: Tree (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132148839479501 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450652840796791} + - component: {fileID: 893044127} + m_Layer: 0 + m_Name: Tree (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149094644465 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653634928715} + - component: {fileID: 73418659} + m_Layer: 0 + m_Name: Tree (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149099580878 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653621000052} + - component: {fileID: 79032482} + m_Layer: 0 + m_Name: Tree (11) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149261385787 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653468236417} + - component: {fileID: 240188781} + m_Layer: 0 + m_Name: Tree (2) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &919132149599788453 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 543450653255075615} + - component: {fileID: 444883191} + m_Layer: 0 + m_Name: Tree + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1300264251544787950 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940766014531} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651675761637} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251559267379 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940713786270} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651593903160} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251699713695 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939763310898} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652543719060} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251738448564 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939751855385} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652553224895} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264251826272847 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939905732066} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652401973828} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252132710154 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940129329319} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652175734529} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252232848982 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941386047995} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653068596829} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252541919868 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941612291537} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652840796791} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252818937727 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940831416530} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653621000052} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252824412224 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940820191213} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653634928715} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264252921895562 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940984839463} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653468236417} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &1300264253119842068 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941333721273} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 4, z: 0} + m_LocalScale: {x: 100, y: 100, z: 300} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653255075615} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1622145354633576443 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940129329319} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145354923758270 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939905732066} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355044604485 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939751855385} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355066572398 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939763310898} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355104580383 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940766014531} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355190494402 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940713786270} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145355676217317 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941333721273} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356025688699 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940984839463} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356111347598 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940831416530} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356123162801 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940820191213} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356372363917 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941612291537} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &1622145356664143527 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941386047995} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 92aa8b0f5f5816f4cae8c6f2091860d4, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!4 &3745622626612772992 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486203162218} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651675761637} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622626694371165 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486150801847} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450651593903160} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627186543716 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485594133134} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652175734529} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627345938906 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485199882032} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652553224895} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627355430385 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485226148635} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652543719060} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627497189665 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485351137227} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652401973828} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627595237650 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484920698872} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450652840796791} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622627904294200 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484676105170} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653068596829} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628254358650 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484623778448} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653255075615} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628411390766 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484134888900} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653634928715} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628425370641 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484123702011} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653621000052} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!4 &3745622628578132452 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484301635342} + m_LocalRotation: {x: -0.7071068, y: 0, z: -0, w: 0.7071067} + m_LocalPosition: {x: -0, y: 0.5, z: 0} + m_LocalScale: {x: 25, y: 25, z: 50} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 543450653468236417} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &4024461403879228136 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941333721273} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404083412854 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940984839463} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404177977987 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940831416530} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404183061948 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940820191213} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404470994816 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941612291537} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404766426026 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638941386047995} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461404871807734 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940129329319} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405164634035 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939905732066} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405249300296 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939751855385} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405306901347 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638939763310898} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405438691791 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940713786270} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &4024461405453442578 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9013638940766014531} + m_Mesh: {fileID: 3613567641014311022, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!23 &4539702727499551571 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484920698872} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727741515641 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484676105170} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702727957362235 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484623778448} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728146922405 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484301635342} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728315703663 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484134888900} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728329569872 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484123702011} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728410643740 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486150801847} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728530242241 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486203162218} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702728969922085 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485594133134} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729213447008 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485351137227} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729340004272 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485226148635} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!23 &4539702729397736347 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485199882032} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 15634b93de15be5419ffed25bc199637, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &5334037118195060548 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485199882032} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118240095087 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485226148635} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118383544255 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485351137227} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037118606113530 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149485594133134} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119181906371 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486150801847} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119200851486 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149486203162218} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119264181680 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484134888900} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119267043983 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484123702011} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119428851578 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484301635342} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119769340644 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484623778448} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037119855492006 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484676105170} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!33 &5334037120080662412 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832149484920698872} + m_Mesh: {fileID: -5495902117074765545, guid: d912959aabd6a3e439552066ae82eb71, type: 3} +--- !u!1 &9013638939751855385 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251738448564} + - component: {fileID: 4024461405249300296} + - component: {fileID: 1622145355044604485} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638939763310898 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251699713695} + - component: {fileID: 4024461405306901347} + - component: {fileID: 1622145355066572398} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638939905732066 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251826272847} + - component: {fileID: 4024461405164634035} + - component: {fileID: 1622145354923758270} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940129329319 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252132710154} + - component: {fileID: 4024461404871807734} + - component: {fileID: 1622145354633576443} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940713786270 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251559267379} + - component: {fileID: 4024461405438691791} + - component: {fileID: 1622145355190494402} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940766014531 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264251544787950} + - component: {fileID: 4024461405453442578} + - component: {fileID: 1622145355104580383} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940820191213 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252824412224} + - component: {fileID: 4024461404183061948} + - component: {fileID: 1622145356123162801} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940831416530 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252818937727} + - component: {fileID: 4024461404177977987} + - component: {fileID: 1622145356111347598} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638940984839463 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252921895562} + - component: {fileID: 4024461404083412854} + - component: {fileID: 1622145356025688699} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941333721273 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264253119842068} + - component: {fileID: 4024461403879228136} + - component: {fileID: 1622145355676217317} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941386047995 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252232848982} + - component: {fileID: 4024461404766426026} + - component: {fileID: 1622145356664143527} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!1 &9013638941612291537 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1300264252541919868} + - component: {fileID: 4024461404470994816} + - component: {fileID: 1622145356372363917} + m_Layer: 0 + m_Name: Cone + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity.meta new file mode 100644 index 0000000..904dcfc --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 8ac819f926d1e914e9552e79c147082d +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity new file mode 100644 index 0000000..37c23d1 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity @@ -0,0 +1,489 @@ +%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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + 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_LightingSettings: {fileID: 0} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1001 &413498297 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 7443408887457311807} + 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: 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!224 &413498298 stripped +RectTransform: + m_CorrespondingSourceObject: {fileID: 4393252310969058990, guid: 0570b6f7f713dc44a90463654bbcd8d0, type: 3} + m_PrefabInstance: {fileID: 413498297} + m_PrefabAsset: {fileID: 0} +--- !u!1 &860526058 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 860526059} + m_Layer: 0 + m_Name: World Center - Reference Only + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &860526059 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 860526058} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1565068570 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887457311805} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d331f979d46e8e4a9fc90070c596d44, type: 3} + m_Name: + m_EditorClassIdentifier: + _updateHostVisibility: 1 + _maximumTimedObserversDuration: 10 + _defaultConditions: + - {fileID: 11400000, guid: 2033f54fd2794464bae08fa5a55c8996, type: 2} + - {fileID: 11400000, guid: 37bd3d466aaa29b4da784cddaaa365ea, type: 2} +--- !u!1 &2134535185 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2134535188} + - component: {fileID: 2134535187} + - component: {fileID: 2134535186} + - component: {fileID: 2134535189} + m_Layer: 0 + m_Name: ServerScenePrewarmer + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2134535186 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2134535185} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 1 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: [] + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 0 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 0 + k__BackingField: 1373109883 + SerializedTransformProperties: + Position: {x: -8.965608, y: -4.7316337, z: 9.317315} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 0, y: 0, z: 0} + IsValid: 0 +--- !u!114 &2134535187 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2134535185} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: b06d5bb8c7e0da04581df0b9454b81b7, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 2134535186} + _networkObjectCache: {fileID: 2134535186} + _scenes: + - Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_0.unity + - Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_1.unity + - Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_2.unity + - Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_3.unity +--- !u!4 &2134535188 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2134535185} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -8.965608, y: -4.7316337, z: 9.317315} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &2134535189 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2134535185} + 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: 9ff842b44ec59314d9efcecbcdbaac04, type: 2} +--- !u!114 &7443408887457311794 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887457311805} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 211a9f6ec51ddc14f908f5acc0cd0423, type: 3} + m_Name: + m_EditorClassIdentifier: + _playerPrefab: {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3} + _addToDefaultScene: 0 + Spawns: [] +--- !u!114 &7443408887457311804 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887457311805} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d2c95dfde7d73b54dbbdc23155d35d36, type: 3} + m_Name: + m_EditorClassIdentifier: + _refreshDefaultPrefabs: 0 + _runInBackground: 1 + _dontDestroyOnLoad: 1 + _objectPool: {fileID: 0} + _persistence: 0 + _logging: {fileID: 0} + _spawnablePrefabs: {fileID: 11400000, guid: ef18464092139404db8e515b0bf59331, type: 2} +--- !u!1 &7443408887457311805 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7443408887457311807} + - component: {fileID: 7443408887457311804} + - component: {fileID: 1565068570} + - component: {fileID: 7443408887457311794} + - component: {fileID: 7443408887457311808} + m_Layer: 0 + m_Name: NetworkManager + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7443408887457311807 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887457311805} + 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_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 413498298} + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &7443408887457311808 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7443408887457311805} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 6d0962ead4b02a34aae248fccce671ce, type: 3} + m_Name: + m_EditorClassIdentifier: + WriteSceneObjectDetails: 1 + ValidateRpcLengths: 1 + DisableObserversRpcLinks: 0 + DisableTargetRpcLinks: 0 + DisableServerRpcLinks: 0 + DisableReplicateRpcLinks: 0 + DisableReconcileRpcLinks: 0 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity.meta new file mode 100644 index 0000000..4f37c22 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: afbcf8f41ca78ef4296672aeb824f605 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scenes/AdditiveScenes_Start.unity + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts.meta new file mode 100644 index 0000000..cef4b4e --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f916eef3fd3b4bd459215b28ba6255e0 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs new file mode 100644 index 0000000..4286578 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs @@ -0,0 +1,96 @@ +using FishNet.Managing.Scened; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Demo.AdditiveScenes +{ + public class LevelLoader : NetworkBehaviour + { + + private void OnTriggerEnter(Collider other) + { + if (!base.IsServerStarted) + return; + + Player player = GetPlayerOwnedObject(other); + if (player == null) + return; + + /* Create a lookup handle using this objects scene. + * This is one of many ways FishNet knows what scene to load + * for the clients. */ + SceneLookupData lookupData = new(gameObject.scene); + SceneLoadData sld = new(lookupData) + { + /* Set automatically unload to false + * so the server does not unload this scene when + * there are no more connections in it. */ + Options = new() + { + AutomaticallyUnload = false + }, + /* Also move the client object to the new scene. + * This step is not required but may be desirable. */ + MovedNetworkObjects = new NetworkObject[] { player.NetworkObject }, + //Load scenes as additive. + ReplaceScenes = ReplaceOption.None, + //Set the preferred active scene so the client changes active scenes. + PreferredActiveScene = new(lookupData), + }; + + base.SceneManager.LoadConnectionScenes(player.Owner, sld); + } + + private void OnTriggerExit(Collider other) + { + if (!base.IsServerStarted) + return; + + Player player = GetPlayerOwnedObject(other); + if (player == null) + return; + + /* Create a lookup handle using this objects scene. + * This is one of many ways FishNet knows what scene to load + * for the clients. */ + SceneLookupData lookupData = new(gameObject.scene); + /* Tell server to keep unused when unloading. This will keep + * the scene even if there are no connections. + * This varies from AutomaticallyUnload slightly; + * automatically unload will remove the scene on the server + * if there are no more connections, such as if players + * were to disconnect. But when manually telling a scene to + * unload you must tell the server to keep it even if unused, + * if that is your preference. */ + SceneUnloadData sud = new(lookupData) + { + Options = new() + { + Mode = UnloadOptions.ServerUnloadMode.KeepUnused + } + }; + + base.SceneManager.UnloadConnectionScenes(player.Owner, sud); + } + + /// + /// Returns a Player script if the object is a player. + /// + /// + /// + private Player GetPlayerOwnedObject(Collider other) + { + /* When an object exits this trigger unload the level for the client. */ + Player player = other.GetComponent(); + //Not the player object. + if (player == null) + return null; + //No owner?? + if (!player.Owner.IsActive) + return null; + + return player; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs.meta new file mode 100644 index 0000000..75f72e9 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 02e5160f1a9b4d047a6cbdb3f094a86d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/LevelLoader.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs new file mode 100644 index 0000000..94aa2a6 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs @@ -0,0 +1,82 @@ +using FishNet.Connection; +using FishNet.Object; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace FishNet.Demo.AdditiveScenes +{ + public class Player : NetworkBehaviour + { + + [SerializeField] + private Transform _ownerObjects; + [SerializeField] + private float _moveRate = 2f; + + private List _wayPoints = new(); + private int _goalIndex; + private Vector3 _goalOffset; + + public override void OnStartServer() + { + _wayPoints = GameObject.FindObjectsOfType().ToList(); + /* Stagger spawn position slightly depending on player count. + * Also inverse direction so players cross each other when more + * than one. This is just demo fanciness. */ + if (base.ServerManager.Clients.Count % 2 == 0) + { + _goalOffset = new(-0.5f, 0f, 0f); + _wayPoints = _wayPoints.OrderBy(x => x.WaypointIndex).ToList(); + } + else + { + _goalOffset = new(0.5f, 0f, 0f); + _wayPoints = _wayPoints.OrderByDescending(x => x.WaypointIndex).ToList(); + } + + //Snap to current waypoint. + transform.position = _wayPoints[0].transform.position + _goalOffset; + //Set goal to next waypoint. + _goalIndex = 1; + } + + public override void OnOwnershipClient(NetworkConnection prevOwner) + { + _ownerObjects.gameObject.SetActive(base.IsOwner); + } + + private void Update() + { + //Not server or not setup. + if (!base.IsServerStarted) + return; + if (_wayPoints.Count == 0) + return; + if (_goalIndex >= _wayPoints.Count) + return; + + Vector3 posGoal = _wayPoints[_goalIndex].transform.position + _goalOffset; + transform.position = Vector3.MoveTowards(transform.position, posGoal, _moveRate * Time.deltaTime); + + Vector3 lookDirection = (posGoal - transform.position).normalized; + //Rotate to goal if there is a look direction. + if (lookDirection != Vector3.zero) + { + Quaternion rot = Quaternion.LookRotation((posGoal - transform.position).normalized, transform.up); + transform.rotation = Quaternion.RotateTowards(transform.rotation, rot, 270f * Time.deltaTime); + } + + //If at goal set next goal. + if (transform.position == posGoal) + { + _goalIndex++; + //Reset index to 0 if at last goal. + if (_goalIndex >= _wayPoints.Count) + _goalIndex = 0; + } + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs.meta new file mode 100644 index 0000000..9216a64 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c8541d1cca4da7043b84a0d563939dea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Player.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs new file mode 100644 index 0000000..7572c15 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs @@ -0,0 +1,43 @@ +using FishNet.Managing.Scened; +using FishNet.Object; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Demo.AdditiveScenes +{ + + public class ServerScenePrewarmer : NetworkBehaviour + { + [SerializeField, Scene] + private string[] _scenes = new string[0]; + + public override void OnStartServer() + { + /* Load all the needed scenes at once. + * This is not really required in most situations + * but the server uses waypoints from each scene + * to move the players. The waypoints won't exist unless + * all scenes do. This can be seen in the Player + * script. */ + + /* Load scenes using the FishNet SceneManager, with automaticallyUnload + * as false. This will keep the scenes from unloading when a client + * disconnects or leaves the scene. */ + foreach (string item in _scenes) + { + SceneLookupData lookupData = new(item); + SceneLoadData sld = new(lookupData) + { + Options = new() + { + AutomaticallyUnload = false + }, + }; + + base.SceneManager.LoadConnectionScenes(sld); + } + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs.meta new file mode 100644 index 0000000..b788920 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b06d5bb8c7e0da04581df0b9454b81b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/ServerScenePrewarmer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs new file mode 100644 index 0000000..376fba3 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace FishNet.Demo.AdditiveScenes +{ + + public class Waypoint : MonoBehaviour + { + public byte WaypointIndex; + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs.meta b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs.meta new file mode 100644 index 0000000..2c143ab --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 70028e1b1708627408637ab9a3cd6bd4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/Additive Scenes/Scripts/Waypoint.cs + uploadId: 762203 diff --git a/Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png b/Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..de8858be547b80ed95625e647ad78ba294db93f5 GIT binary patch literal 45367 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

mHVGPGm}y`7lOc`|kw$C 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 UDXC%0bHu70Tg|snjO@<+53I9EC;$Ke literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png.meta b/Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png.meta new file mode 100644 index 0000000..0e3eeb4 --- /dev/null +++ b/Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png.meta @@ -0,0 +1,99 @@ +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: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/SceneManager/SceneManager Event Diagram.png + uploadId: 762203 diff --git a/Assets/FishNet/Demos/Scripts.meta b/Assets/FishNet/Demos/Scripts.meta new file mode 100644 index 0000000..8a219e2 --- /dev/null +++ b/Assets/FishNet/Demos/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b8aa3d7aa8ea4b04b8c6bf02a715a645 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs b/Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs new file mode 100644 index 0000000..a641e6d --- /dev/null +++ b/Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs @@ -0,0 +1,253 @@ +using FishNet.Managing; +using FishNet.Transporting; +using UnityEngine; +using UnityEngine.EventSystems; +using UnityEngine.UI; + +namespace FishNet.Example +{ + + 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(4, 110, 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(165f, 42f); + style.fontSize = 26; + //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.gameObject.SetActive(false); + _clientIndicator.transform.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/Demos/Scripts/NetworkHudCanvases.cs.meta b/Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs.meta new file mode 100644 index 0000000..d5613d0 --- /dev/null +++ b/Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6d3606bfdac5a4743890fc1a5ecd8f24 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Demos/Scripts/NetworkHudCanvases.cs + uploadId: 762203 diff --git a/Assets/FishNet/LICENSE.txt b/Assets/FishNet/LICENSE.txt new file mode 100644 index 0000000..a705e30 --- /dev/null +++ b/Assets/FishNet/LICENSE.txt @@ -0,0 +1,37 @@ +0.a Definitions. + +0.b "FishNet" means FishNet, FishNetworking, Fish-Networking, networking for Unity Engine. + +0.c "Repository Service" means the respository service through which Fish-Net is made available. + +0.d “Software” means the software (including code in source or object format as applicable) of Fish-Net that accompanies this License. + +0.e "FirstGearGames" means Benjamin Berwick. + +0.f "FishNet-Pro" means pro versions of FishNet acquired directly from FirstGearGames or through purchase at FirstGearGames managed profiles such as Patreon or GitHub Sponsors. + +0.g "Purchaser" is a single individual which has acquired FishNet-Pro. + +1.a 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. + +2.a Exclusions. Other products of like Software (explicitly networking solutions) may not use, reverse engineer, or implement Software in part or full. + +2.b Products not of like Software as outlined in 2.a, such as add-ons, tools, or assets, may include or be created for Software in it's original, unmodified form. + +2.c Exclusions do not apply to parts of Software which are governed by a third-party license, nor to games developed with Software. + +3.a FishNet-Pro License. FishNet-Pro uses this license. + +3.b Distribution: Purchasers may distribute FishNet-Pro to their organization, or team of 20 or fewer members. Purchasers may distribute FishNet-Pro to their physical classroom environments with no member count restrictions. + +4.a Third-Party License Notice. Software may contain third-party licenses. 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. + +5.a 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. + +6.a Governing Law and Venue. This License is governed by and construed in accordance with the laws of Florida, 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 Flagler County, Florida concerning any dispute arising out of this License (“Dispute”). + +7.a Disclaimer, Limitation of Liability. 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 action of contract, tort or otherwise, arising from, out of or in connection with Software or the use or other dealings in Software. + +8.a You agree by submitting content of any kind to Software within Repository Service does not grant you ownership to Software nor additional rights to Software. + +8.b By submitting to Repository Service you are granting FirstGearGames with a no-charge, and royalty-free license to reproduce, modify, and use submitted content. diff --git a/Assets/FishNet/LICENSE.txt.meta b/Assets/FishNet/LICENSE.txt.meta new file mode 100644 index 0000000..6fc415e --- /dev/null +++ b/Assets/FishNet/LICENSE.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 83c5a6d0014103d48a0028f0ab7fec8d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/LICENSE.txt + uploadId: 762203 diff --git a/Assets/FishNet/Runtime.meta b/Assets/FishNet/Runtime.meta new file mode 100644 index 0000000..c8774b4 --- /dev/null +++ b/Assets/FishNet/Runtime.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fcaa22efc92f24499ee8812f2fda081 +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..a039d91 --- /dev/null +++ b/Assets/FishNet/Runtime/Authenticating.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 34e749ac19246214591a2e3f58dc6365 +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..1eb49c9 --- /dev/null +++ b/Assets/FishNet/Runtime/Authenticating/Authenticator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a9adfb82407774645a1f455ceb9298f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Authenticating/Authenticator.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Broadcast.meta b/Assets/FishNet/Runtime/Broadcast.meta new file mode 100644 index 0000000..d12a395 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7601d0cb4fcf4ef468b1faeee7bbf3f0 +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..2a48616 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/Helping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 634f0556bc4961f44846882dac5d1c7f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs new file mode 100644 index 0000000..db237cd --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs @@ -0,0 +1,199 @@ +//using FishNet.Connection; //remove on v5 +//using FishNet.Serializing; +//using FishNet.Transporting; +//using GameKit.Dependencies.Utilities; +//using System; +//using System.Collections.Generic; +//using System.Diagnostics; + +//namespace FishNet.Broadcast.Helping +//{ +// internal static class BroadcastHelper +// { +// /// +// /// Gets the key for a broadcast type. +// /// +// /// +// /// +// /// +// internal static ushort GetKey() +// { +// return typeof(T).FullName.GetStableHashU16(); +// } +// } + +// /// +// /// Implemented by server and client broadcast handlers. +// /// +// public abstract class BroadcastHandlerBase +// { +// /// +// /// Current index when iterating invokes. +// /// This value will be -1 when not iterating. +// /// +// protected int IteratingIndex; + +// public abstract void RegisterHandler(object obj); +// public abstract void UnregisterHandler(object obj); +// public virtual void InvokeHandlers(PooledReader reader, Channel channel) { } +// public virtual void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) { } +// public virtual bool RequireAuthentication => false; +// } + +// /// +// /// Handles broadcasts received on server, from clients. +// /// +// internal class ClientBroadcastHandler : BroadcastHandlerBase +// { +// /// +// /// Action handlers for the broadcast. +// /// +// private List> _handlers = new List>(); +// /// +// /// True to require authentication for the broadcast type. +// /// +// private bool _requireAuthentication; + +// public ClientBroadcastHandler(bool requireAuthentication) +// { +// _requireAuthentication = requireAuthentication; +// } + +// /// +// /// Invokes handlers after reading broadcast. +// /// +// /// True if a rebuild was required. +// public override void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) +// { +// T result = reader.Read(); +// for (base.IteratingIndex = 0; base.IteratingIndex < _handlers.Count; base.IteratingIndex++) +// { +// Action item = _handlers[base.IteratingIndex]; +// if (item != null) +// { +// item.Invoke(conn, result, channel); +// } +// else +// { +// _handlers.RemoveAt(base.IteratingIndex); +// base.IteratingIndex--; +// } +// } + +// base.IteratingIndex = -1; +// } + +// /// +// /// Adds a handler for this type. +// /// +// public override void RegisterHandler(object obj) +// { +// Action handler = (Action)obj; +// _handlers.AddUnique(handler); +// } + +// /// +// /// Removes a handler from this type. +// /// +// /// +// public override void UnregisterHandler(object obj) +// { +// Action handler = (Action)obj; +// int indexOf = _handlers.IndexOf(handler); +// //Not registered. +// if (indexOf == -1) +// return; + +// /* Has already been iterated over, need to subtract +// * 1 from iteratingIndex to accomodate +// * for the entry about to be removed. */ +// if (base.IteratingIndex >= 0 && (indexOf <= base.IteratingIndex)) +// base.IteratingIndex--; + +// //Remove entry. +// _handlers.RemoveAt(indexOf); +// } + +// /// +// /// True to require authentication for the broadcast type. +// /// +// public override bool RequireAuthentication => _requireAuthentication; +// } + + + +// /// +// /// Handles broadcasts received on client, from server. +// /// +// internal class ServerBroadcastHandler : BroadcastHandlerBase +// { +// /// +// /// Action handlers for the broadcast. +// /// Even though List lookups are slower this allows easy adding and removing of entries during iteration. +// /// +// private List> _handlers = new List>(); + +// /// +// /// Invokes handlers after reading broadcast. +// /// +// /// True if a rebuild was required. +// public override void InvokeHandlers(PooledReader reader, Channel channel) +// { +// T result = reader.Read(); +// for (base.IteratingIndex = 0; base.IteratingIndex < _handlers.Count; base.IteratingIndex++) +// { +// Action item = _handlers[base.IteratingIndex]; +// if (item != null) +// { +// item.Invoke(result, channel); +// } +// else +// { +// _handlers.RemoveAt(base.IteratingIndex); +// base.IteratingIndex--; +// } +// } + +// base.IteratingIndex = -1; +// } + +// /// +// /// Adds a handler for this type. +// /// +// public override void RegisterHandler(object obj) +// { +// Action handler = (Action)obj; +// _handlers.AddUnique(handler); +// } + +// /// +// /// Removes a handler from this type. +// /// +// /// +// public override void UnregisterHandler(object obj) +// { +// Action handler = (Action)obj; +// int indexOf = _handlers.IndexOf(handler); +// //Not registered. +// if (indexOf == -1) +// return; + +// /* Has already been iterated over, need to subtract +// * 1 from iteratingIndex to accomodate +// * for the entry about to be removed. */ +// if (base.IteratingIndex >= 0 && (indexOf <= base.IteratingIndex)) +// base.IteratingIndex--; + +// //Remove entry. +// _handlers.RemoveAt(indexOf); +// } + +// /// +// /// True to require authentication for the broadcast type. +// /// +// public override bool RequireAuthentication => false; +// } + + + +//} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs.meta b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs.meta new file mode 100644 index 0000000..35dea8d --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c314af08d630630449b7b7af740b9c7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Broadcast/Helping/BroadcastHelpers.cs + uploadId: 762203 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..5885533 --- /dev/null +++ b/Assets/FishNet/Runtime/Broadcast/IBroadcast.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 88ec864df25feed49bdcdab7f880531d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Broadcast/IBroadcast.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/CodeGenerating.meta b/Assets/FishNet/Runtime/CodeGenerating.meta new file mode 100644 index 0000000..1a2f5ac --- /dev/null +++ b/Assets/FishNet/Runtime/CodeGenerating.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: afabc0828ac7433468e40b80f5c9524c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs b/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs new file mode 100644 index 0000000..acf8e6e --- /dev/null +++ b/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs @@ -0,0 +1,64 @@ +using FishNet.Utility; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.CodeGenerating +{ + /// + /// Allows a SyncType to be mutable. + /// + public class AllowMutableSyncTypeAttribute : Attribute { } + /// + /// Type will be included in auto serializer creation. + /// + [AttributeUsage((AttributeTargets.Class | AttributeTargets.Struct), Inherited = true, AllowMultiple = false)] + public class IncludeSerializationAttribute : Attribute { } + /// + /// Type will be excluded from auto serializer creation. + /// + public class ExcludeSerializationAttribute : Attribute { } + /// + /// Method will not be considered a writer or reader. + /// + public class NotSerializerAttribute : Attribute { } + /// + /// Method or type will be made public by codegen. + /// + internal class MakePublicAttribute : Attribute { } + /// + /// Method is a comparer for a value type. + /// + public class CustomComparerAttribute : Attribute { } + /// + /// Used on a type when you want a custom serializer to be global across all assemblies. + /// + [AttributeUsage((AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface), Inherited = true, AllowMultiple = false)] + public class UseGlobalCustomSerializerAttribute : Attribute { } + /// + /// Uses built-in caches to retrieve read classes rather than initializing a new instance. + /// This attribute is primarily for internal use and may change at anytime without notice. + /// + [AttributeUsage((AttributeTargets.Class), Inherited = true, AllowMultiple = false)] + public class ReadUnallocatedAttribute : Attribute { } + /// + /// Indicates a method is the default writer for a type. The first non-extension parameter indicates the type this writer is for. + /// This attribute is primarily for internal use and may change at anytime without notice. + /// + public class DefaultWriterAttribute : Attribute { } + /// + /// Indicates a method is the default reader for a type. The return type indicates what type the reader is for. + /// This attribute is primarily for internal use and may change at anytime without notice. + /// + public class DefaultReaderAttribute : Attribute { } + /// + /// Indicates a method is a delta writer. The first non-extension parameter indicates the type this writer is for. + /// This attribute is primarily for internal use and may change at anytime without notice. + /// + public class DefaultDeltaWriterAttribute : Attribute { } + /// + /// Indicates a method is a delta reader. The return type indicates what type the reader is for. + /// This attribute is primarily for internal use and may change at anytime without notice. + /// + public class DefaultDeltaReaderAttribute : Attribute { } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs.meta b/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs.meta new file mode 100644 index 0000000..6aeb9bd --- /dev/null +++ b/Assets/FishNet/Runtime/CodeGenerating/Attributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1581268482fee3b489c2ba9e8cd2b293 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/CodeGenerating/Attributes.cs + uploadId: 762203 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..0ff5b2f --- /dev/null +++ b/Assets/FishNet/Runtime/Config.json.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 9f1ece47c2d48194ea4827bf592a2279 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Config.json + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection.meta b/Assets/FishNet/Runtime/Connection.meta new file mode 100644 index 0000000..015e31f --- /dev/null +++ b/Assets/FishNet/Runtime/Connection.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1bfcb6f3f32d11a4296ade8dc07d56bb +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..daddbc6 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/Buffer.cs @@ -0,0 +1,296 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Object; +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; + Writer.WriteUInt32Unpacked(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 || (!_isSendLastBundle && _sendLastBundle.HasData)); + /// + /// All buffers written. Collection is not cleared when reset but rather the index in which to write is. + /// + private List _buffers = new(); + /// + /// 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; + /// + /// Packet bundle to use for last enqueued data. + /// + private PacketBundle _sendLastBundle; + /// + /// True if being used as an sendLast bundle. + /// + private bool _isSendLastBundle; + + internal PacketBundle(NetworkManager manager, int mtu, int reserve = 0, DataOrderType orderType = DataOrderType.Default) + { + _isSendLastBundle = (orderType == DataOrderType.Last); + //If this is not the send last packetbundle then make a new one. + if (!_isSendLastBundle) + _sendLastBundle = new(manager, mtu, reserve, DataOrderType.Last); + + _networkManager = manager; + _maximumTransportUnit = mtu; + /* Allow bytes for the tick. + * Modify reserve after making sendLast bundle + * so that the wrong reserve is not passed into + * the sendLast bundle. */ + reserve += TransportManager.UNPACKED_TICK_LENGTH; + _reserve = reserve; + //Add buffer requires the right reserve so call after setting. + AddBuffer(); + + Reset(false); + } + + public void Dispose() + { + for (int i = 0; i < _buffers.Count; i++) + _buffers[i].Dispose(); + + _sendLastBundle?.Dispose(); + } + + /// + /// Adds a buffer using current settings. + /// + private ByteBuffer AddBuffer() + { + ByteBuffer ba = new(_maximumTransportUnit, _reserve); + _buffers.Add(ba); + return ba; + } + + /// + /// Resets using current settings. + /// + internal void Reset(bool resetSendLast) + { + _bufferIndex = 0; + + for (int i = 0; i < _buffers.Count; i++) + _buffers[i].Reset(); + + if (resetSendLast) + _sendLastBundle.Reset(false); + } + + /// + /// 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, DataOrderType orderType = DataOrderType.Default) + { + /* If not the send last bundle and to send data last + * then send using the send last bundle. */ + if (!_isSendLastBundle && orderType == DataOrderType.Last) + { + _sendLastBundle.Write(segment, forceNewBuffer, orderType); + return; + } + + //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); + } + + /// + /// Returns the packetBundle for send last. + /// + /// + internal PacketBundle GetSendLastBundle() => _sendLastBundle; + + /// + /// 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..b55a63c --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/Buffer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eb2f3ce9b5ac27f40b7daa9364fb4d60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/Buffer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/EstimatedTick.cs b/Assets/FishNet/Runtime/Connection/EstimatedTick.cs new file mode 100644 index 0000000..8a5e33a --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/EstimatedTick.cs @@ -0,0 +1,217 @@ +using System.Runtime.CompilerServices; + +namespace FishNet.Managing.Timing +{ + public class EstimatedTick + { + #region Types. + /// + /// How to handle old ticks, specifically related to EstimatedTick. + /// + public enum OldTickOption : byte + { + /// + /// Completely ignore old ticks. + /// + Discard = 0, + /// + /// Set LastRemoteTick but do not update RemoteTick. + /// + SetLastRemoteTick = 1, + /// + /// Set LastRemoteTick and RemoteTick. + /// + SetRemoteTick = 2, + } + #endregion + + /// + /// Local tick when this was last updated. + /// + public uint LocalTick { get; private set; } = TimeManager.UNSET_TICK; + /// + /// Last remote tick this was updated with that was not out of order or a duplicate. + /// + public uint RemoteTick { get; private set; } = TimeManager.UNSET_TICK; + /// + /// Last remote tick received regardless if it was out of order or a duplicate. + /// + public uint LastRemoteTick { get; private set; } = TimeManager.UNSET_TICK; + /// + /// True if LastRemoteTick is equal to RemoteTick. + /// This would indicate that the LastRemoteTick did not arrive out of order. + /// + public bool IsLastRemoteTickOrdered => (LastRemoteTick == RemoteTick); + /// + /// True if value is unset. + /// + //Only need to check one value for unset as they all would be if not set. + public bool IsUnset => (LocalTick == TimeManager.UNSET_TICK); + /// + /// Last TimeManager specified during an Update call. + /// + private TimeManager _updateTimeManager; + /// + /// LocalTick when Value was last reset. + /// + private uint _valueLocalTick = TimeManager.UNSET_TICK; + + + /// + /// Number of ticks LocalTick is being current LocalTick. + /// + public uint LocalTickDifference(TimeManager tm = null) + { + if (!TryAssignTimeManager(ref tm)) + return TimeManager.UNSET_TICK; + + long value = (tm.LocalTick - LocalTick); + //Shouldn't be possible to be less than 0. + if (value < 0) + return TimeManager.UNSET_TICK; + else if (value > uint.MaxValue) + value = uint.MaxValue; + + return (uint)value; + } + + /// + /// True if values were updated this tick. + /// + public bool IsCurrent(TimeManager tm = null) + { + if (!TryAssignTimeManager(ref tm)) + return false; + + return (!IsUnset && LocalTick == tm.LocalTick); + } + + /// + /// Current estimated value. + /// + /// NetworkManager to use. When null default value will be returned. + public uint Value(TimeManager tm = null) + { + if (!TryAssignTimeManager(ref tm)) + return TimeManager.UNSET_TICK; + + return Value(out _, tm); + } + + /// + /// Current estimated value. Outputs if value is current. + /// + /// NetworkManager to use. When null default value will be returned. + /// True if the value was updated this local tick. + public uint Value(out bool isCurrent, TimeManager tm = null) + { + //Default value. + isCurrent = false; + + if (!TryAssignTimeManager(ref tm)) + return TimeManager.UNSET_TICK; + if (IsUnset) + return TimeManager.UNSET_TICK; + + isCurrent = IsCurrent(tm); + + uint diff = (tm.LocalTick - _valueLocalTick); + return (diff + RemoteTick); + } + + /// + /// Initializes this EstimatedTick with values. + /// + public void Initialize(TimeManager tm, uint remoteTick = 0, uint lastRemoteTick = 0, uint localTick = 0) + { + _updateTimeManager = tm; + RemoteTick = remoteTick; + LastRemoteTick = lastRemoteTick; + LocalTick = localTick; + } + + /// + /// Updates values. + /// + /// TimeManager to use. + /// Remote tick being updated. + /// How to handle remoteTick if it is old. + /// /// True to reset Value based on this information. False will allow Value to continue to to estimate tick based on the last reset. + /// True if was able to update values. + public bool Update(TimeManager tm, uint remoteTick, OldTickOption oldTickOption = OldTickOption.Discard, bool resetValue = true) + { + _updateTimeManager = tm; + //Always set LastRemoteTick even if out of order. + LastRemoteTick = remoteTick; + //If cannot update with old values return. + if (oldTickOption != OldTickOption.SetRemoteTick && remoteTick <= RemoteTick) + return false; + + //nm is assumed set here. + LocalTick = tm.LocalTick; + if (resetValue) + _valueLocalTick = LocalTick; + RemoteTick = remoteTick; + + return true; + } + + /// + /// Updates values. + /// + /// Remote tick being updated. + /// How to handle remoteTick if it is old. + /// True to reset Value based on this information. False will allow Value to continue to to estimate tick based on the last reset. + /// True if was able to update values. + public bool Update(uint remoteTick, OldTickOption oldTickOption = OldTickOption.Discard, bool resetValue = true) + { + TimeManager tm = null; + if (!TryAssignTimeManager(ref tm)) + return false; + + return Update(tm, remoteTick, oldTickOption); + } + + /// + /// Updates Value based on current ticks. + /// This is typically used when you want to control when Value is reset through the Update methods. + /// + public void UpdateValue() + { + _valueLocalTick = LocalTick; + } + + /// + /// Assigns a TimeManager reference to UpdateTimeManager if was null. + /// + /// True if the reference has value or was assigned value. False if the reference remains null. + private bool TryAssignTimeManager(ref TimeManager tm) + { + if (tm == null) + tm = _updateTimeManager; + + return (tm != null); + } + + /// + /// Resets values to unset and clears the NetworkManager. + /// + public void Reset() + { + ResetTicks(); + _updateTimeManager = null; + } + + + /// + /// Resets only tick values, leaving type references. + /// + public void ResetTicks() + { + LocalTick = TimeManager.UNSET_TICK; + RemoteTick = TimeManager.UNSET_TICK; + LastRemoteTick = TimeManager.UNSET_TICK; + _valueLocalTick = TimeManager.UNSET_TICK; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/EstimatedTick.cs.meta b/Assets/FishNet/Runtime/Connection/EstimatedTick.cs.meta new file mode 100644 index 0000000..9459016 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/EstimatedTick.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 36df408e03d0cab4ab728897541c2bf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/EstimatedTick.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs new file mode 100644 index 0000000..524faf9 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs @@ -0,0 +1,111 @@ +using FishNet.Broadcast; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Transporting; +using FishNet.Object; +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(); + /// + /// 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(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, DataOrderType orderType = DataOrderType.Default) + { + //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, orderType); + 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..c4cacf9 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5bc17ee5bac499347a6fbad9dd24b7b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/NetworkConnection.Buffer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs new file mode 100644 index 0000000..79ecfaa --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs @@ -0,0 +1,98 @@ +using FishNet.Component.Observing; +using FishNet.Managing; +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 + { + #region Internal. + /// + /// Current GridEntry this connection is in. + /// + internal GridEntry HashGridEntry = HashGrid.EmptyGridEntry; + #endregion + + #region Private. + /// + /// HashGrid for the NetworkManager on this connection. + /// + private HashGrid _hashGrid; + /// + /// Last unscaled time the HashGrid position was updated with this connections Objects. + /// + private float _nextHashGridUpdateTime; + /// + /// Current GridPosition this connection is in. + /// + private Vector2Int _hashGridPosition = HashGrid.UnsetGridPosition; + #endregion + + /// + /// Called when the FirstObject changes for this connection. + /// + private void Observers_FirstObjectChanged() + { + UpdateHashGridPositions(true); + } + + /// + /// Initializes this for use. + /// + private void Observers_Initialize(NetworkManager nm) + { + nm.TryGetInstance(out _hashGrid); + } + + /// + /// Updates the HashGridPosition value for FirstObject. + /// + internal void UpdateHashGridPositions(bool force) + { + if (_hashGrid == null) + return; + + float unscaledTime = Time.unscaledTime; + //Not enough time has passed to update. + if (!force && unscaledTime < _nextHashGridUpdateTime) + return; + + const float updateInterval = 1f; + _nextHashGridUpdateTime = unscaledTime + updateInterval; + + if (FirstObject == null) + { + HashGridEntry = HashGrid.EmptyGridEntry; + _hashGridPosition = HashGrid.UnsetGridPosition; + } + else + { + Vector2Int newPosition = _hashGrid.GetHashGridPosition(FirstObject); + if (newPosition != _hashGridPosition) + { + _hashGridPosition = newPosition; + HashGridEntry = _hashGrid.GetGridEntry(newPosition); + } + } + } + + /// + /// Resets values. + /// + private void Observers_Reset() + { + _hashGrid = null; + _hashGridPosition = HashGrid.UnsetGridPosition; + _nextHashGridUpdateTime = 0f; + } + + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs.meta new file mode 100644 index 0000000..fe200b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e1d1fe830d38ad043bd5e616cf6386ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/NetworkConnection.Observers.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs new file mode 100644 index 0000000..f2acb4a --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs @@ -0,0 +1,105 @@ +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 + { +#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. */ + TimeManager tm = (NetworkManager == null) ? InstanceFinder.TimeManager : NetworkManager.TimeManager; + /* Server FPS is running low, timing isn't reliable enough to kick clients. + * Respond with clients ping and remove infractions just in case the + * client received some from other server instabilities. */ + if (tm.LowFrameRate) + { + //_excessivePingCount = 0f; + return false; + } + + 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; + } + } + } + + +} \ 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..c5c5b71 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 20633bf6995f6534ba2b27e1eab3054d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/NetworkConnection.PingPong.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs new file mode 100644 index 0000000..fb01ff5 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs @@ -0,0 +1,122 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Managing; +using FishNet.Managing.Predicting; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +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 + { + /// + /// Approximate replicate tick on the server for this connection. + /// This also contains the last set value for local and remote. + /// + public EstimatedTick ReplicateTick { get; private set; } = new(); + /// + /// Writers for states. + /// + internal List PredictionStateWriters = new(); + + internal void Prediction_Initialize(NetworkManager manager, bool asServer) { } + + + /// + /// Writes a prediction state. + /// + /// + internal void WriteState(PooledWriter data) + { +#if !DEVELOPMENT + //Do not send states to clientHost. + if (IsLocalClient) + return; +#endif + + TimeManager timeManager = NetworkManager.TimeManager; + TransportManager transportManager = NetworkManager.TransportManager; + uint ticksBehind = (IsLocalClient) ? 0 : PacketTick.LocalTickDifference(timeManager); + /* If it's been a really long while the client could just be setting up + * or dropping. Only send if they've communicated within 5 seconds. */ + if (ticksBehind > (timeManager.TickRate * 5)) + return; + + int mtu = transportManager.GetLowestMTU((byte)Channel.Unreliable); + PooledWriter stateWriter; + int writerCount = PredictionStateWriters.Count; + /* Conditions to create a new writer are: + * - writer does not exist yet. + * - data length + currentWriter length > mtu */ + Channel channel = Channel.Unreliable; + if (writerCount > 0) + transportManager.CheckSetReliableChannel((data.Length + PredictionStateWriters[writerCount - 1].Length), ref channel); + /* If no writers or if channel would be forced reliable. + * + * By checking if channel would be reliable this is + * essentially asking if (current written + new data) would + * exceed mtu. When it would get a new writer to try + * and favor unreliable. Emphasis on try, because if some + * really unlikely chance the data was really large it would + * still send on reliable down the line. */ + if (writerCount == 0 || channel == Channel.Reliable) + { + stateWriter = WriterPool.Retrieve(mtu); + PredictionStateWriters.Add(stateWriter); + stateWriter.Skip(PredictionManager.STATE_HEADER_RESERVE_LENGTH); + /// 2 PacketId. + /// 4 Last replicate tick run for connection. + /// 4 Length unpacked. + } + else + { + stateWriter = PredictionStateWriters[writerCount - 1]; + } + + stateWriter.WriteArraySegment(data.GetArraySegment()); + } + + /// + /// Stores prediction writers to be re-used later. + /// + internal void StorePredictionStateWriters() + { + for (int i = 0; i < PredictionStateWriters.Count; i++) + WriterPool.Store(PredictionStateWriters[i]); + + PredictionStateWriters.Clear(); + } + + + /// + /// Sets the last tick a NetworkBehaviour replicated with. + /// + /// True to set unordered value, false to set ordered. + internal void SetReplicateTick(uint value, EstimatedTick.OldTickOption oldTickOption = EstimatedTick.OldTickOption.Discard) + { + ReplicateTick.Update(value, oldTickOption); + } + + + /// + /// Resets NetworkConnection. + /// + + private void Prediction_Reset() + { + StorePredictionStateWriters(); + ReplicateTick.Reset(); + } + } + + +} diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs.meta b/Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs.meta new file mode 100644 index 0000000..6bb9086 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a0068eb93a417a44b9151b61a08d8547 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/NetworkConnection.Prediction.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs new file mode 100644 index 0000000..6f31cff --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs @@ -0,0 +1,81 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Serializing; +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 + { + #region Public. + /// + /// Returns true if this connection is a clientHost. + /// + public bool IsHost => (NetworkManager == null) ? false : (NetworkManager.IsServerStarted && (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 = "") + { + if (CanKick()) + 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 = "") + { + if (CanKick()) + NetworkManager.ServerManager.Kick(this, reader, kickReason, loggingType, log); + } + + private bool CanKick() + { + //Connection isn't valid, calling kick on an empty connection. + if (!IsValid) + return false; + + //Should never happen. + if (NetworkManager == null) + { + NetworkManager = InstanceFinder.NetworkManager; + NetworkManager.LogError($"NetworkManager was not set for connection {this.ToString()}. InstanceFinder has been used."); + } + + return true; + } + } +} \ 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..694246e --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d45abc242399194b85e6c16bcb3676b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/NetworkConnection.QOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/NetworkConnection.cs b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs new file mode 100644 index 0000000..1c071cb --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs @@ -0,0 +1,470 @@ +using FishNet.Component.Observing; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using UnityEngine.SceneManagement; +using static FishNet.Managing.Timing.EstimatedTick; + +namespace FishNet.Connection +{ + + public static class NetworkConnectionExtensions + { + + /// + /// True if this connection is valid. An invalid connection indicates no client is set for this reference. + /// Null references can be used with this method. + /// + public static bool IsValid(this NetworkConnection c) + { + if (c == null) + return false; + else + return c.IsValid; + } + } + /// + /// A container for a connected client used to perform actions on and gather information for the declared client. + /// + public partial class NetworkConnection : IResettable, IEquatable + { + + #region Internal. + /// + /// Tick when Disconnecting was set. + /// + internal uint DisconnectingTick { get; private set; } + /// + /// ObjectIds to use for predicted spawning. + /// + internal Queue PredictedObjectIds = new(); + /// + /// True if the client has sent the same version that the server is on. + /// + internal bool HasSentVersion; + /// + /// LocalTick of the server when this connection was established. This value is not set for clients. + /// + internal uint ServerConnectionTick; + #endregion + + #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; + } + /// + /// TransportIndex this connection is on. + /// For security reasons this value will be unset on clients if this is not their connection. + /// This is not yet used. + /// + public int TransportIndex { get; private set; } = -1; + /// + /// True if this connection is authenticated. Only available to server. + /// + public bool IsAuthenticated { get; private set; } + [Obsolete("Use IsAuthenticated.")] //Remove in V5 + public bool Authenticated + { + get => IsAuthenticated; + set => IsAuthenticated = value; + } + /// + /// 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; + /// + /// Objects owned by this connection. Available to this connection and server. + /// + public HashSet Objects = new(); + /// + /// The first object within Objects. + /// + public NetworkObject FirstObject { get; private set; } + /// + /// Sets a custom FirstObject. This connection must be owner of the specified object. + /// + /// + public void SetFirstObject(NetworkObject nob) + { + //Invalid object. + if (!Objects.Contains(nob)) + { + string errMessage = $"FirstObject for {ClientId} cannot be set to {nob.name} as it's not within Objects for this connection."; + NetworkManager.LogError(errMessage); + return; + } + + FirstObject = nob; + } + /// + /// Scenes this connection is in. Available to this connection and server. + /// + public HashSet Scenes { get; private set; } = new(); + /// + /// True if this connection is being disconnected. Only available to server. + /// + public bool Disconnecting { 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; + /// + /// Tick of the last packet received from this connection which was not out of order. + /// This value is only available on the server. + /// + public EstimatedTick PacketTick { get; private set; } = new(); + /// + /// Approximate local tick as it is on this connection. + /// This also contains the last set value for local and remote. + /// + public EstimatedTick LocalTick { get; private set; } = new(); + #endregion + + #region Private. + /// + /// True if loaded start scenes as server. + /// + private bool _loadedStartScenesAsServer; + /// + /// True if loaded start scenes as client. + /// + private bool _loadedStartScenesAsClient; + #endregion + + #region Const. + /// + /// Value used when ClientId has not been set. + /// + public const int UNSET_CLIENTID_VALUE = -1; + /// + /// Maximum value a ClientId can be. + /// + public const int MAXIMUM_CLIENTID_VALUE = int.MaxValue; + /// + /// Maximum value a ClientId can be excluding simulated value. + /// + public const int MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE = (int.MaxValue - 1); + /// + /// Value to use as a ClientId when simulating a local client without actually using a socket. + /// + public const int SIMULATED_CLIENTID_VALUE = int.MaxValue; + /// + /// Number of bytes to reserve for a connectionId if writing the value uncompressed. + /// + public const int CLIENTID_UNCOMPRESSED_RESERVE_LENGTH = 4; + #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, int transportIndex, bool asServer) + { + Initialize(manager, clientId, transportIndex, asServer); + } + + /// + /// Outputs data about this connection as a string. + /// + /// + public override string ToString() + { + int clientId = ClientId; + string ip = (NetworkManager != null) ? NetworkManager.TransportManager.Transport.GetConnectionAddress(clientId) : "Unset"; + return $"Id [{ClientId}] Address [{ip}]"; + } + + /// + /// Initializes this for use. + /// + + private void Initialize(NetworkManager nm, int clientId, int transportIndex, bool asServer) + { + NetworkManager = nm; + LocalTick.Initialize(nm.TimeManager); + PacketTick.Initialize(nm.TimeManager); + if (asServer) + ServerConnectionTick = nm.TimeManager.LocalTick; + TransportIndex = transportIndex; + ClientId = clientId; + /* Set PacketTick to current values so + * that timeouts and other things around + * first packet do not occur due to an unset value. */ + PacketTick.Update(nm.TimeManager, 0, OldTickOption.SetLastRemoteTick); + Observers_Initialize(nm); + Prediction_Initialize(nm, asServer); + //Only the server uses the ping and buffer. + if (asServer) + { + InitializeBuffer(); + InitializePing(); + } + } + + /// + /// 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 (!IsValid) + { + NetworkManager.LogWarning($"Disconnect called on an invalid connection."); + return; + } + 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() + { + IsAuthenticated = true; + } + + /// + /// Adds to Objects owned by this connection. + /// + /// + + internal void AddObject(NetworkObject nob) + { + if (!IsValid) + return; + + Objects.Add(nob); + //If adding the first object then set new FirstObject. + if (Objects.Count == 1) + SetFirstObject(); + + OnObjectAdded?.Invoke(nob); + } + + /// + /// Removes from Objects owned by this connection. + /// + /// + + internal void RemoveObject(NetworkObject nob) + { + if (!IsValid) + { + ClearObjects(); + return; + } + + 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; + Observers_FirstObjectChanged(); + 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); + } + + /// + /// Resets all states for re-use. + /// + + public void ResetState() + { + MatchCondition.RemoveFromMatchesWithoutRebuild(this, NetworkManager); + + foreach (PacketBundle p in _toClientBundles) + p.Dispose(); + _toClientBundles.Clear(); + + ServerConnectionTick = 0; + PacketTick.Reset(); + LocalTick.Reset(); + TransportIndex = -1; + ClientId = -1; + ClearObjects(); + IsAuthenticated = false; + HasSentVersion = false; + NetworkManager = null; + _loadedStartScenesAsClient = false; + _loadedStartScenesAsServer = false; + SetDisconnecting(false); + Scenes.Clear(); + PredictedObjectIds.Clear(); + ResetPingPong(); + Observers_Reset(); + Prediction_Reset(); + } + + public void InitializeState() { } + + } + + +} \ 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..de448b5 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/NetworkConnection.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 01c7bfc71e29621408451fa2fa6b1a0b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/NetworkConnection.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Connection/OldTickOption.cs b/Assets/FishNet/Runtime/Connection/OldTickOption.cs new file mode 100644 index 0000000..67a5b5e --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/OldTickOption.cs @@ -0,0 +1,7 @@ +using System.Runtime.CompilerServices; + +namespace FishNet.Managing.Timing +{ + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Connection/OldTickOption.cs.meta b/Assets/FishNet/Runtime/Connection/OldTickOption.cs.meta new file mode 100644 index 0000000..a45b0a2 --- /dev/null +++ b/Assets/FishNet/Runtime/Connection/OldTickOption.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 139cb2731a4787e47919ef19010d7953 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Connection/OldTickOption.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Documenting.meta b/Assets/FishNet/Runtime/Documenting.meta new file mode 100644 index 0000000..8bfea04 --- /dev/null +++ b/Assets/FishNet/Runtime/Documenting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3eb13a869216f634eb89b030d30bf879 +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..0f29c96 --- /dev/null +++ b/Assets/FishNet/Runtime/Documenting/Attributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9d101aaaeb244ac48bfe2d7d05308c1c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Documenting/Attributes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor.meta b/Assets/FishNet/Runtime/Editor.meta new file mode 100644 index 0000000..7c7c355 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 652ea22ae970f014187104e2330ca034 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/BuildIdentifier.cs b/Assets/FishNet/Runtime/Editor/BuildIdentifier.cs new file mode 100644 index 0000000..d038a29 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/BuildIdentifier.cs @@ -0,0 +1,48 @@ +#if UNITY_EDITOR + +using System.IO; +using UnityEditor; +using UnityEditor.Callbacks; + +namespace FishNet.Editing +{ + /* When creating dedicated server builds this will place an empty file within + * the build folder. + * + * The file contains absolutely no information, and is used by our partners to identify how many of their customers are using + * Fish-Networking. + * + * The created file is not required -- you may delete the file and/or this code, but please + * consider retaining the file as it helps keep FishNet free. */ + + public class BuildIdentifier + { + [PostProcessBuild(1)] + public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject) + { + //Not a server build. +#if !SERVER_BUILD && !UNITY_SERVER + return; +#endif + + string buildPath = Path.GetDirectoryName(pathToBuiltProject); + if (buildPath == null) + return; + + //Validate that we are in the right folder. + string crashHandler = Path.Combine(buildPath, "UnityCrashHandler64.exe"); + if (File.Exists(crashHandler)) + { + //Try to create the empty file. + try + { + string filePath = Path.Combine(buildPath, "BuildIdentifier.json"); + File.WriteAllText(filePath, string.Empty); + } + finally { } + } + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/BuildIdentifier.cs.meta b/Assets/FishNet/Runtime/Editor/BuildIdentifier.cs.meta new file mode 100644 index 0000000..00bb86a --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/BuildIdentifier.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 390372a39ccc82a4d8bf9ca660da578a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/BuildIdentifier.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/CodeStripping.cs b/Assets/FishNet/Runtime/Editor/CodeStripping.cs new file mode 100644 index 0000000..4708263 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/CodeStripping.cs @@ -0,0 +1,116 @@ +#if UNITY_EDITOR + +using FishNet.Configuring; +using System.IO; +using UnityEngine; +using System.Xml.Serialization; + +using FishNet.Editing.PrefabCollectionGenerator; +using UnityEditor.Compilation; +using UnityEditor.Build.Reporting; +using UnityEditor; +using UnityEditor.Build; + +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; + + 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..62a5ff6 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/CodeStripping.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0c7409efe2f84e7428d5c6c97ed7d32e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/CodeStripping.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring.meta b/Assets/FishNet/Runtime/Editor/Configuring.meta new file mode 100644 index 0000000..833d87c --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f10fbb8567e7a7749831005ce1e1ee4f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs b/Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs new file mode 100644 index 0000000..c8d6ea2 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs @@ -0,0 +1,77 @@ +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +using FishNet.Object; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Editing.Beta +{ + public class BetaModeMenu : MonoBehaviour + { + #region const. + private const string STABLE_SYNCTYPES_DEFINE = "FISHNET_STABLE_SYNCTYPES"; + private const string STABLE_REPLICATESTATES_DEFINE = "FISHNET_STABLE_REPLICATESTATES"; + private const string STABLE_RECURSIVE_DESPAWNS_DEFINE = "FISHNET_STABLE_RECURSIVE_DESPAWNS"; + #endregion + + #region Beta SyncTypes +#if FISHNET_STABLE_SYNCTYPES + [MenuItem("Tools/Fish-Networking/Beta/Enable for SyncTypes", false, -1101)] + private static void EnableBetaSyncTypes() => SetBetaSyncTypes(useStable: false); +#else + [MenuItem("Tools/Fish-Networking/Beta/Disable for SyncTypes", false, -1101)] + private static void DisableBetaSyncTypes() => SetBetaSyncTypes(useStable: true); +#endif + private static void SetBetaSyncTypes(bool useStable) + { + bool result = DeveloperMenu.RemoveOrAddDefine(STABLE_SYNCTYPES_DEFINE, removeDefine: !useStable); + if (result) + Debug.LogWarning($"Beta SyncTypes are now {GetBetaEnabledText(useStable)}."); + } + #endregion + + #region Beta Recursive Despawns +#if FISHNET_STABLE_RECURSIVE_DESPAWNS + [MenuItem("Tools/Fish-Networking/Beta/Enable for Recursive Despawns", false, -1101)] + private static void EnableBetaRecursiveDespawns() => SetBetaRecursiveDespawns(useStable: false); +#else + [MenuItem("Tools/Fish-Networking/Beta/Disable for Recursive Despawns", false, -1101)] + private static void DisableBetaRecursiveDespawns() => SetBetaRecursiveDespawns(useStable: true); +#endif + private static void SetBetaRecursiveDespawns(bool useStable) + { + bool result = DeveloperMenu.RemoveOrAddDefine(STABLE_RECURSIVE_DESPAWNS_DEFINE, removeDefine: !useStable); + if (result) + Debug.LogWarning($"Beta Recursive Despawns are now {GetBetaEnabledText(useStable)}."); + } + #endregion + + #region Beta ReplicateStates +#if FISHNET_STABLE_REPLICATESTATES + [MenuItem("Tools/Fish-Networking/Beta/Enable for ReplicateStates", false, -1101)] + private static void EnableBetaReplicateStates() => SetBetaReplicateStates(useStable: false); +#else + [MenuItem("Tools/Fish-Networking/Beta/Disable for ReplicateStates", false, -1101)] + private static void DisableBetaReplicateStates() => SetBetaReplicateStates(useStable: true); +#endif + private static void SetBetaReplicateStates(bool useStable) + { + bool result = DeveloperMenu.RemoveOrAddDefine(STABLE_REPLICATESTATES_DEFINE, removeDefine: !useStable); + if (result) + Debug.LogWarning($"Beta ReplicateStates are now {GetBetaEnabledText(useStable)}."); + } + #endregion + + private static string GetBetaEnabledText(bool useStable) + { + return (useStable) ? "disabled" : "enabled"; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs.meta new file mode 100644 index 0000000..9f62693 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ecf03df72c983164586a22e695d17905 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/BetaModeMenu.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs new file mode 100644 index 0000000..e9757b5 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs @@ -0,0 +1,156 @@ +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +using System; +using System.Collections.Generic; +using System.IO; +using System.Xml.Serialization; +using UnityEditor; +using UnityEngine; + + +namespace FishNet.Configuring +{ + + public enum StrippingTypes : int + { + Redirect = 0, + Empty_Experimental = 1, + } + public enum SearchScopeType : int + { + EntireProject = 0, + SpecificFolders = 1, + } + + public class CreateNewNetworkBehaviourConfigurations + { + public string templateDirectoryPath = "Assets"; + + } + + public class PrefabGeneratorConfigurations + { + public bool Enabled = true; + public bool LogToConsole = true; + public bool FullRebuild = false; + public bool SpawnableOnly = true; + public bool SaveChanges = true; + public string DefaultPrefabObjectsPath = Path.Combine("Assets", "DefaultPrefabObjects.asset"); + internal string DefaultPrefabObjectsPath_Platform => Generator.GetPlatformPath(DefaultPrefabObjectsPath); + public int SearchScope = (int)SearchScopeType.EntireProject; + public List ExcludedFolders = new(); + public List IncludedFolders = new(); + } + + 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(); + public CodeStrippingConfigurations CodeStripping = new(); + public CreateNewNetworkBehaviourConfigurations CreateNewNetworkBehaviour = new(); + } + + 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(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($"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(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($"An error occurred while writing ConfigurationData. Message: {ex.Message}"); + } + + } + + } + + +} +#endif \ 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..dd2738b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4be37e1b0afd29944ad4fa0b92ed8c7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/ConfigurationData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs new file mode 100644 index 0000000..c672ba8 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs @@ -0,0 +1,100 @@ +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +using FishNet.Object; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Editing +{ + public class ConfigurationEditor : EditorWindow + { + [MenuItem("Tools/Fish-Networking/Configuration", false, 0)] + public static void ShowConfiguration() + { + SettingsService.OpenProjectSettings("Project/Fish-Networking/Configuration"); + } + } + + public class DeveloperMenu : MonoBehaviour + { + #region const. + private const string QOL_ATTRIBUTES_DEFINE = "DISABLE_QOL_ATTRIBUTES"; + private const string DEVELOPER_ONLY_WARNING = "If you are not a developer or were not instructed to do this by a developer things are likely to break. You have been warned."; + #endregion + + #region QOL Attributes +#if DISABLE_QOL_ATTRIBUTES + [MenuItem("Tools/Fish-Networking/Utility/Quality of Life Attributes/Enable", false, -999)] + private static void EnableQOLAttributes() + { + bool result = RemoveOrAddDefine(QOL_ATTRIBUTES_DEFINE, removeDefine: true); + if (result) + Debug.LogWarning($"Quality of Life Attributes have been enabled."); + } +#else + [MenuItem("Tools/Fish-Networking/Utility/Quality of Life Attributes/Disable", false, 0)] + private static void DisableQOLAttributes() + { + bool result = RemoveOrAddDefine(QOL_ATTRIBUTES_DEFINE, removeDefine: false); + if (result) + Debug.LogWarning($"Quality of Life Attributes have been disabled. {DEVELOPER_ONLY_WARNING}"); + } +#endif + #endregion + + internal static bool RemoveOrAddDefine(string define, bool removeDefine) + { + string currentDefines = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup); + HashSet definesHs = new(); + string[] currentArr = currentDefines.Split(';'); + + //Add any define which doesn't contain MIRROR. + foreach (string item in currentArr) + definesHs.Add(item); + + int startingCount = definesHs.Count; + + if (removeDefine) + definesHs.Remove(define); + else + definesHs.Add(define); + + bool modified = (definesHs.Count != startingCount); + if (modified) + { + string changedDefines = string.Join(";", definesHs); + PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, changedDefines); + } + + return modified; + } + } + + public class RefreshDefaultPrefabsMenu : MonoBehaviour + { + /// + /// Rebuilds the DefaultPrefabsCollection file. + /// + [MenuItem("Tools/Fish-Networking/Utility/Refresh Default Prefabs", false, 300)] + public static void RebuildDefaultPrefabs() + { +#if PARRELSYNC + if (ParrelSync.ClonesManager.IsClone() && ParrelSync.Preferences.AssetModPref.Value) + { + Debug.Log("Cannot perform this operation on a ParrelSync clone"); + return; + } +#endif + Debug.Log("Refreshing default prefabs."); + Generator.GenerateFull(null, true); + } + } + +} + +#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..d1ae57d --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8135b3a4c31cfb74896f1e9e77059c89 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/ConfigurationEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs new file mode 100644 index 0000000..d90114f --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs @@ -0,0 +1,110 @@ +#if UNITY_EDITOR +using System.IO; +using System.Xml.Serialization; +using UnityEngine; +using UnityEditor.Compilation; +using UnityEditor.Build.Reporting; +using UnityEditor; +using UnityEditor.Build; + +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("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 true if this editor is a multiplayer clone. + /// + /// + public static bool IsMultiplayerClone() + { +#if UNITY_EDITOR + if (Application.dataPath.ToLower().Contains("library/vp/")) + return true; + +#if PARRELSYNC + return ParrelSync.ClonesManager.IsClone(); +#endif +#endif + return false; + } + + /// + /// 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(typeof(ConfigurationData)); + fs = new(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(); + //Don't unset loaded, if its true then it should have proper info. + //_configurationData.Loaded = false; + } + } + + return _configurations; + } + } +} + +#endif \ No newline at end of file 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..1eafdfd --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8d05bf07ec9af2c46a1fe6c24871cccb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/Configuring.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs b/Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs new file mode 100644 index 0000000..5f9fd08 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs @@ -0,0 +1,60 @@ +#if UNITY_EDITOR +using System; +using FishNet.Configuring; +using FishNet.Managing; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Editing +{ + /// + /// Contributed by YarnCat! Thank you! + /// + [InitializeOnLoad] + public class DelayedEditorTasks : EditorWindow + { + private static double _startTime = double.MinValue; + + static DelayedEditorTasks() + { + if (Configuration.IsMultiplayerClone()) + return; + + const string startupCheckString = "FishNetDelayedEditorTasks"; + if (SessionState.GetBool(startupCheckString, false)) + return; + + _startTime = EditorApplication.timeSinceStartup; + EditorApplication.update += CheckRunTasks; + + SessionState.SetBool(startupCheckString, true); + } + + private static void CheckRunTasks() + { + if (EditorApplication.timeSinceStartup - _startTime < 1f) + return; + + EditorApplication.update -= CheckRunTasks; + + LogFeedbackLink(); + + //First time use, no other actions should be done. + if (FishNetGettingStartedEditor.ShowGettingStarted()) + return; + + ReviewReminderEditor.CheckRemindToReview(); + } + + private static void LogFeedbackLink() + { + //Only log the link when editor opens. + if (Time.realtimeSinceStartup < 10f) + { + string msg = $"Thank you for using Fish-Networking! If you have any feedback -- be suggestions, documentation, or performance related, let us know through our anonymous Google feedback form!{Environment.NewLine}" + @"https://forms.gle/1g13VY4KKMnEqpkp6"; + Debug.Log(msg); + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs.meta new file mode 100644 index 0000000..387a871 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eb3b5223de134ee41bc1ad462ce897f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/DelayedEditorTasks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs new file mode 100644 index 0000000..f482751 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs @@ -0,0 +1,139 @@ +#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("Tools/Fish-Networking/Getting Started",isValidateFunction: false, 9999)] + public static void GettingStartedMenu() + { + FishNetGettingStartedEditor window = (FishNetGettingStartedEditor)EditorWindow.GetWindow(typeof(FishNetGettingStartedEditor)); + window.position = new(0, 0, 320, 355); + Rect mainPos; + mainPos = EditorGUIUtility.GetMainWindowPosition(); + 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("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("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(1, 1, 1, 1); + } + + internal static bool ShowGettingStarted() + { + bool shown = EditorPrefs.GetBool(SHOWED_GETTING_STARTED, false); + if (!shown) + { + EditorPrefs.SetBool(SHOWED_GETTING_STARTED, true); + ReviewReminderEditor.ResetDateTimeReminded(); + GettingStartedMenu(); + + return true; + } + + return false; + } + + private 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(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..1a64d58 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 335aec9a9dce4944994cb57ac704ba5a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/FIshNetGettingStartedEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs new file mode 100644 index 0000000..65090a3 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs @@ -0,0 +1,462 @@ +#if UNITY_EDITOR +using System.Collections.Generic; +using System.Linq; +using FishNet.Editing.PrefabCollectionGenerator; +using FishNet.Object; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityScene = UnityEngine.SceneManagement.Scene; +using UnitySceneManagement = UnityEngine.SceneManagement; + +namespace FishNet.Editing +{ + /// + /// Contributed by YarnCat! Thank you! + /// + public class ReserializeNetworkObjectsEditor : EditorWindow + { + /// + /// True if currently iterating. + /// + [System.NonSerialized] + internal static bool IsRunning; + + private enum ReserializeSceneType : int + { + AllScenes = 0, + OpenScenes = 1, + SelectedScenes = 2, + BuildScenes = 3, + } + + private struct OpenScene + { + public UnityScene Scene; + public string Path; + + public OpenScene(UnityScene scene) + { + Scene = scene; + Path = scene.path; + } + } + + private Texture2D _fishnetLogo; + private Texture2D _buttonBg; + private Texture2D _buttonBgHover; + private GUIStyle _upgradeRequiredStyle; + private GUIStyle _instructionsStyle; + private GUIStyle _buttonStyle; + + private bool _loaded; + + private bool _iteratePrefabs; + private bool _iterateScenes; + private ReserializeSceneType _sceneReserializeType = ReserializeSceneType.OpenScenes; + private bool _enabledOnlyBuildScenes = true; + + private const string UPGRADE_PART_COLOR = "cd61ff"; + private const string UPGRADE_COMPLETE_COLOR = "32e66e"; + private const string PREFS_PREFIX = "FishNetReserialize"; + + private static ReserializeNetworkObjectsEditor _window; + + [MenuItem("Tools/Fish-Networking/Utility/Reserialize NetworkObjects", false, 400)] + internal static void ReserializeNetworkObjects() + { + if (ApplicationState.IsPlaying()) + { + Debug.LogError($"NetworkObjects cannot be reserialized while in play mode."); + return; + } + + InitializeWindow(); + } + + private static void InitializeWindow() + { + if (_window != null) + return; + + _window = (ReserializeNetworkObjectsEditor)EditorWindow.GetWindow(typeof(ReserializeNetworkObjectsEditor)); + _window.position = new(0f, 0f, 550f, 300f); + Rect mainPos; + mainPos = EditorGUIUtility.GetMainWindowPosition(); + Rect 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; + } + + private static void StyleWindow() + { + if (_window == null) + return; + + _window._fishnetLogo = (Texture2D)AssetDatabase.LoadAssetAtPath("Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png", typeof(Texture)); + _window._upgradeRequiredStyle = new("label"); + _window._upgradeRequiredStyle.fontSize = 20; + _window._upgradeRequiredStyle.wordWrap = true; + _window._upgradeRequiredStyle.alignment = TextAnchor.MiddleCenter; + _window._upgradeRequiredStyle.normal.textColor = new Color32(255, 102, 102, 255); + + _window._instructionsStyle = new("label"); + _window._instructionsStyle.fontSize = 14; + _window._instructionsStyle.wordWrap = true; + _window._instructionsStyle.alignment = TextAnchor.MiddleCenter; + _window._instructionsStyle.normal.textColor = new Color32(255, 255, 255, 255); + _window._instructionsStyle.hover.textColor = new Color32(255, 255, 255, 255); + + _window._buttonBg = MakeBackgroundTexture(1, 1, new Color32(52, 111, 255, 255)); + _window._buttonBgHover = MakeBackgroundTexture(1, 1, new Color32(99, 153, 255, 255)); + _window._buttonStyle = new("button"); + _window._buttonStyle.fontSize = 18; + _window._buttonStyle.fontStyle = FontStyle.Bold; + _window._buttonStyle.normal.background = _window._buttonBg; + _window._buttonStyle.active.background = _window._buttonBgHover; + _window._buttonStyle.focused.background = _window._buttonBgHover; + _window._buttonStyle.onFocused.background = _window._buttonBgHover; + _window._buttonStyle.hover.background = _window._buttonBgHover; + _window._buttonStyle.onHover.background = _window._buttonBgHover; + _window._buttonStyle.alignment = TextAnchor.MiddleCenter; + _window._buttonStyle.normal.textColor = new(1, 1, 1, 1); + } + + private void OnGUI() + { + //If not yet loaded then set last used values. + if (!_loaded) + { + LoadLastValues(); + _loaded = true; + } + + float thisWidth = this.position.width; + StyleWindow(); + //Starting values. + Vector2 requiredSize = new Vector2(this.position.width, 160f); + + GUILayout.Box(_fishnetLogo, GUILayout.Width(requiredSize.x), GUILayout.Height(requiredSize.y)); + + GUILayout.Space(8f); + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(5f); + CreateInformationLabel("Use this window to refresh serialized values on all NetworkObject prefabs and scene NetworkObjects."); + EditorGUILayout.EndHorizontal(); + GUILayout.Space(8f); + + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(30f); + _iteratePrefabs = EditorGUILayout.Toggle("Reserialize Prefabs", _iteratePrefabs); + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + //Some dumb reason Unity moves the checkbox further when using nested settings. + float rebuildScenesSpacing = (_iterateScenes) ? 27f : 30f; + GUILayout.Space(rebuildScenesSpacing); + EditorGUILayout.BeginVertical(); + + _iterateScenes = EditorGUILayout.Toggle("Reserialize Scenes", _iterateScenes); + + if (_iterateScenes) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(15f); + _sceneReserializeType = (ReserializeSceneType)EditorGUILayout.EnumPopup("Targeted Scenes", _sceneReserializeType); + EditorGUILayout.EndHorizontal(); + + requiredSize.y += 20f; + + if (_sceneReserializeType == ReserializeSceneType.BuildScenes) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(30f); + _enabledOnlyBuildScenes = EditorGUILayout.Toggle("Enabled Only", _enabledOnlyBuildScenes); + EditorGUILayout.EndHorizontal(); + requiredSize.y += 18f; + } + + if (_sceneReserializeType != ReserializeSceneType.OpenScenes) + { + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(30f); + EditorGUILayout.HelpBox("This operation will open and close targeted scene one at a time. Your current open scenes will be closed and re-opened without saving.", MessageType.Warning); + EditorGUILayout.EndHorizontal(); + requiredSize.y += 40f; + } + } + + EditorGUILayout.EndVertical(); + EditorGUILayout.EndHorizontal(); + + requiredSize.y += 80f; + + GUILayout.Space(8f); + + EditorGUILayout.BeginHorizontal(); + + if (!_iteratePrefabs && !_iterateScenes) + GUI.enabled = false; + + if (GUILayout.Button("Run Task")) + { + IsRunning = true; + + SaveLastValues(); + + ReserializeProjectPrefabs(); + ReserializeScenes(); + + LogColoredText($"Task complete.", UPGRADE_COMPLETE_COLOR); + + _iteratePrefabs = false; + _iterateScenes = false; + + IsRunning = false; + } + + GUI.enabled = true; + EditorGUILayout.EndHorizontal(); + + this.minSize = requiredSize; + this.maxSize = this.minSize; + + void CreateInformationLabel(string text, FontStyle? style = null) + { + EditorGUILayout.BeginHorizontal(); + + FontStyle firstStyle = _instructionsStyle.fontStyle; + if (style != null) + _instructionsStyle.fontStyle = style.Value; + + GUILayout.Label(text, _instructionsStyle, GUILayout.Width(thisWidth * 0.95f)); + + _instructionsStyle.fontStyle = firstStyle; + EditorGUILayout.EndHorizontal(); + + requiredSize.y += 55f; + } + } + + private void LoadLastValues() + { + _iteratePrefabs = EditorPrefs.GetBool($"{PREFS_PREFIX}{nameof(_iteratePrefabs)}", defaultValue: false); + _iterateScenes = EditorPrefs.GetBool($"{PREFS_PREFIX}{nameof(_iterateScenes)}", defaultValue: false); + _sceneReserializeType = (ReserializeSceneType)EditorPrefs.GetInt($"{PREFS_PREFIX}{nameof(_sceneReserializeType)}", defaultValue: (int)ReserializeSceneType.OpenScenes); + _enabledOnlyBuildScenes = EditorPrefs.GetBool($"{PREFS_PREFIX}{nameof(_enabledOnlyBuildScenes)}", defaultValue: true); + } + + private void SaveLastValues() + { + EditorPrefs.SetBool($"{PREFS_PREFIX}{nameof(_iteratePrefabs)}", _iteratePrefabs); + EditorPrefs.SetBool($"{PREFS_PREFIX}{nameof(_iterateScenes)}", _iterateScenes); + EditorPrefs.SetInt($"{PREFS_PREFIX}{nameof(_sceneReserializeType)}", (int)_sceneReserializeType); + EditorPrefs.SetBool($"{PREFS_PREFIX}{nameof(_enabledOnlyBuildScenes)}", _enabledOnlyBuildScenes); + } + + private void ReserializeProjectPrefabs() + { + if (!_iteratePrefabs) + return; + + int checkedObjects = 0; + int duplicateNetworkObjectsRemoved = 0; + + bool modified = false; + + List networkObjects = Generator.GetNetworkObjects(settings: null); + foreach (NetworkObject nob in networkObjects) + { + checkedObjects++; + duplicateNetworkObjectsRemoved += nob.RemoveDuplicateNetworkObjects(); + + nob.ReserializeEditorSetValues(setWasActiveDuringEdit: true, setSceneId: false); + EditorUtility.SetDirty(nob); + + modified = true; + } + + if (modified) + AssetDatabase.SaveAssets(); + + Debug.Log($"Reserialized {checkedObjects} NetworkObject prefabs. Removed {duplicateNetworkObjectsRemoved} duplicate NetworkObject components."); + } + + private void ReserializeScenes() + { + if (!_iterateScenes) + return; + + int duplicateNetworkObjectsRemoved = 0; + int checkedObjects = 0; + int checkedScenes = 0; + int changedObjects = 0; + + List openScenes = GetOpenScenes(); + + //If running for open scenes only. + if (_sceneReserializeType == ReserializeSceneType.OpenScenes) + { + ReserializeScenes(openScenes, ref checkedScenes, ref checkedObjects, ref changedObjects, ref duplicateNetworkObjectsRemoved); + } + //Running on multiple scenes. + else + { + //When working on multiple scenes make sure open scenes are not dirty to prevent data loss. + foreach (OpenScene os in openScenes) + { + if (os.Scene.isDirty) + { + Debug.LogError($"One or more open scenes are dirty. To prevent data loss scene reserialization will not complete. Ensure all open scenes are saved before continuing."); + return; + } + } + List targetedScenes; + if (_sceneReserializeType == ReserializeSceneType.SelectedScenes) + { + targetedScenes = Selection.GetFiltered(SelectionMode.Assets).ToList(); + } + else if (_sceneReserializeType == ReserializeSceneType.AllScenes) + { + targetedScenes = new(); + + string[] scenePaths = Generator.GetProjectFiles("Assets", "unity", new(), recursive: true); + foreach (string path in scenePaths) + { + SceneAsset sceneAsset = AssetDatabase.LoadAssetAtPath(path); + if (sceneAsset != null) + targetedScenes.Add(sceneAsset); + } + } + else if (_sceneReserializeType == ReserializeSceneType.BuildScenes) + { + targetedScenes = new(); + + EditorBuildSettingsScene[] buildScenes = EditorBuildSettings.scenes; + foreach (EditorBuildSettingsScene bs in buildScenes) + { + if (_enabledOnlyBuildScenes && !bs.enabled) + continue; + + SceneAsset sceneAsset = AssetDatabase.LoadAssetAtPath(bs.path); + if (sceneAsset != null) + targetedScenes.Add(sceneAsset); + } + } + else + { + Debug.LogError($"Unsupported {nameof(ReserializeSceneType)} type {_sceneReserializeType}."); + return; + } + + ReserializeScenes(targetedScenes, ref checkedScenes, ref checkedObjects, ref changedObjects, ref duplicateNetworkObjectsRemoved); + + //Reopen original scenes. + for (int i = 0; i < openScenes.Count; i++) + { + string path = openScenes[i].Path; + + /* Make sure asset exists before trying to reopen scene. + * Its possible the dev had a scene open that wasn't saved, which + * would otherwise result in an error here. */ + SceneAsset sceneAsset = AssetDatabase.LoadAssetAtPath(path); + if (sceneAsset != null) + { + OpenSceneMode mode = (i == 0) ? OpenSceneMode.Single : OpenSceneMode.Additive; + EditorSceneManager.OpenScene(path, mode); + } + } + } + + if (changedObjects > 0) + AssetDatabase.SaveAssets(); + + string saveText = ((_sceneReserializeType == ReserializeSceneType.OpenScenes) && (changedObjects > 0)) ? " Please save your open scenes." : string.Empty; + Debug.Log($"Checked {checkedObjects} NetworkObjects over {checkedScenes} scenes. {changedObjects} sceneIds were generated. {duplicateNetworkObjectsRemoved} duplicate NetworkObject components were removed. {saveText}"); + + LogColoredText($"Scene NetworkObjects refreshed.", UPGRADE_PART_COLOR); + + List GetOpenScenes() + { + List result = new(); + int sceneCount = UnitySceneManagement.SceneManager.sceneCount; + for (int i = 0; i < sceneCount; i++) + { + UnityScene scene = UnitySceneManagement.SceneManager.GetSceneAt(i); + if (scene.isLoaded) + result.Add(new(scene)); + } + + return result; + } + } + + /// + /// Refreshes NetworkObjects for specified scenes. + /// + private static void ReserializeScenes(List sceneAssets, ref int checkedScenes, ref int checkedObjects, ref int changedObjects, ref int duplicateNetworkObjectsRemoved) + { + foreach (SceneAsset sa in sceneAssets) + { + string path = AssetDatabase.GetAssetPath(sa); + UnityScene scene = EditorSceneManager.OpenScene(path, OpenSceneMode.Single); + List foundNobs = NetworkObject.CreateSceneId(scene, force: true, out int changed); + + foreach (NetworkObject n in foundNobs) + { + duplicateNetworkObjectsRemoved += n.RemoveDuplicateNetworkObjects(); + n.ReserializeEditorSetValues(setWasActiveDuringEdit: true, setSceneId: false); + } + + EditorSceneManager.SaveScene(scene); + + checkedScenes++; + checkedObjects += foundNobs.Count; + changedObjects += changed; + } + } + + /// + /// Refreshes NetworkObjects in OpenScenes. + /// + private static void ReserializeScenes(List openScenes, ref int checkedScenes, ref int checkedObjects, ref int changedObjects, ref int duplicateNetworkObjectsRemoved) + { + foreach (OpenScene os in openScenes) + { + List foundNobs = NetworkObject.CreateSceneId(os.Scene, force: true, out int changed); + + foreach (NetworkObject n in foundNobs) + { + duplicateNetworkObjectsRemoved += n.RemoveDuplicateNetworkObjects(); + n.ReserializeEditorSetValues(setWasActiveDuringEdit: true, setSceneId: false); + } + + checkedScenes++; + checkedObjects += foundNobs.Count; + changedObjects += changed; + } + } + + private static void LogColoredText(string txt, string hexColor) + { + Debug.Log($"{txt}"); + } + + 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(width, height); + backgroundTexture.SetPixels(pixels); + backgroundTexture.Apply(); + return backgroundTexture; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs.meta b/Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs.meta new file mode 100644 index 0000000..f673238 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d0f2e650746c37949a9dd7b96e7c6f65 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/ReserializeNetworkObjectsEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs new file mode 100644 index 0000000..70f7a14 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs @@ -0,0 +1,170 @@ +#if UNITY_EDITOR +using System; +using FishNet.Configuring; +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); + ResetDateTimeReminded(); + + ShowReminder(); + } + + internal static void ResetDateTimeReminded() + { + EditorPrefs.SetString(DATETIME_REMINDED, DateTime.Now.ToBinary().ToString()); + } + + private static void ShowReminder() + { + InitializeWindow(); + } + + private static void InitializeWindow() + { + if (_window != null) + return; + _window = (ReviewReminderEditor)EditorWindow.GetWindow(typeof(ReviewReminderEditor)); + _window.position = new(0f, 0f, 320f, 300f); + Rect mainPos; + mainPos = EditorGUIUtility.GetMainWindowPosition(); + Rect 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("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("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(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(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..f7aa26f --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4260206b6a57e4243b56437f8f283084 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/ReviewReminderEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs b/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs new file mode 100644 index 0000000..db5a065 --- /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("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() + { + padding = new(10, 10, 10, 10), + }; + + _scrollView = GUILayout.BeginScrollView(_scrollView, scrollViewStyle); + + EditorGUILayout.BeginHorizontal(); + + GUIStyle toggleStyle = new(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(); + 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..3c2b56b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b3d7d3c45d53dea4e8a0a7da73d64021 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Configuring/SettingsProvider.cs + uploadId: 762203 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..031e54b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Constants.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1d2683b3becd2c5488c1f338972d49e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Constants.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs new file mode 100644 index 0000000..f62afdf --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs @@ -0,0 +1 @@ +//Remove in V5 \ 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..1116cad --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2bd002f6c85dd4341bcaf163eaaa3ddf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/DefaultPrefabsFinder.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Finding.cs b/Assets/FishNet/Runtime/Editor/Finding.cs new file mode 100644 index 0000000..dbcea52 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Finding.cs @@ -0,0 +1,199 @@ +#if UNITY_EDITOR +using FishNet.Utility; +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 UpdateFishNetPaths() + { + 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 all GameObjects in Assets and optionally scenes. + /// + /// + /// + public static List GetGameObjects(bool userAssemblies, bool fishNetAssembly, bool includeScenes, string[] ignoredPaths = null) + { + List results = new(); + + string[] guids; + string[] objectPaths; + + UpdateFishNetPaths(); + + 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(); + + 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 buffer = new(); + //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(); + + 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(); + 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..a9afacf --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Finding.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b777233d19062274f9eec6a982d8ff37 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Finding.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs b/Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs new file mode 100644 index 0000000..0dbc921 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs @@ -0,0 +1,57 @@ +#if UNITY_EDITOR +using System; +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Editing +{ + /* When you import playeveryware's EOS asset, it force installs NGO, which creates + * a lot of issues for anyone not using NGO. This script will block the force installation. */ + public class ForceInstallPreventor : AssetPostprocessor + { + private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + // No need to continue if nothing was imported. + if (importedAssets == null || importedAssets.Length == 0) + return; + + EditorApplication.LockReloadAssemblies(); + + foreach (string path in importedAssets) + CheckTargetPath(path); + + /* We don't have a way to know if the user intentionally + * had domain locked so we just have to unlock it and hope + * we aren't messing up users settings. + * + * Worse case scenario this will only happen when the forceware + * is removed. + * + * There is a 'didDomainReload' boolean override for this + * method, but it does not seem to reflect the information + * we need. + * */ + EditorApplication.UnlockReloadAssemblies(); + } + + private void OnPreprocessAsset() + { + CheckTargetPath(assetImporter.assetPath); + } + + private static void CheckTargetPath(string path) + { + if (!path.Contains("PackageInstallHelper_Netcode", StringComparison.CurrentCultureIgnoreCase)) + return; + + try + { + File.Delete(path); + Debug.Log($"Fish-Networking prevented PlayEveryWare from forcefully installing Netcode for GameObjects."); + } + finally { } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs.meta b/Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs.meta new file mode 100644 index 0000000..fefe764 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7a0f9a4fe4875c54fa7820bc76908204 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/ForceInstallPreventor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour.meta b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour.meta new file mode 100644 index 0000000..841c756 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 564bc17539291c64683b5b20c06eda8b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs new file mode 100644 index 0000000..fcf59a4 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs @@ -0,0 +1,65 @@ +#if UNITY_EDITOR +using FishNet.Configuring; +using System; +using System.IO; +using System.Text; +using UnityEditor; +using UnityEngine; +using File = System.IO.File; + +namespace FishNet.Editing.NewNetworkBehaviourScript +{ + internal sealed class CreateNewNetworkBehaviour : MonoBehaviour + { + private const string TEMPLATE_CLASS_NAME = "NewNetworkBehaviourTemplate"; + public static string TemplatePath => Path.Combine(Configuration.Configurations.CreateNewNetworkBehaviour.templateDirectoryPath, $"{TEMPLATE_CLASS_NAME}.txt"); + + [MenuItem("Assets/Create/FishNet/NetworkBehaviour Script", false, -220)] + private static void CreateNewAsset() + { + try + { + EnsureTemplateExists(); + ProjectWindowUtil.CreateScriptAssetFromTemplateFile(TemplatePath, $"{TEMPLATE_CLASS_NAME}.cs"); + } + catch (Exception e) + { + Debug.LogError($"An exception occurred while trying to copy the NetworkBehaviour template. {e.Message}"); + } + } + + public static void EnsureTemplateExists() + { + try + { + if (!File.Exists(TemplatePath)) + { + string fileContent = GetNewTemplateText(); + File.WriteAllText(TemplatePath, fileContent); + } + } + catch (Exception e) + { + Debug.LogError($"An exception occurred while trying to create the NetworkBehaviour template. {e.Message}"); + } + } + + private static string GetNewTemplateText() + { + StringBuilder sb = new(); + sb.AppendLine("using UnityEngine;"); + sb.AppendLine("using FishNet.Object;"); + sb.AppendLine(); + sb.AppendLine("public class NewNetworkBehaviourTemplate : NetworkBehaviour"); + sb.AppendLine("{"); + sb.AppendLine(" private void Awake() { }"); + sb.AppendLine(); + sb.AppendLine(" private void Update() { }"); + sb.AppendLine("}"); + + return sb.ToString(); + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs.meta b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs.meta new file mode 100644 index 0000000..fe1b98f --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bd7764ab379082b4f9889c73b9133da9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/CreateNewNetworkBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs new file mode 100644 index 0000000..dd38379 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs @@ -0,0 +1,103 @@ +#if UNITY_EDITOR + +using UnityEditor; +using UnityEngine; +using UnitySettingsProviderAttribute = UnityEditor.SettingsProviderAttribute; +using UnitySettingsProvider = UnityEditor.SettingsProvider; +using FishNet.Configuring; +using System.IO; +using System; +using System.Text.RegularExpressions; + +namespace FishNet.Editing.NewNetworkBehaviourScript +{ + internal static class SettingsProvider + { + private static CreateNewNetworkBehaviourConfigurations _settings; + private static GUIContent _folderIcon; + private static readonly Regex SlashRegex = new(@"[\\//]"); + + [UnitySettingsProvider] + private static UnitySettingsProvider Create() + { + return new("Project/Fish-Networking/New NetworkBehaviour Template", SettingsScope.Project) + { + label = "New NetworkBehaviour Template", + + guiHandler = OnGUI, + + keywords = new string[] + { + "Fish", + "Networking", + "CreateNewNetworkBehaviour", + "Template" + }, + }; + } + + private static void OnGUI(string searchContext) + { + if (_settings == null) + _settings = Configuration.Configurations.CreateNewNetworkBehaviour; + + if (_folderIcon == null) + _folderIcon = EditorGUIUtility.IconContent("d_FolderOpened Icon"); + + EditorGUI.BeginChangeCheck(); + + GUILayoutOption iconWidthConstraint = GUILayout.MaxWidth(32.0f); + GUILayoutOption iconHeightConstraint = GUILayout.MaxHeight(EditorGUIUtility.singleLineHeight); + + if (GUILayout.Button("Edit template")) + { + CreateNewNetworkBehaviour.EnsureTemplateExists(); + + try + { + System.Diagnostics.Process.Start(CreateNewNetworkBehaviour.TemplatePath); + } + catch (Exception e) + { + Debug.LogError($"An issue occurred while trying to launch the NetworkBehaviour template. {e.Message}"); + } + } + GUILayout.Space(20); + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("Template directory: ", GUILayout.MaxWidth(150)); + string newDirectoryPath; + + newDirectoryPath = EditorGUILayout.DelayedTextField(_settings.templateDirectoryPath, GUILayout.MaxWidth(600)); + if (newDirectoryPath.StartsWith("Assets") && Directory.Exists(newDirectoryPath)) + { + _settings.templateDirectoryPath = newDirectoryPath; + } + else + { + EditorWindow.focusedWindow.ShowNotification(new($"Directory must be inside the Assets folder."), 2); + } + + + if (GUILayout.Button(_folderIcon, iconHeightConstraint, iconWidthConstraint)) + { + newDirectoryPath = EditorUtility.OpenFolderPanel("Select template directory", _settings.templateDirectoryPath, ""); + } + if (newDirectoryPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase)) + { + newDirectoryPath = SlashRegex.Replace(newDirectoryPath.Remove(0, Path.GetDirectoryName(Application.dataPath).Length + 1), Path.DirectorySeparatorChar.ToString()); + _settings.templateDirectoryPath = newDirectoryPath; + } + else if (!newDirectoryPath.StartsWith(Application.dataPath, StringComparison.OrdinalIgnoreCase) && !newDirectoryPath.StartsWith("Assets")) + { + EditorWindow.focusedWindow.ShowNotification(new($"Directory must be inside the Assets folder."), 2); + } + + EditorGUILayout.EndHorizontal(); + +// EditorGUILayout.HelpBox("By default MonoBehaviour script template will be copied", MessageType.Info); + if (EditorGUI.EndChangeCheck()) + Configuration.Configurations.Write(true); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs.meta b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs.meta new file mode 100644 index 0000000..c11d26b --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ec84d908960b66b448f94582e747689b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/NewNetworkBehaviour/SettingsProvider.cs + uploadId: 762203 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..bd2d03e --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PlayModeTracker.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d4a1d20c6a03a524ab21c7aebed106d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/PlayModeTracker.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta new file mode 100644 index 0000000..65aa5dc --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a26a5fd0a07d6964faab3ddc4d7454ee +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..f57b930 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs @@ -0,0 +1,697 @@ +#if UNITY_EDITOR +using FishNet.Configuring; +using FishNet.Managing; +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. + internal readonly struct SpecifiedFolder + { + public readonly string Path; + public readonly 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(); + /// + /// 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 + + internal static string[] GetProjectFiles(string startingPath, string fileExtension, List excludedPaths, bool recursive) + { + //Opportunity to exit early if there are no excluded paths. + if (excludedPaths.Count == 0) + { + string[] strResults = Directory.GetFiles(startingPath, $"*{fileExtension}", SearchOption.AllDirectories); + return strResults; + } + //starting path is excluded. + if (excludedPaths.Contains(startingPath)) + return new string[0]; + + //Folders remaining to be iterated. + List enumeratedCollection = new() { 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(); + //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(); + } + + /// + /// 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 PARRELSYNC + if (ParrelSync.ClonesManager.IsClone() && ParrelSync.Preferences.AssetModPref.Value) + { + UnityDebug.Log("Skipping prefab generation in ParrelSync clone"); + return; + } +#endif + + //Do not run if the reserializer is currently running. + if (ReserializeNetworkObjectsEditor.IsRunning) + return; + + 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(); + + 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 (CanAddNetworkObject(nob, settings)) + { + 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(); + } + + /// + /// Gets NetworkObjects from folders using settings. + /// + internal static List GetNetworkObjects(PrefabGeneratorConfigurations settings = null) + { + List folders = GetSpecifiedFolders(settings); + if (folders == null) + return new(); + + return GetNetworkObjects(folders, settings); + } + + /// + /// Gets specified folders to check for prefab generation. This may include excluded paths. Null is returned on error. + /// + internal static List GetSpecifiedFolders(PrefabGeneratorConfigurations settings = null) + { + settings = GetSettingsIfNull(settings); + + List folderStrs; + + if (settings.SearchScope == (int)SearchScopeType.EntireProject) + { + folderStrs = new List(); + folderStrs.Add("Assets*"); + } + else if (settings.SearchScope == (int)SearchScopeType.SpecificFolders) + { + folderStrs = settings.IncludedFolders.ToList(); + } + else + { + UnityDebug.LogError($"{settings.SearchScope} is not handled; folder paths cannot be found."); + return null; + } + + return GetSpecifiedFolders(folderStrs); + } + + + /// + /// Gets all NetworkObjects in specified folders while ignoring any excluded paths. + /// + internal static List GetNetworkObjects(SpecifiedFolder specifiedFolder, PrefabGeneratorConfigurations settings = null) + { + List excludedPaths = GetSettingsIfNull(settings).ExcludedFolders; + + List foundNobs = new(); + + foreach (string path in GetProjectFiles(specifiedFolder.Path, "prefab", excludedPaths, specifiedFolder.Recursive)) + { + NetworkObject nob = AssetDatabase.LoadAssetAtPath(path); + if (CanAddNetworkObject(nob, settings)) + foundNobs.Add(nob); + } + + return foundNobs; + } + + /// + /// Gets all NetworkObjects in specified folders while ignoring any excluded paths. + /// + internal static List GetNetworkObjects(List specifiedFolders, PrefabGeneratorConfigurations settings = null) + { + List foundNobs = new(); + + foreach (SpecifiedFolder sf in specifiedFolders) + foundNobs.AddRange(GetNetworkObjects(sf, settings)); + + return foundNobs; + } + + + /// + /// Generates prefabs by iterating all files within settings parameters. + /// + public static void GenerateFull(PrefabGeneratorConfigurations settings = null, bool forced = false, bool initializeAdded = true) + { +#if PARRELSYNC + if (ParrelSync.ClonesManager.IsClone() && ParrelSync.Preferences.AssetModPref.Value) + { + UnityDebug.Log("Skipping prefab generation in ParrelSync clone"); + return; + } +#endif + //Do not run if the reserializer is currently running. + if (ReserializeNetworkObjectsEditor.IsRunning) + { + UnityDebug.LogError($"Cannot generate default prefabs when ReserializeNetworkObjectsEditor is running"); + return; + } + + settings = GetSettingsIfNull(settings); + + if (!forced && !settings.Enabled) + return; + + bool log = settings.LogToConsole; + + Stopwatch sw = (log) ? Stopwatch.StartNew() : null; + + DefaultPrefabObjects prefabCollection = GetDefaultPrefabObjects(settings); + //No need to error if not found, GetDefaultPrefabObjects will throw. + if (prefabCollection == null) + return; + + List foundNobs = GetNetworkObjects(settings); + + //Clear and add built list. + prefabCollection.Clear(); + prefabCollection.AddObjects(foundNobs, checkForDuplicates: false, initializeAdded); + 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(); + } + } + + /// + /// Returns settings parameter if not null, otherwise returns settings from configuration. + /// + internal static PrefabGeneratorConfigurations GetSettingsIfNull(PrefabGeneratorConfigurations settings) => (settings == null) ? Configuration.Configurations.PrefabGenerator : settings; + + /// + /// Iterates folders building them into SpecifiedFolders. + /// + internal static List GetSpecifiedFolders(List strFolders) + { + List results = new(); + //Remove astericks. + foreach (string path in strFolders) + { + 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; + } + + p = GetPlatformPath(p); + + //Path does not exist. + if (!Directory.Exists(p)) + continue; + + results.Add(new(p, recursive)); + } + + + RemoveOverlappingFolders(results); + + return results; + + //Removes paths which may overlap each other, such as sub directories. + static void RemoveOverlappingFolders(List specifiedFolders) + { + for (int z = 0; z < specifiedFolders.Count; z++) + { + for (int i = 0; i < specifiedFolders.Count; i++) + { + //Do not check against self. + if (i == z) + continue; + + //Duplicate. + if (specifiedFolders[z].Path.Equals(specifiedFolders[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."); + specifiedFolders.RemoveAt(i); + break; + } + + /* We are checking if i can be within + * z. This is only possible if i is longer + * than z. */ + if (specifiedFolders[i].Path.Length < specifiedFolders[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 (!specifiedFolders[z].Recursive) + continue; + + //Compare paths. + string zPath = GetPathWithSeparator(specifiedFolders[z].Path); + string iPath = zPath.Substring(0, zPath.Length); + //If paths match. + if (iPath.Equals(zPath, System.StringComparison.OrdinalIgnoreCase)) + { + UnityDebug.LogError($"Path {specifiedFolders[i].Path} is included within recursive path {specifiedFolders[z].Path}. Remove path {specifiedFolders[i].Path} to clear this error."); + specifiedFolders.RemoveAt(i); + break; + } + } + } + + string GetPathWithSeparator(string txt) + { + return txt.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + Path.DirectorySeparatorChar; + } + } + } + + internal static string GetPlatformPath(string path) + { + if (string.IsNullOrEmpty(path)) + return path; + + path = path.Replace(@"\"[0], Path.DirectorySeparatorChar); + path = path.Replace(@"/"[0], Path.DirectorySeparatorChar); + return path; + } + + /// + /// Returns the DefaultPrefabObjects file. + /// + internal static DefaultPrefabObjects GetDefaultPrefabObjects(PrefabGeneratorConfigurations settings = null) + { + if (settings == null) + settings = Configuration.Configurations.PrefabGenerator; + + //If not using default prefabs then exit early. + if (!settings.Enabled) + return null; + + //Load the prefab collection + string defaultPrefabsPath = settings.DefaultPrefabObjectsPath_Platform; + 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 -> Utility -> Refresh Default Prefabs to refresh 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 PARRELSYNC + if (!ParrelSync.ClonesManager.IsClone() && ParrelSync.Preferences.AssetModPref.Value) + { +#endif + 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 PARRELSYNC + } +#endif + + 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_Platform); + //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 (CanAddNetworkObject(nob, settings)) + { + //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; + } + //Other conditions which a full rebuild may be required. + else if (!fullRebuild) + { + const string fishnetVersionSave = "fishnet_version"; + string savedVersion = EditorPrefs.GetString(fishnetVersionSave, string.Empty); + if (savedVersion != NetworkManager.FISHNET_VERSION) + { + fullRebuild = true; + EditorPrefs.SetString(fishnetVersionSave, NetworkManager.FISHNET_VERSION); + } + } + + if (fullRebuild) + GenerateFull(settings); + else + GenerateChanged(importedAssets, deletedAssets, movedAssets, movedFromAssetPaths, settings); + } + } + + /// + /// Returns true if a NetworkObject can be added to DefaultPrefabs. + /// + private static bool CanAddNetworkObject(NetworkObject networkObject, PrefabGeneratorConfigurations settings) + { + settings = GetSettingsIfNull(settings); + return networkObject != null && (networkObject.GetIsSpawnable() || !settings.SpawnableOnly); + } + } +} + +#endif \ No newline at end of file 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..6a02604 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 68e990388e202d54aa0fe9e7aa8cc716 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/Generator.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs new file mode 100644 index 0000000..011a40c --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs @@ -0,0 +1,239 @@ +#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(@"[\\//]"); + + 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("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() + { + padding = new(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.SpawnableOnly = EditorGUILayout.Toggle(ObjectNames.NicifyVariableName(nameof(_settings.SpawnableOnly)), _settings.SpawnableOnly); + _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($"{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("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($"{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($"{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($"{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($"{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..33ce221 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7451fcc5eeb5b89468ab2ce22f678b26 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/PrefabCollectionGenerator/SettingsProvider.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs new file mode 100644 index 0000000..0bbb9d1 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs @@ -0,0 +1,98 @@ +#if UNITY_EDITOR +using FishNet.Managing; +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.Build; +using UnityEngine; + +namespace FishNet +{ + internal static class ScriptingDefines + { + [InitializeOnLoadMethod] + public static void AddDefineSymbols() + { + // Get data about current target group + bool standaloneAndServer = false; + BuildTarget buildTarget = EditorUserBuildSettings.activeBuildTarget; + BuildTargetGroup buildTargetGroup = BuildPipeline.GetBuildTargetGroup(buildTarget); + if (buildTargetGroup == BuildTargetGroup.Standalone) + { + StandaloneBuildSubtarget standaloneSubTarget = EditorUserBuildSettings.standaloneBuildSubtarget; + if (standaloneSubTarget == StandaloneBuildSubtarget.Server) + standaloneAndServer = true; + } + + // Prepare named target, depending on above stuff + NamedBuildTarget namedBuildTarget; + if (standaloneAndServer) + namedBuildTarget = NamedBuildTarget.Server; + else + namedBuildTarget = NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup); + + string currentDefines = PlayerSettings.GetScriptingDefineSymbols(namedBuildTarget); + /* 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(); + 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[] currentVersionSplit = NetworkManager.FISHNET_VERSION.Split("."); + string thisVersion = $"{versionPrefix}{currentVersionSplit[0]}"; + + string[] fishNetDefines = new string[] + { + "FISHNET", + thisVersion, + + }; + bool modified = false; + //Now add FN defines. + foreach (string item in fishNetDefines) + modified |= definesHs.Add(item); + + //Remove old prediction defines. + modified |= definesHs.Remove("PREDICTION_V2"); + /* 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(); + 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.SetScriptingDefineSymbols(namedBuildTarget, 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..38739e6 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/ScriptingDefines.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 893e5074d486a0e4aaf7803436fef791 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/ScriptingDefines.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Textures.meta b/Assets/FishNet/Runtime/Editor/Textures.meta new file mode 100644 index 0000000..fd1a1a1 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3e64b45ed42e33641b9cc1f6ee7e2975 +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..f76868e --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/Icon.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e3fad7777e2bfd04f8ad002a7797348c +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 0000000000000000000000000000000000000000..084c0e6e72afce3e99d539e25ee751517c2fad53 GIT binary patch literal 41016 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 g;Z7R5v3r*UrrZeU(PmbT$ie+KYym;e9( literal 0 HcmV?d00001 diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/FishNet_Text.png.meta b/Assets/FishNet/Runtime/Editor/Textures/UI/FishNet_Text.png.meta new file mode 100644 index 0000000..7fd3af9 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI/FishNet_Text.png.meta @@ -0,0 +1,142 @@ +fileFormatVersion: 2 +guid: 9f392fdb366cd42408f283227989684c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + 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 + vTOnly: 0 + ignoreMasterTextureLimit: 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 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + cookieLightType: 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: WebGL + 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: Server + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Textures/UI/FishNet_Text.png + uploadId: 762203 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 0000000000000000000000000000000000000000..5e5d02e1a9508802f7d1942fd957366adbd7f02e GIT binary patch literal 37614 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 Tc+Vsu;MWT!4aI7Ci=h7lQEy3% literal 0 HcmV?d00001 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..bb74c63 --- /dev/null +++ b/Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png.meta @@ -0,0 +1,142 @@ +fileFormatVersion: 2 +guid: 5ea1cf1e0e57aff4e9ad3cd4246b0e80 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + 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 + ignoreMasterTextureLimit: 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 + cookieLightType: 1 + 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: WebGL + 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: Server + 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: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Editor/Textures/UI/Logo_With_Text.png + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Editor/Textures/UI/Server_Text.png b/Assets/FishNet/Runtime/Editor/Textures/UI/Server_Text.png new file mode 100644 index 0000000000000000000000000000000000000000..01816d0d8f9e22aac96103c3f67d82283807d174 GIT binary patch literal 6573 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 aps = new(); + //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.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) + { + NetworkManagerExtensions.Log("Synchronized parameters may not be changed while playing."); + } + else + { + if (ignored) + na.IgnoredParameters.Remove(parameterName); + else + na.IgnoredParameters.Add(parameterName); + } + UnityEditor.EditorUtility.SetDirty(target); + } + + 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..c9a1fca --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 65609e99a0823a347a2f615b0e6f736e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/Editor/NetworkAnimatorEditor.cs + uploadId: 762203 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..e8cbb9b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs @@ -0,0 +1,1512 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +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 GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using FishNet.Managing; +using UnityEngine; +using TimeManagerCls = FishNet.Managing.Timing.TimeManager; + +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(_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 + { + /// + /// Frame which the state was changed. + /// + public int FrameCount; + /// + /// 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 frame) + { + FrameCount = frame; + IsCrossfade = default; + Hash = default; + FixedTime = default; + DurationTime = default; + OffsetTime = default; + NormalizedTransitionTime = default; + } + + public StateChange(int frame, int hash, bool fixedTime, float duration, float offset, float normalizedTransition) + { + FrameCount = frame; + 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(); + #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) + { + NetworkManagerExtensions.LogWarning("Index exceeds Buffers count."); + return; + } + if (index > _bufferLengths.Length) + { + NetworkManagerExtensions.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(); + #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 synchronize changes even when the animator component is disabled. + /// + [Tooltip("True to synchronize changes even when the animator component is disabled.")] + [SerializeField] + private bool _synchronizeWhenDisabled; + /// + /// 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; + /// + /// + /// + [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 readonly List _parameterDetails = new(); + /// + /// Last int values. + /// + private readonly List _ints = new(); + /// + /// Last float values. + /// + private readonly List _floats = new(); + /// + /// Last bool values. + /// + private readonly List _bools = new(); + /// + /// Last layer weights. + /// + private float[] _layerWeights; + /// + /// Last speed. + /// + private float _speed; + /// + /// Trigger values set by using SetTrigger and ResetTrigger. + /// + private readonly List _triggerUpdates = new(); + // /// + // /// Updates going to clients. + // /// + // private List _toClientsBuffer = new(); + /// + /// Returns if the animator is exist and can be synchronized. + /// + private bool _canSynchronizeAnimator + { + get + { + if (!_isAnimatorSet) + return false; + + if (_animator.enabled || _synchronizeWhenDisabled) + return true; + + return false; + } + } + /// + /// True if the animator is valid but not enabled. + /// + private bool _isAnimatorSet + { + get + { + bool failedChecks = (_animator == null || _animator.runtimeAnimatorController == null); + return !failedChecks; + } + } + /// + /// Float valeus to smooth towards. + /// + private Dictionary _smoothedFloats = new(); + /// + /// Returns if floats can be smoothed for this client. + /// + private bool _canSmoothFloats + { + get + { + //Don't smooth on server only. + if (!base.IsClientStarted) + 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(); + /// + /// Last animator set. + /// + private Animator _lastAnimator; + /// + /// Last Controller set. + /// + private RuntimeAnimatorController _lastController; + /// + /// PooledWriter for this animator. + /// + private PooledWriter _writer = new(); + /// + /// 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(); + /// + /// Tick when the buffer may begin to run. + /// + private uint _startTick = TimeManagerCls.UNSET_TICK; + /// + /// True if subscribed to TimeManager for ticks. + /// + private bool _subscribedToTicks; + #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(); + } + + private void OnDestroy() + { + ChangeTickSubscription(false); + } + + [APIExclude] + public override void OnSpawnServer(NetworkConnection connection) + { + if (!_canSynchronizeAnimator) + return; + if (AnimatorUpdated(out ArraySegment updatedBytes, true)) + TargetAnimatorUpdated(connection, updatedBytes); + } + + public override void OnStartNetwork() + { + ChangeTickSubscription(true); + } + + [APIExclude] + public override void OnStartServer() + { + //If using client authoritative then initialize clientAuthoritativeUpdates. + if (_clientAuthoritative) + { + _clientAuthoritativeUpdates = new(); + // //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 OnStartClient() + { + base.TimeManager.OnUpdate += TimeManager_OnUpdate; + } + + public override void OnStopClient() + { + if (base.TimeManager != null) + base.TimeManager.OnUpdate -= TimeManager_OnUpdate; + } + + public override void OnStopNetwork() + { + _unsynchronizedLayerStates.Clear(); + ChangeTickSubscription(false); + } + + /// + /// Tries to subscribe to TimeManager ticks. + /// + private void ChangeTickSubscription(bool subscribe) + { + if (subscribe == _subscribedToTicks || base.NetworkManager == null) + return; + + _subscribedToTicks = subscribe; + if (subscribe) + { + base.NetworkManager.TimeManager.OnPreTick += TimeManager_OnPreTick; + base.NetworkManager.TimeManager.OnPostTick += TimeManager_OnPostTick; + } + else + { + base.NetworkManager.TimeManager.OnPreTick -= TimeManager_OnPreTick; + base.NetworkManager.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + } + + + + /// + /// Called right before a tick occurs, as well before data is read. + /// + private void TimeManager_OnPreTick() + { + if (!_canSynchronizeAnimator) + { + _fromServerBuffer.Clear(); + return; + } + //Disabled/cannot start. + if (_startTick == 0) + return; + //Nothing in queue. + if (_fromServerBuffer.Count == 0) + { + _startTick = 0; + 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. + /// + + private void TimeManager_OnPostTick() + { + //One check rather than per each method. + if (!_canSynchronizeAnimator) + return; + + CheckSendToServer(); + CheckSendToClients(); + } + + private void TimeManager_OnUpdate() + { + if (!_canSynchronizeAnimator) + return; + + if (base.IsClientStarted) + 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 (!_canSynchronizeAnimator) + { + //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) + { + base.NetworkManager.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(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.IsServerStarted || !base.IsClientInitialized) + return; + //Cannot send to server if not client authoritative or don't have authority. + if (!ClientAuthoritative || !base.IsOwner) + return; + + /* 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 initialized. + if (!base.IsServerInitialized) + return; + + 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; + + SendSegment(new(buffer, 0, bufferLength)); + } + //Reset client auth buffer. + _clientAuthoritativeUpdates.Reset(); + } + //Sending from server, send what's changed. + else + { + if (AnimatorUpdated(out ArraySegment updatedBytes, _forceAllOnTimed)) + SendSegment(updatedBytes); + + _forceAllOnTimed = false; + } + + //Sends segment to clients + void SendSegment(ArraySegment data) + { + foreach (NetworkConnection nc in base.Observers) + { + //If to not send to owner. + if (!_sendToOwner && nc == base.Owner) + continue; + TargetAnimatorUpdated(nc, data); + } + } + } + + + /// + /// 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(); + + /* 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.Clear(); + + /* 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.WriteUInt8Unpacked(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.WriteUInt8Unpacked(parameterIndex); + _writer.WriteSingle(next); + _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.WriteUInt8Unpacked(parameterIndex); + _writer.WriteInt32(next); + _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.WriteUInt8Unpacked(_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(Time.frameCount); + } + + /* Only iterate if the collection has values. This is to avoid some + * unnecessary caching when collection is empty. */ + if (_unsynchronizedLayerStates.Count > 0) + { + int frameCount = Time.frameCount; + List sentLayers = CollectionCaches.RetrieveList(); + //Go through each layer which needs to be synchronized. + foreach (KeyValuePair item in _unsynchronizedLayerStates) + { + /* If a frame has not passed since the state was created + * then do not send it until next tick. State changes take 1 frame + * to be processed by Unity, this check ensures that. */ + if (frameCount == item.Value.FrameCount) + continue; + + //Add to layers being sent. This is so they can be removed from the collection later. + sentLayers.Add(item.Key); + 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.WriteUInt8Unpacked(STATE); + _writer.WriteUInt8Unpacked((byte)layerIndex); + //Current hash will always be too large to compress. + _writer.WriteInt32Unpacked(stateHash); + _writer.WriteSingle(normalizedTime); + } + } + //When it's a crossfade then send crossfade data. + else + { + _writer.WriteUInt8Unpacked(CROSSFADE); + _writer.WriteUInt8Unpacked((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); + _writer.WriteSingle(sc.OffsetTime); + _writer.WriteSingle(sc.NormalizedTransitionTime); + } + } + + if (sentLayers.Count > 0) + { + for (int i = 0; i < sentLayers.Count; i++) + _unsynchronizedLayerStates.Remove(sentLayers[i]); + //Store cache. + CollectionCaches.Store(sentLayers); + } + } + + /* Layer weights. */ + for (int layerIndex = 0; layerIndex < _layerWeights.Length; layerIndex++) + { + float next = _animator.GetLayerWeight(layerIndex); + if (forceAll || _layerWeights[layerIndex] != next) + { + _writer.WriteUInt8Unpacked(LAYER_WEIGHT); + _writer.WriteUInt8Unpacked((byte)layerIndex); + _writer.WriteSingle(next); + _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.WriteUInt8Unpacked(SPEED); + _writer.WriteSingle(speedNext); + _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 (!_canSynchronizeAnimator) + return; + if (_layerWeights == null) + return; + if (updatedParameters.Count == 0) + return; + + PooledReader reader = ReaderPool.Retrieve(updatedParameters, base.NetworkManager); + + try + { + while (reader.Remaining > 0) + { + byte parameterIndex = reader.ReadUInt8Unpacked(); + //Layer weight + if (parameterIndex == LAYER_WEIGHT) + { + byte layerIndex = reader.ReadUInt8Unpacked(); + float value = reader.ReadSingle(); + _animator.SetLayerWeight((int)layerIndex, value); + } + //Speed. + else if (parameterIndex == SPEED) + { + float value = reader.ReadSingle(); + _animator.speed = value; + } + //State. + else if (parameterIndex == STATE) + { + byte layerIndex = reader.ReadUInt8Unpacked(); + //Hashes will always be too large to compress. + int hash = reader.ReadInt32Unpacked(); + float normalizedTime = reader.ReadSingle(); + //Play results. + _animator.Play(hash, layerIndex, normalizedTime); + } + //Crossfade. + else if (parameterIndex == CROSSFADE) + { + byte layerIndex = reader.ReadUInt8Unpacked(); + //Hashes will always be too large to compress. + int hash = reader.ReadInt32(); + bool useFixedTime = reader.ReadBoolean(); + //Get time values. + float durationTime = reader.ReadSingle(); + float offsetTime = reader.ReadSingle(); + float normalizedTransitionTime = reader.ReadSingle(); + //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(); + //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(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 + { + base.NetworkManager.LogWarning($"Unhandled parameter type of {acpt}."); + } + } + } + + } + catch + { + base.NetworkManager.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."); + } + finally + { + reader?.Store(); + } + } + + /// + /// 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 (!_canSynchronizeAnimator) + return false; + + AnimatorStateInfo st = _animator.GetCurrentAnimatorStateInfo(layerIndex); + stateHash = st.fullPathHash; + normalizedTime = st.normalizedTime; + + return (stateHash != 0); + } + + /// + /// Immediately sends all variables and states of layers. + /// This is a very bandwidth intensive operation. + /// + public void SendAll() + { + _forceAllOnTimed = true; + } + + #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 (!_canSynchronizeAnimator) + return; + if (_animator.HasState(layer, hash) || hash == 0) + { + _animator.Play(hash, layer, normalizedTime); + _unsynchronizedLayerStates[layer] = new(Time.frameCount); + } + } + /// + /// 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 (!_canSynchronizeAnimator) + return; + if (_animator.HasState(layer, hash) || hash == 0) + { + _animator.PlayInFixedTime(hash, layer, fixedTime); + _unsynchronizedLayerStates[layer] = new(Time.frameCount); + } + } + #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 (!_canSynchronizeAnimator) + return; + if (_animator.HasState(layer, hash) || hash == 0) + { + _animator.CrossFade(hash, normalizedTransitionDuration, layer, normalizedTimeOffset, normalizedTransitionTime); + _unsynchronizedLayerStates[layer] = new(Time.frameCount, 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 (!_canSynchronizeAnimator) + return; + if (_animator.HasState(layer, hash) || hash == 0) + { + _animator.CrossFadeInFixedTime(hash, fixedTransitionDuration, layer, fixedTimeOffset, normalizedTransitionTime); + _unsynchronizedLayerStates[layer] = new(Time.frameCount, 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 (!_canSynchronizeAnimator) + 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 (!_canSynchronizeAnimator) + return; + + bool clientAuth = ClientAuthoritative; + //If there is an owner perform checks. + if (base.Owner.IsValid) + { + //If client auth and not owner. + if (clientAuth && !base.IsOwner) + return; + } + //There is no owner. + else + { + if (!base.IsServerStarted) + return; + } + + //Update locally. + if (set) + _animator.SetTrigger(hash); + else + _animator.ResetTrigger(hash); + + /* Can send if any of the following are true: + * ClientAuth + Owner. + * ClientAuth + No Owner + IsServer + * !ClientAuth + IsServer. */ + bool canSend = (clientAuth && base.IsOwner) + || (clientAuth && !base.Owner.IsValid) + || (!clientAuth && base.IsServerStarted); + + //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(i, set)); + return; + } + } + //Fall through, hash not found. + base.NetworkManager.LogWarning($"Hash {hash} not found while trying to update a trigger."); + } + } + #endregion + + #region Remote actions. + /// + /// Called on clients to receive an animator update. + /// + /// + [TargetRpc(ValidateTarget = false)] + private void TargetAnimatorUpdated(NetworkConnection connection, ArraySegment data) + { + if (!_canSynchronizeAnimator) + return; + + //If receiver is client host then do nothing, clientHost need not process. + if (base.IsServerInitialized && connection.IsLocalClient) + return; + + bool clientAuth = ClientAuthoritative; + bool isOwner = base.IsOwner; + /* If set for client auth and owner then do not process. + * This could be the case if an update was meant to come before + * ownership gain but came out of late due to out of order when using unreliable. + * Cannot check sendToOwner given clients may not + * always be aware of owner depending on ShareIds setting. */ + if (clientAuth && isOwner) + return; + /* If not client auth and not to send to owner, and is owner + * then also return. */ + if (!clientAuth && !_sendToOwner && isOwner) + return; + + ReceivedServerData rd = new(data); + _fromServerBuffer.Enqueue(rd); + + if (_startTick == 0) + _startTick = (base.TimeManager.LocalTick + _interpolation); + } + /// + /// Called on server to receive an animator update. + /// + /// + [ServerRpc] + private void ServerAnimatorUpdated(ArraySegment data) + { + if (!_canSynchronizeAnimator) + 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); + } + #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..091698f --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e8cac635f24954048aad3a6ff9110beb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/NetworkAnimator/NetworkAnimator.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta new file mode 100644 index 0000000..4d7a787 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c69d0773e094d8442b66666dc0b49caa +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..4219b6b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1746873c72693e54fbf636563628b50f +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..652490b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs @@ -0,0 +1,143 @@ +#if UNITY_EDITOR +using FishNet.Editing; +using GameKit.Dependencies.Utilities; +using UnityEditor; +using UnityEngine; +using LayoutTools = GameKit.Dependencies.Utilities.EditorGuiLayoutTools; + + +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 _interval; + 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"); + _interval = serializedObject.FindProperty(nameof(_interval)); + _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(); + + LayoutTools.AddObjectField("Script:", MonoScript.FromMonoBehaviour((NetworkTransform)target), typeof(NetworkTransform), false, EditorLayoutEnableType.Disabled); + + bool isPro = false; + + if (isPro) + EditorGUILayout.HelpBox(EditingConstants.PRO_ASSETS_UNLOCKED_TEXT, MessageType.None); + else + EditorGUILayout.HelpBox(EditingConstants.PRO_ASSETS_LOCKED_TEXT, MessageType.Warning); + + //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++; + //Interval. + EditorGUILayout.PropertyField(_interval, new GUIContent("Send Interval")); + //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..3416de8 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3ed56c899b8ecf241a2bc3e40a2dabc1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/NetworkTransform/Editor/NetworkTransformEditor.cs + uploadId: 762203 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..84d164e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs @@ -0,0 +1,2467 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using FishNet.Managing.Timing; +using UnityEngine; +using UnityEngine.Scripting; +using static FishNet.Object.NetworkObject; + +namespace FishNet.Component.Transforming +{ + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Component/NetworkTransform")] + public sealed class NetworkTransform : NetworkBehaviour + { + #region Types. + [Serializable] + public enum ComponentConfigurationType + { + Disabled = 0, + CharacterController = 1, + Rigidbody = 2, + Rigidbody2D = 3, + } + + private struct ReceivedClientData + { + /// + /// Level of detail indexes which have data. + /// + public bool HasData; + /// + /// Most recent data. + /// + public PooledWriter Writer; + /// + /// Channel the current data arrived on. + /// + public Channel Channel; + /// + /// LocalTick of the side receiving this update, typically the server. + /// + public uint LocalTick; + + /// + /// Updates current values. + /// + /// True to set all HasData to true. + public void Update(ArraySegment data, Channel channel, bool updateHasData, uint localTick) + { + if (Writer == null) + Writer = WriterPool.Retrieve(); + + Writer.Clear(); + Writer.WriteArraySegment(data); + Channel = channel; + LocalTick = localTick; + + if (updateHasData) + HasData = true; + } + + /// + /// Will cause this data to send on the reliable channel once even if data is unchanged. + /// + public void SendReliably() + { + HasData = true; + Channel = Channel.Reliable; + } + + public void ResetState() + { + HasData = false; + WriterPool.StoreAndDefault(ref Writer); + } + } + + [Serializable] + public struct SnappedAxes + { + public bool X; + public bool Y; + public bool Z; + } + + [Flags] + private enum ChangedDelta : uint + { + Unset = 0, + PositionX = 1, + PositionY = 2, + PositionZ = 4, + Rotation = 8, + Extended = 16, + ScaleX = 32, + ScaleY = 64, + ScaleZ = 128, + Nested = 256, + All = ~0u, + } + + [Flags] + private enum ChangedFull + { + Unset = 0, + Position = 1, + Rotation = 2, + Scale = 4, + Childed = 8, + Teleport = 16, + } + + [Flags] + private enum UpdateFlagA : byte + { + Unset = 0, + X2 = 1, + X4 = 2, + Y2 = 4, + Y4 = 8, + Z2 = 16, + Z4 = 32, + Rotation = 64, + Extended = 128 + } + + [Flags] + private enum UpdateFlagB : byte + { + Unset = 0, + X2 = 1, + X4 = 2, + Y2 = 4, + Y4 = 8, + Z2 = 16, + Z4 = 32, + Child = 64, + Teleport = 128 + } + + public class GoalData : IResettable + { + public uint ReceivedTick; + public RateData Rates = new(); + public TransformData Transforms = new(); + + [Preserve] + public GoalData() { } + + public void ResetState() + { + ReceivedTick = 0; + Transforms.ResetState(); + Rates.ResetState(); + } + + public void InitializeState() { } + } + + public class RateData : IResettable + { + /// + /// 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; + /// + /// Time remaining until transform is expected to reach it's goal. + /// + internal float TimeRemaining; + + [Preserve] + public RateData() { } + + public void Update(RateData rd) + { + Update(rd.Position, rd.Rotation, rd.Scale, rd.LastUnalteredPositionRate, rd.TickSpan, rd.TimeRemaining); + } + + /// + /// Updates rates. + /// + public void Update(float position, float rotation, float scale, float unalteredPositionRate, uint tickSpan, float timeRemaining) + { + Position = position; + Rotation = rotation; + Scale = scale; + LastUnalteredPositionRate = unalteredPositionRate; + TickSpan = tickSpan; + TimeRemaining = timeRemaining; + } + + public void ResetState() + { + Position = 0f; + Rotation = 0f; + Scale = 0f; + LastUnalteredPositionRate = 0f; + TickSpan = 0; + TimeRemaining = 0f; + } + + public void InitializeState() { } + } + + public class TransformData : IResettable + { + public enum ExtrapolateState : byte + { + Disabled = 0, + Available = 1, + Active = 2 + } + + /// + /// True if default state. This becomes false during an update and true when resetting state. + /// + public bool IsDefault { get; private set; } = true; + + /// + /// Tick this data was received or created. + /// + public uint Tick; + /// + /// True if this data has already been checked for snapping. + /// Snapping calls may occur multiple times when data is received, depending why or how it came in. + /// This check prevents excessive work. + /// + public bool SnappingChecked; + /// + /// Local position in the data. + /// + public Vector3 Position; + /// + /// Local rotation in the data. + /// + public Quaternion Rotation; + /// + /// Local scale in the data. + /// + public Vector3 Scale; + /// + /// Position to extrapolate towards. + /// + public Vector3 ExtrapolatedPosition; + /// + /// Current state of extrapolation. + /// + public ExtrapolateState ExtrapolationState; + /// + /// NetworkBehaviour which is the parent of this object for Tick. + /// + public NetworkBehaviour ParentBehaviour; + + [Preserve] + public TransformData() { } + + 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) + { + IsDefault = false; + Tick = tick; + Position = position; + Rotation = rotation; + Scale = scale; + ExtrapolatedPosition = extrapolatedPosition; + ParentBehaviour = parentBehaviour; + } + + public void ResetState() + { + IsDefault = true; + Tick = 0; + SnappingChecked = false; + Position = Vector3.zero; + Rotation = Quaternion.identity; + Scale = Vector3.zero; + ExtrapolatedPosition = Vector3.zero; + ExtrapolationState = ExtrapolateState.Disabled; + ParentBehaviour = null; + } + + public void InitializeState() { } + } + #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; } + + /// + /// NetworkBehaviour this transform is a child of. + /// + public NetworkBehaviour ParentBehaviour { 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() + { + 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.IsServerInitialized) + ObserversSetSendToOwner(value); + } + + /// + /// How often in ticks to synchronize. This is default to 1 but can be set longer to send less often. This value may also be changed at runtime. Enabling Network level of detail for this NetworkTransform disables manual control of this feature as it will be handled internally. + /// + [Tooltip("How often in ticks to synchronize. This is default to 1 but can be set longer to send less often. This value may also be changed at runtime. Enabling Network level of detail for this NetworkTransform disables manual control of this feature as it will be handled internally.")] + [Range(1, byte.MaxValue)] + [SerializeField] + 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(); + + /// + /// 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(); + + /// + /// 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(); + + /// + /// 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() + { + 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; + /// + /// 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 ChangedDelta _serverChangedSinceReliable; + /// + /// 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 ReceivedClientData _authoritativeClientData = new(); + /// + /// True if subscribed to TimeManager for ticks. + /// + private bool _subscribedToTicks; + /// + /// True if subscribed to TimeManager for update. + /// + private bool _subscribedToUpdate; + /// + /// Starting interpolation on the rigidbody. + /// + private RigidbodyInterpolation? _initializedRigidbodyInterpolation; + /// + /// Starting interpolation on the rigidbody2d. + /// + private RigidbodyInterpolation2D? _initializedRigidbodyInterpolation2d; + /// + /// Last TransformData to be received from the server. + /// + private TransformData _lastReceivedServerTransformData; + /// + /// Last TransformData to be received from the server. + /// + private TransformData _lastReceivedClientTransformData; + /// + /// Last RateData to be calculated from LastReceivedTransformData. + /// + private readonly RateData _lastCalculatedRateData = new(); + /// + /// GoalDatas to move towards. + /// + private readonly Queue _goalDataQueue = new(); + /// + /// Current GoalData being used. + /// + private GoalData _currentGoalData; + /// + /// True if the transform has changed since it started. + /// + private bool _changedSinceStart; + /// + /// Number of intervals remaining before synchronization. + /// + private short _intervalsRemaining; + /// + /// Last sent transform data. + /// + private TransformData _lastSentTransformData; + /// + /// Writers for changed data. + /// + private PooledWriter _toClientChangedWriter; + /// + /// If not unset a force send will occur on or after this tick. + /// + private uint _forceSendTick = Managing.Timing.TimeManager.UNSET_TICK; + /// + /// Returns all properties as changed. + /// + private ChangedDelta _fullChanged => ChangedDelta.All; + /// + /// When true teleport will be sent with the next changed data. + /// + private bool _teleport; + /// + /// Cached transform + /// + private Transform _cachedTransform; + /// + /// Cached TimeManager reference for performance. + /// + private TimeManager _timeManager; + #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() + { + base.ResetState(true); + ResetState_OnDestroy(); + } + + public override void OnStartNetwork() + { + _cachedTransform = transform; + _timeManager = base.TimeManager; + + ChangeTickSubscription(true); + } + + public override void OnStartServer() + { + _lastReceivedClientTransformData = ObjectCaches.Retrieve(); + InitializeFields(true); + SetDefaultGoalData(); + } + + 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. + PooledWriter writer = WriterPool.Retrieve(); + SerializeChanged(_fullChanged, writer); + TargetUpdateTransform(connection, writer.GetArraySegment(), Channel.Reliable); + writer.Store(); + } + } + + public override void OnStartClient() + { + _lastReceivedServerTransformData = ObjectCaches.Retrieve(); + ChangeUpdateSubscription(subscribe: true); + ConfigureComponents(); + InitializeFields(false); + SetDefaultGoalData(); + } + + public override void OnOwnershipServer(NetworkConnection prevOwner) + { + ConfigureComponents(); + _intervalsRemaining = 0; + //Reset last tick since each client sends their own ticks. + _lastServerRpcTick = 0; + + TryClearGoalDatas_OwnershipChange(prevOwner, true); + } + + public override void OnOwnershipClient(NetworkConnection prevOwner) + { + ConfigureComponents(); + _intervalsRemaining = 0; + + //Not new owner. + if (!base.IsOwner) + { + /* 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(); + } + + TryClearGoalDatas_OwnershipChange(prevOwner, false); + } + + public override void OnStopNetwork() + { + ResetState(); + ChangeUpdateSubscription(subscribe: false); + } + + /// + /// Tries to clear the GoalDatas queue during an ownership change. + /// + private void TryClearGoalDatas_OwnershipChange(NetworkConnection prevOwner, bool asServer) + { + if (_clientAuthoritative) + { + //If not server + if (!asServer) + { + //If owner now then clear as the owner controls the object now and shouldnt use past datas. + if (base.IsOwner) + _goalDataQueue.Clear(); + } + //as Server. + else + { + //If new owner is valid then clear to allow new owner datas. + if (base.Owner.IsValid) + _goalDataQueue.Clear(); + } + } + /* Server authoritative never clears because the + * clients do not control this object thus should always + * follow the queue. */ + } + + private void TimeManager_OnUpdate() + { + MoveToTarget(Time.deltaTime); + } + + /// + /// Adds collections required. + /// + private void InitializeFields(bool asServer) + { + bool asClientAndNotHost = (!asServer && !base.IsServerStarted); + + /* Even though these collections are nullified on clean up + * they could still exist on the reinitialization for clientHost if + * an object is despawned to a pool then very quickly respawned + * before the clientHost side has not processed the despawn yet. + * Because of this check count rather than null. */ + if (asClientAndNotHost || asServer) + { + //Prefer to reset existing. + if (_lastSentTransformData != null) + _lastSentTransformData.ResetState(); + else + _lastSentTransformData = ResettableObjectCaches.Retrieve(); + } + + if (asServer) + { + if (_toClientChangedWriter != null) + _toClientChangedWriter.Clear(); + else + _toClientChangedWriter = WriterPool.Retrieve(); + } + } + + /// + /// Configures components automatically. + /// + + /// + /// Configures components automatically. + /// + private void ConfigureComponents() + { + //Disabled. + if (_componentConfiguration == ComponentConfigurationType.Disabled) + return; + + //RB. + if (_componentConfiguration == ComponentConfigurationType.Rigidbody) + { + if (TryGetComponent(out Rigidbody c)) + { + //If first time set starting interpolation. + if (_initializedRigidbodyInterpolation == null) + _initializedRigidbodyInterpolation = c.interpolation; + + bool isKinematic = CanMakeKinematic(); + c.isKinematic = isKinematic; + + if (isKinematic) + c.interpolation = RigidbodyInterpolation.None; + else + c.interpolation = _initializedRigidbodyInterpolation.Value; + } + } + //RB2D + else if (_componentConfiguration == ComponentConfigurationType.Rigidbody2D) + { + if (TryGetComponent(out Rigidbody2D c)) + { + //If first time set starting interpolation. + if (_initializedRigidbodyInterpolation2d == null) + _initializedRigidbodyInterpolation2d = c.interpolation; + + bool isKinematic = CanMakeKinematic(); + c.isKinematic = isKinematic; + c.simulated = !isKinematic; + + if (isKinematic) + c.interpolation = RigidbodyInterpolation2D.None; + else + c.interpolation = _initializedRigidbodyInterpolation2d.Value; + } + } + //CC + else if (_componentConfiguration == ComponentConfigurationType.CharacterController) + { + if (TryGetComponent(out CharacterController c)) + { + //Client auth. + if (_clientAuthoritative) + { + c.enabled = base.IsController; + } + //Server auth. + else + { + //Not CSP. + if (_sendToOwner) + c.enabled = base.IsServerInitialized; + //Most likely CSP. + else + c.enabled = (base.IsServerInitialized || base.IsOwner); + } + } + } + + bool CanMakeKinematic() + { + bool isServerStarted = base.IsServerStarted; + + //When not client auth, kinematic is always true if not server. + if (!_clientAuthoritative) + return !isServerStarted; + + /* If here then is client-auth. */ + + //Owner shouldn't be kinematic as they are controller. + if (base.IsOwner) + return false; + + //Is server, and there is no owner. + if (isServerStarted && !base.Owner.IsActive) + return false; + + return true; + } + } + + /// + /// Called when a tick occurs. + /// + private void TimeManager_OnPostTick() + { + //If to force send via tick delay do so and reset force send tick. + if (_forceSendTick != Managing.Timing.TimeManager.UNSET_TICK && _timeManager.LocalTick > _forceSendTick) + { + _forceSendTick = Managing.Timing.TimeManager.UNSET_TICK; + ForceSend(); + } + + UpdateParentBehaviour(); + + /* Intervals remaining is only used when the interval value + * is set higher than 1. An interval of 1 indicates to send + * every tick. Only check to wait more ticks if interval + * is larger than 1. */ + if (_interval > 1) + { + /* If intervalsRemaining is unset then that means the transform + * did not change last tick. See if transform changed and if so then + * update remaining to _interval. */ + if (_intervalsRemaining == -1) + { + //Transform didn't change, no reason to start remaining. + if (!_cachedTransform.hasChanged) + return; + + _intervalsRemaining = _interval; + } + + //If here then intervalsRemaining can be deducted. + _intervalsRemaining--; + //Interval not met yet. + if (_intervalsRemaining > 0) + return; + //Intervals remainin is met. Reset to -1 to await new change. + else + _intervalsRemaining = -1; + } + + bool isServerInitialized = base.IsServerInitialized; + bool isClientInitialized = base.IsClientInitialized; + + if (isServerInitialized) + { + /* If client is not initialized then + * call a move to targe ton post tick to ensure + * anything with instant rates gets moved. */ + if (!isClientInitialized) + MoveToTarget((float)_timeManager.TickDelta); + // + SendToClients(); + } + + if (isClientInitialized) + SendToServer(_lastSentTransformData); + } + + /// + /// Tries to subscribe to TimeManager ticks. + /// + private void ChangeTickSubscription(bool subscribe) + { + if (subscribe == _subscribedToTicks || base.NetworkManager == null) + return; + + _subscribedToTicks = subscribe; + if (subscribe) + base.NetworkManager.TimeManager.OnPostTick += TimeManager_OnPostTick; + else + base.NetworkManager.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + + private void ChangeUpdateSubscription(bool subscribe) + { + if (subscribe == _subscribedToUpdate || _timeManager == null) + return; + + _subscribedToUpdate = subscribe; + if (subscribe) + _timeManager.OnUpdate += TimeManager_OnUpdate; + else + _timeManager.OnUpdate -= TimeManager_OnUpdate; + } + + /// + /// Returns if controlling logic can be run. This may be the server when there is no owner, even if client authoritative, and more. + /// + /// + private bool CanControl() + { + //Client auth. + if (_clientAuthoritative) + { + return base.IsController; + } + //Server auth. + else + { + if (base.IsServerInitialized) + return true; + } + + //Fall through. + return false; + } + + /// + /// When called by the controller of this object the next changed data will be teleported to by spectators. + /// + public void Teleport() + { + if (CanControl()) + _teleport = true; + } + + /// + /// 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 after a number of ticks. + /// + public void ForceSend(uint ticks) + { + /* If there is a pending delayed force send then queue it + * immediately and set a new delay tick. */ + if (_forceSendTick != Managing.Timing.TimeManager.UNSET_TICK) + ForceSend(); + _forceSendTick = _timeManager.LocalTick + ticks; + } + + /// + /// Resets last sent information to force a resend of current values. + /// + public void ForceSend() + { + _lastSentTransformData.ResetState(); + if (_authoritativeClientData.Writer != null) + _authoritativeClientData.SendReliably(); + } + + /// + /// Updates the interval value over the network. + /// + /// New interval. + public void SetInterval(byte value) + { + bool canSet = (base.IsServerInitialized && !_clientAuthoritative) || (base.IsServerInitialized && _clientAuthoritative && !base.Owner.IsValid) || (_clientAuthoritative && base.IsOwner); + + if (!canSet) + return; + + if (base.IsServerInitialized) + 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(BufferLast = true, RunLocally = true)] + private void ObserversSetInterval(byte value) + { + SetIntervalInternal(value); + } + + /// + /// Creates goal data using current position. + /// + private void SetDefaultGoalData() + { + Transform t = _cachedTransform; + NetworkBehaviour parentBehaviour = null; + //If there is a parent try to output the behaviour on it. + if (_synchronizeParent) + { + if (base.NetworkObject.CurrentParentNetworkBehaviour != null) + { + t.parent.TryGetComponent(out parentBehaviour); + if (parentBehaviour == null) + { + LogInvalidParent(); + } + else + { + _parentTransform = t.parent; + ParentBehaviour = parentBehaviour; + } + } + } + + _teleport = false; + SetLastReceived(_lastReceivedServerTransformData); + SetLastReceived(_lastReceivedClientTransformData); + //SetInstantRates(_currentGoalData.Rates, 0, -1f); + + void SetLastReceived(TransformData td) + { + //Could be null if not initialized due to server or client side not being used. + if (td == null) + return; + td.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, parentBehaviour); + } + } + + /// + /// Prints an invalid parent debug. + /// + private void LogInvalidParent() + { + base.NetworkManager.LogWarning($"{gameObject.name} [Id {base.ObjectId}] is childed but the parent {_cachedTransform.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 childed. 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.Skip(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 = _cachedTransform; + /* 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 childed. Unsual transform scale shouldn't affect rotation. */ + writer.WriteQuaternion(t.localRotation, _packing.Rotation); + } + } + + /* If there is a teleport pending then apply + * extended flag since thats where teleport resides. */ + bool teleport = _teleport; + if (teleport) + changed |= ChangedDelta.Extended; + + if (ChangedContains(changed, ChangedDelta.Extended)) + { + AutoPackType localPacking = packing.Scale; + flagsA |= UpdateFlagA.Extended; + int startIndexB = writer.Position; + writer.Skip(1); + + /* Redundant to do the teleport check here since it was done + * just above, but for code consistency the teleport updateflag + * is set within this conditional with rest of the extended + * datas. */ + if (teleport) + { + flagsB |= UpdateFlagB.Teleport; + _teleport = false; + } + + /* 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); + } + } + } + + //Childed. + if (ChangedContains(changed, ChangedDelta.Nested) && ParentBehaviour != null) + { + flagsB |= UpdateFlagB.Child; + writer.WriteNetworkBehaviour(ParentBehaviour); + } + + writer.InsertUInt8Unpacked((byte)flagsB, startIndexB); + } + + //Insert flags. + writer.InsertUInt8Unpacked((byte)flagsA, startIndexA); + + bool ChangedContains(ChangedDelta whole, ChangedDelta part) + { + return (whole & part) == part; + } + } + + /// + /// Deerializes a received packet. + /// + private void DeserializePacket(ArraySegment data, TransformData prevTransformData, TransformData nextTransformData, ref ChangedFull changedFull) + { + PooledReader reader = ReaderPool.Retrieve(data, base.NetworkManager); + UpdateFlagA flagsA = (UpdateFlagA)reader.ReadUInt8Unpacked(); + + int readerRemaining; + readerRemaining = reader.Remaining; + //X + if (UpdateFlagAContains(flagsA, UpdateFlagA.X2)) + nextTransformData.Position.x = reader.ReadInt16() / 100f; + else if (UpdateFlagAContains(flagsA, UpdateFlagA.X4)) + nextTransformData.Position.x = reader.ReadSingle(); + else + nextTransformData.Position.x = prevTransformData.Position.x; + //Y + if (UpdateFlagAContains(flagsA, UpdateFlagA.Y2)) + nextTransformData.Position.y = reader.ReadInt16() / 100f; + else if (UpdateFlagAContains(flagsA, UpdateFlagA.Y4)) + nextTransformData.Position.y = reader.ReadSingle(); + else + nextTransformData.Position.y = prevTransformData.Position.y; + //Z + if (UpdateFlagAContains(flagsA, UpdateFlagA.Z2)) + nextTransformData.Position.z = reader.ReadInt16() / 100f; + else if (UpdateFlagAContains(flagsA, UpdateFlagA.Z4)) + nextTransformData.Position.z = reader.ReadSingle(); + else + nextTransformData.Position.z = prevTransformData.Position.z; + //If remaining has changed then a position was read. + if (readerRemaining != reader.Remaining) + changedFull |= ChangedFull.Position; + + //Rotation. + if (UpdateFlagAContains(flagsA, UpdateFlagA.Rotation)) + { + //Always use _packing value even if childed. + nextTransformData.Rotation = reader.ReadQuaternion(_packing.Rotation); + changedFull |= ChangedFull.Rotation; + } + else + { + nextTransformData.Rotation = prevTransformData.Rotation; + } + + //Extended settings. + if (UpdateFlagAContains(flagsA, UpdateFlagA.Extended)) + { + UpdateFlagB flagsB = (UpdateFlagB)reader.ReadUInt8Unpacked(); + readerRemaining = reader.Remaining; + + //X + if (UpdateFlagBContains(flagsB, UpdateFlagB.X2)) + nextTransformData.Scale.x = reader.ReadInt16() / 100f; + else if (UpdateFlagBContains(flagsB, UpdateFlagB.X4)) + nextTransformData.Scale.x = reader.ReadSingle(); + else + nextTransformData.Scale.x = prevTransformData.Scale.x; + //Y + if (UpdateFlagBContains(flagsB, UpdateFlagB.Y2)) + nextTransformData.Scale.y = reader.ReadInt16() / 100f; + else if (UpdateFlagBContains(flagsB, UpdateFlagB.Y4)) + nextTransformData.Scale.y = reader.ReadSingle(); + else + nextTransformData.Scale.y = prevTransformData.Scale.y; + //X + if (UpdateFlagBContains(flagsB, UpdateFlagB.Z2)) + nextTransformData.Scale.z = reader.ReadInt16() / 100f; + else if (UpdateFlagBContains(flagsB, UpdateFlagB.Z4)) + nextTransformData.Scale.z = reader.ReadSingle(); + else + nextTransformData.Scale.z = prevTransformData.Scale.z; + + if (reader.Remaining != readerRemaining) + changedFull |= ChangedFull.Scale; + else + nextTransformData.Scale = prevTransformData.Scale; + + if (UpdateFlagBContains(flagsB, UpdateFlagB.Teleport)) + changedFull |= ChangedFull.Teleport; + + if (UpdateFlagBContains(flagsB, UpdateFlagB.Child)) + { + nextTransformData.ParentBehaviour = reader.ReadNetworkBehaviour(); + changedFull |= ChangedFull.Childed; + } + 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; + } + + reader.Store(); + } + + /// + /// Updates the ParentBehaviour field when able to. + /// + private void UpdateParentBehaviour() + { + if (!_synchronizeParent) + return; + //No permissions to set. + if (!CanControl()) + return; + Transform parent = _cachedTransform.parent; + + //No parent. + if (parent == null) + { + /* Check for being set without using nob.SetParent. + * Only check if was previously set inside this component; otherwise + * this would spam anytime the parent was null. */ + if (base.NetworkObject.RuntimeParentNetworkBehaviour != null) + base.NetworkManager.LogWarning($"{gameObject.name} parent object was removed without calling UnsetParent. Use networkObject.UnsetParent() to remove a NetworkObject from it's parent. This is being made a requirement in Fish-Networking v4."); + + ParentBehaviour = null; + _parentTransform = null; + } + //Has a parent, see if eligible. + else + { + //No change. + if (_parentTransform == parent) + return; + + _parentTransform = parent; + NetworkBehaviour outParentBehaviour; + + if (!parent.TryGetComponent(out outParentBehaviour)) + { + ParentBehaviour = null; + LogInvalidParent(); + } + else + { + ParentBehaviour = outParentBehaviour; + //Check for being set without using nob.SetParent. + if (base.NetworkObject.CurrentParentNetworkBehaviour != ParentBehaviour) + base.NetworkManager.LogWarning($"{gameObject.name} parent was set without calling SetParent. Use networkObject.SetParent(obj) to assign a NetworkObject a new parent. This is being made a requirement in Fish-Networking v4."); + } + } + } + + /// + /// Sets the transforms parent if it's changed. + /// + /// + private void SetParent(NetworkBehaviour parent, RateData rd) + { + Transform target = (parent == null) ? null : parent.transform; + Transform t = _cachedTransform; + //Unchanged. + if (target == t.parent) + return; + + Vector3 scale = t.localScale; + //Set parent after scale is cached so scale can be maintained after changing parent. + if (target != null) + base.NetworkObject.SetParent(parent); + else + base.NetworkObject.UnsetParent(); + + t.localScale = scale; + + /* Set ratedata to immediate so there's no blending between transform values when + * getting on or off platforms. */ + if (rd != null) + rd.Update(-1f, -1f, -1f, rd.LastUnalteredPositionRate, rd.TickSpan, rd.TimeRemaining); + } + + /// + /// Moves to a GoalData. Automatically determins if to use data from server or client. + /// + private void MoveToTarget(float delta) + { + if (_currentGoalData == null) + return; + + //Cannot move if neither is active. + if (!base.IsServerInitialized && !base.IsClientInitialized) + 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.IsServerInitialized) + return; + + /* 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; + + //Set parent. + if (_synchronizeParent) + SetParent(td.ParentBehaviour, rd); + + float multiplier = 1f; + int queueCount = _goalDataQueue.Count; + //Increase move rate slightly if over queue count. + if (queueCount > (_interpolation + 1)) + multiplier += 0.05f; + + //Rate to update. Changes per property. + float rate; + Transform t = _cachedTransform; + + //Snap any bits of the transform that should be. + SnapProperties(td); + + //Position. + if (_synchronizePosition) + { + rate = rd.Position; + Vector3 posGoal = (td.ExtrapolationState == TransformData.ExtrapolateState.Active && !_lastReceiveReliable) ? td.ExtrapolatedPosition : td.Position; + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (rate == -1f) + t.localPosition = td.Position; + else + t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, rate * delta * multiplier); + } + + //Rotation. + if (_synchronizeRotation) + { + rate = rd.Rotation; + // ReSharper disable once CompareOfFloatsByEqualityOperator + if (rate == -1f) + t.localRotation = td.Rotation; + else + t.localRotation = Quaternion.RotateTowards(t.localRotation, td.Rotation, rate * delta); + } + + //Scale. + if (_synchronizeScale) + { + rate = rd.Scale; + // ReSharper disable once CompareOfFloatsByEqualityOperator + 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) + { + 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)) + _currentGoalData = null; + OnInterpolationComplete?.Invoke(); + + } + } + } + + /// + /// Sends transform data to clients if needed. + /// + private void SendToClients() + { + //True if clientAuthoritative and there is an owner. + bool clientAuthoritativeWithOwner = (_clientAuthoritative && base.Owner.IsValid); + //Channel to send rpc on. + Channel channel = Channel.Unreliable; + /* If relaying from client and owner isnt clientHost. + * If owner is clientHost just send current server values. */ + if (clientAuthoritativeWithOwner && !base.Owner.IsLocalClient) + { + /* If there is not new data yet and the last received was not reliable + * then a packet maybe did not arrive when expected. See if we need + * to force a reliable with the last data based on ticks passed since + * last update.*/ + if (!_authoritativeClientData.HasData && _authoritativeClientData.Channel != Channel.Reliable && _authoritativeClientData.Writer != null) + { + /* If ticks have passed beyond interpolation then force + * to send reliably. */ + uint maxPassedTicks = (uint)(1 + _interpolation + _extrapolation); + uint localTick = _timeManager.LocalTick; + if ((localTick - _authoritativeClientData.LocalTick) > maxPassedTicks) + _authoritativeClientData.SendReliably(); + //Not enough time to send reliably, just don't need update. + else + return; + } + + if (_authoritativeClientData.HasData) + { + _changedSinceStart = true; + //Resend data from clients. + ObserversUpdateClientAuthoritativeTransform(_authoritativeClientData.Writer.GetArraySegment(), _authoritativeClientData.Channel); + //Now being sent data can unset. + _authoritativeClientData.HasData = false; + } + } + //Sending server transform state. + else + { + PooledWriter writer = _toClientChangedWriter; + + TransformData lastSentData = _lastSentTransformData; + ChangedDelta changed = GetChanged(lastSentData); + + //If no change. + if (changed == ChangedDelta.Unset) + { + //No changes since last reliable; transform is up to date. + if (_serverChangedSinceReliable == ChangedDelta.Unset) + return; + + _serverChangedSinceReliable = ChangedDelta.Unset; + writer = _toClientChangedWriter; + /* If here then current is unset but last was not. + * Send last as reliable so clients have the latest sent through. */ + channel = Channel.Reliable; + } + //There is change. + else + { + //Since this is writing new data, reset the writer. + writer.Clear(); + + _serverChangedSinceReliable |= changed; + + _changedSinceStart = true; + Transform t = _cachedTransform; + /* 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); + } + + ObserversUpdateClientAuthoritativeTransform(writer.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.IsServerInitialized) + 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 = _cachedTransform; + lastSentTransformData.Update(0, t.localPosition, t.localRotation, t.localScale, t.localPosition, ParentBehaviour); + + //Send latest. + PooledWriter writer = WriterPool.Retrieve(); + SerializeChanged(changed, writer); + ServerUpdateTransform(writer.GetArraySegment(), channel); + + writer.Store(); + } + + #region GetChanged. + /// + /// Returns if the transform differs from td. + /// + private bool HasChanged(TransformData td) + { + Transform t = _cachedTransform; + bool changed = (td.Position != t.localPosition || td.Rotation != t.localRotation || td.Scale != t.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.Childed; + // } + + // return hasChanged; + //} + /// + /// Gets transform values that have changed against goalData. + /// + private ChangedDelta GetChanged(TransformData transformData) + { + //If default return full changed. + if (transformData == null || transformData.IsDefault) + return _fullChanged; + + /* 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(transformData.Position, transformData.Rotation, transformData.Scale, transformData.ParentBehaviour); + } + + /// + /// Gets transform values that have changed against specified proprties. + /// + private ChangedDelta GetChanged(Vector3 lastPosition, Quaternion lastRotation, Vector3 lastScale, NetworkBehaviour lastParentBehaviour) + { + ChangedDelta changed = ChangedDelta.Unset; + Transform t = _cachedTransform; + + Vector3 position = t.localPosition; + if (Mathf.Abs(position.x - lastPosition.x) >= 0.001f) + changed |= ChangedDelta.PositionX; + if (Mathf.Abs(position.y - lastPosition.y) >= 0.001f) + changed |= ChangedDelta.PositionY; + if (Mathf.Abs(position.z - lastPosition.z) >= 0.001f) + changed |= ChangedDelta.PositionZ; + + Quaternion rotation = t.localRotation; + if (!rotation.Matches(lastRotation, true)) + changed |= ChangedDelta.Rotation; + + ChangedDelta startChanged; + startChanged = changed; + + Vector3 scale = t.localScale; + if (Mathf.Abs(scale.x - lastScale.x) >= 0.001f) + changed |= ChangedDelta.ScaleX; + if (Mathf.Abs(scale.y - lastScale.y) >= 0.001f) + changed |= ChangedDelta.ScaleY; + if (Mathf.Abs(scale.z - lastScale.z) >= 0.001f) + changed |= ChangedDelta.ScaleZ; + + if (changed != ChangedDelta.Unset && ParentBehaviour != null) + changed |= ChangedDelta.Nested; + + //If added scale or childed 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.SnappingChecked) + return; + + transformData.SnappingChecked = true; + Transform t = _cachedTransform; + + //Position. + if (_synchronizePosition) + { + Vector3 startPosition = t.localPosition; + 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, uint tickDifference, float timeRemaining) + { + //Was default to 1 tickDiff and -1 time remaining. + rd.Update(-1f, -1f, -1f, -1f, tickDifference, timeRemaining); + } + + /// + /// Sets move rates which will occur over time. + /// + private void SetCalculatedRates(TransformData prevTd, RateData prevRd, 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; + } + + float timePassed; + uint tickDifference = GetTickDifference(prevTd, nextGd, 1, out timePassed); + + //Distance between properties. + float distance; + float positionRate = 0f; + float rotationRate = 0f; + float scaleRate = 0f; + + RateData rd = nextGd.Rates; + + //Quick exit/check for teleport. + if (ChangedFullContains(changedFull, ChangedFull.Teleport)) + { + SetInstantRates(rd, tickDifference, timePassed); + return; + } + + //Correction to apply towards rates when a rate change is detected as abnormal. + float abnormalCorrection = 1f; + 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) + { + //Over threshold. + if (distance >= _teleportThreshold) + { + SetInstantRates(rd, tickDifference, timePassed); + return; + } + } + + //Check to teleport only position due to low distance. + if (LowDistance(distance, false)) + { + unalteredPositionRate = -1f; + positionRate = -1f; + } + else + { + //Check position rates now. + //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; + } + /* 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); + if (LowDistance(distance, true)) + { + rotationRate = -1f; + } + else + { + 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); + if (LowDistance(distance, false)) + { + scaleRate = -1f; + } + else + { + scaleRate = (distance / timePassed) * abnormalCorrection; + if (scaleRate <= 0f) + scaleRate = -1f; + } + } + + rd.Update(positionRate, rotationRate, scaleRate, unalteredPositionRate, tickDifference, timePassed); + + //Returns if whole contains part. + bool ChangedFullContains(ChangedFull whole, ChangedFull part) + { + return (whole & part) == part; + } + + /* Returns if the provided distance is extremely small. + * This is used to decide if a property should be teleported. + * When distances are exceptionally small smoothing rate + * calculations may result as an invalid value. */ + bool LowDistance(float dist, bool rotation) + { + if (rotation) + return (dist < 1f); + else + return (dist < 0.0001f); + } + } + + /// + /// Gets the tick difference between two GoalDatas. + /// + private uint GetTickDifference(TransformData prevTd, GoalData nextGd, uint minimum, out float timePassed) + { + TransformData nextTd = nextGd.Transforms; + + uint lastTick = prevTd.Tick; + /* Ticks passed between datas. If 0 then the last data + * was either not set or reliable, in which case the tick + * difference should be considered 1. */ + if (lastTick == 0) + lastTick = (nextTd.Tick - _interval); + + long tickDifference = (nextTd.Tick - lastTick); + if (tickDifference < minimum) + tickDifference = minimum; + + timePassed = (float)base.NetworkManager.TimeManager.TicksToTime((uint)tickDifference); + return (uint)tickDifference; + } + #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.IsServerInitialized && conn.IsLocalClient) + return; +#endif + /* Zero data was sent, this should not be possible. + * This is a patch to a NetworkLOD bug until it can + * be resolved properly. */ + if (data.Count == 0) + return; + + DataReceived(data, channel, false); + } + + /// + /// Updates clients with transform data. + /// + [ObserversRpc] + private void ObserversUpdateClientAuthoritativeTransform(ArraySegment data, Channel channel) + { + if (!_clientAuthoritative && base.IsOwner && !_sendToOwner) + return; + if (_clientAuthoritative && base.IsOwner) + return; + if (base.IsServerInitialized) + return; + //Not new data. + uint lastPacketTick = _timeManager.LastPacketTick.LastRemoteTick; + if (lastPacketTick <= _lastObserversRpcTick) + return; + + _lastObserversRpcTick = lastPacketTick; + DataReceived(data, channel, false); + } + + /// + /// Updates the transform on the server. + /// + [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; + } + + TimeManager tm = base.TimeManager; + //Not new data. + uint lastPacketTick = tm.LastPacketTick.LastRemoteTick; + if (lastPacketTick <= _lastServerRpcTick) + return; + _lastServerRpcTick = lastPacketTick; + + _authoritativeClientData.Update(data, channel, updateHasData: true, tm.LocalTick); + DataReceived(data, channel, true); + } + + /// + /// Processes received data for lcients and server. + /// + private void DataReceived(ArraySegment data, Channel channel, bool asServer) + { + if (base.IsDeinitializing) + return; + + TransformData prevTd = (asServer) ? _lastReceivedClientTransformData : _lastReceivedServerTransformData; + RateData prevRd = _lastCalculatedRateData; + + ChangedFull changedFull = ChangedFull.Unset; + GoalData nextGd = ResettableObjectCaches.Retrieve(); + 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.IsClientStarted) + { + uint tickDifference = GetTickDifference(prevTd, nextGd, 1, out float timePassed); + SetInstantRates(nextGd.Rates, tickDifference, timePassed); + } + //Otherwise use timed. + else + { + SetCalculatedRates(prevTd, prevRd, nextGd, changedFull, hasChanged, channel); + } + + _lastReceiveReliable = (channel == Channel.Reliable); + /* If channel is reliable then this is a settled packet. + * Set tick to UNSET. When this occurs time calculations + * assume only 1 tick has passed. */ + if (channel == Channel.Reliable) + nextTd.Tick = Managing.Timing.TimeManager.UNSET_TICK; + + prevTd.Update(nextTd); + prevRd.Update(nextGd.Rates); + + nextGd.ReceivedTick = _timeManager.LocalTick; + + bool currentDataNull = (_currentGoalData == null); + /* 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 (!currentDataNull && _currentGoalData.Transforms.ExtrapolationState == TransformData.ExtrapolateState.Active) + { + 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 (currentDataNull && _goalDataQueue.Count >= _interpolation || channel == Channel.Reliable) + { + 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(); + ResettableObjectCaches.Store(tmpGd); + } + + //Snap to the next data to fix any smoothing timings. + SetCurrentGoalData(_goalDataQueue.Dequeue()); + SetInstantRates(_currentGoalData!.Rates, 1, -1f); + SnapProperties(_currentGoalData.Transforms, true); + } + } + + /// + /// Sets CurrentGoalData value. + /// + private void SetCurrentGoalData(GoalData data) + { + if (_currentGoalData != null) + ResettableObjectCaches.Store(_currentGoalData); + + _currentGoalData = data; + OnNextGoal?.Invoke(data); + } + + /// + /// Updates a TransformData from packetData. + /// + private void UpdateTransformData(ArraySegment packetData, TransformData prevTransformData, TransformData nextTransformData, ref ChangedFull changedFull) + { + DeserializePacket(packetData, prevTransformData, nextTransformData, ref changedFull); + nextTransformData.Tick = _timeManager.LastPacketTick.LastRemoteTick; + } + + /// + /// Configures this NetworkTransform for CSP. + /// + internal void ConfigureForPrediction(PredictionType predictionType) + { + _clientAuthoritative = false; + _sendToOwner = false; + + //Do not try to change component configuration if its already specified. + if (_componentConfiguration != ComponentConfigurationType.Disabled) + { + if (predictionType == PredictionType.Rigidbody) + _componentConfiguration = ComponentConfigurationType.Rigidbody; + else if (predictionType == PredictionType.Rigidbody2D) + _componentConfiguration = ComponentConfigurationType.Rigidbody2D; + else if (predictionType == PredictionType.Other) + /* 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) + { + //If sending from the server. + if (base.IsServerInitialized) + { + //If no owner, or not client auth. + if (base.IsController || !_clientAuthoritative) + ObserversSetSynchronizedProperties(value); + else + return; + } + //Sending from client. + else if (_clientAuthoritative && base.IsOwner) + { + ServerSetSynchronizedProperties(value); + } + //Cannot change. + else + { + return; + } + + //Update locally. + SetSynchronizedPropertiesInternal(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); + //Send to observers. + ObserversSetSynchronizedProperties(value); + } + + /// + /// Sets synchronized values based on value. + /// + [ObserversRpc(BufferLast = true, ExcludeServer = true)] + private void ObserversSetSynchronizedProperties(SynchronizedProperty value) + { + 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; + } + } + + /// + /// Deinitializes this component. + /// + private void ResetState() + { + _teleport = false; + ChangeTickSubscription(false); + /* Reset server and client side since this is called from + * OnStopNetwork. */ + + _lastObserversRpcTick = TimeManager.UNSET_TICK; + _authoritativeClientData.ResetState(); + + WriterPool.StoreAndDefault(ref _toClientChangedWriter); + + ObjectCaches.StoreAndDefault(ref _authoritativeClientData.HasData); + ObjectCaches.StoreAndDefault(ref _serverChangedSinceReliable); + + ResettableObjectCaches.StoreAndDefault(ref _lastReceivedClientTransformData); + ResettableObjectCaches.StoreAndDefault(ref _lastReceivedServerTransformData); + //Goaldatas. Would only exist if client or clientHost. + while (_goalDataQueue.Count > 0) + ResettableObjectCaches.Store(_goalDataQueue.Dequeue()); + + if (_lastSentTransformData != null) + _lastSentTransformData.ResetState(); + ResettableObjectCaches.StoreAndDefault(ref _currentGoalData); + } + + /// + /// Deinitializes this component for OnDestroy. + /// + private void ResetState_OnDestroy() + { + ResettableObjectCaches.StoreAndDefault(ref _lastSentTransformData); + WriterPool.StoreAndDefault(ref _toClientChangedWriter); + } + } +} \ 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..50555f3 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a2836e36774ca1c4bbbee976e17b649c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/NetworkTransform/NetworkTransform.cs + uploadId: 762203 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..578bea9 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs @@ -0,0 +1,12 @@ +namespace FishNet.Component.Transforming +{ + [System.Flags] + 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..e37fc8b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3e6005ee9abfdd542ad27023114bbe04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/NetworkTransform/SynchronizedProperty.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction.meta new file mode 100644 index 0000000..a2fc2cf --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a68eb7a82e2c73847beb5b90cd65acc1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs new file mode 100644 index 0000000..70711fe --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs @@ -0,0 +1,382 @@ +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using GameKit.Dependencies.Utilities.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using UnityEngine; +using TimeManagerCls = FishNet.Managing.Timing.TimeManager; + +namespace FishNet.Component.Prediction +{ + public abstract class NetworkCollider : NetworkBehaviour + { + #region Types. + private struct CollisionData + { + /// + /// Tick when entering collision. + /// + public uint EnterTick; + /// + /// Tick when exiting collision. + /// + public uint ExitTick; + + public CollisionData(uint enterTick) : this() + { + EnterTick = enterTick; + ExitTick = FishNet.Managing.Timing.TimeManager.UNSET_TICK; + } + + public CollisionData(uint enterTick, uint exitTick) : this() + { + EnterTick = enterTick; + ExitTick = exitTick; + } + } + #endregion + + /// + /// Called when another collider enters this collider. + /// + public event Action OnEnter; + /// + /// Called when another collider stays in this collider. + /// + public event Action OnStay; + /// + /// Called when another collider exits this collider. + /// + public event Action OnExit; + /// + /// True to run collisions for colliders which are triggers, false to run collisions for colliders which are not triggers. + /// + [HideInInspector] + protected bool IsTrigger; + /// + /// Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient. + /// + [Tooltip("Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient.")] + [SerializeField] + private ushort _maximumSimultaneousHits = 16; + /// + /// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough. + /// + [Tooltip("Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.")] + [Range(0f, 100f)] + [SerializeField] + private float _additionalSize = 0.1f; + /// + /// Layers to trace on. This is used when value is not nothing. + /// + [Tooltip("Layers to trace on. This is used when value is not nothing.")] + [SerializeField] + private LayerMask _layers = (LayerMask)0; + + /// + /// The colliders on this object. + /// + private Collider[] _colliders; + /// + /// The hits from the last check. + /// + private Collider[] _hits; + // /// + // /// The history of collider data. + // /// + // private ResettableRingBuffer _colliderDataHistory; + private Dictionary _enteredColliders; + + /// + /// True if colliders have been searched for at least once. + /// We cannot check the null state on _colliders because Unity has a habit of initializing collections on it's own. + /// + private bool _collidersFound; + /// + /// Last layer of the gameObject. + /// + private int _lastGameObjectLayer = -1; + /// + /// Interactable layers for the layer of this gameObject. + /// + private int _interactableLayers; + + protected virtual void Awake() + { + //_colliderDataHistory = ResettableCollectionCaches.RetrieveRingBuffer(); + //_colliderDataHistory = new(); + _enteredColliders = CollectionCaches.RetrieveDictionary(); + _hits = CollectionCaches.RetrieveArray(); + if (_hits.Length < _maximumSimultaneousHits) + _hits = new Collider[_maximumSimultaneousHits]; + } + + private void OnDestroy() + { + CollectionCaches.StoreAndDefault(ref _enteredColliders); + CollectionCaches.StoreAndDefault(ref _hits, _hits.Length); + } + + public override void OnStartNetwork() + { + FindColliders(); + + //Initialize the ringbuffer. Server only needs 1 tick worth of history. + // uint historyTicks = (base.IsServerStarted) ? 1 : TimeManager.TimeToTicks(_historyDuration); + //_colliderDataHistory.Initialize((int)historyTicks); + + //Events needed by server and client. + TimeManager.OnPrePhysicsSimulation += TimeManager_OnPostPhysicsSimulation; + } + + public override void OnStartClient() + { + //Events only needed by the client. + PredictionManager.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay; + PredictionManager.OnPostReconcileSyncTransforms += PredictionManagerOnPreReconcile; + } + + private void PredictionManagerOnPreReconcile(uint clientTick, uint serverTick) + { + if (_enteredColliders.Count > 0) + { + List entriesToRemove = CollectionCaches.RetrieveList(); + + foreach (KeyValuePair kvp in _enteredColliders) + { + if (kvp.Value.ExitTick < clientTick) + entriesToRemove.Add(kvp.Key); + } + foreach (Collider entry in entriesToRemove) + _enteredColliders.Remove(entry); + + CollectionCaches.Store(entriesToRemove); + } + + CheckColliders(clientTick); + } + + public override void OnStopClient() + { + //Events only needed by the client. + PredictionManager.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay; + PredictionManager.OnPostReconcileSyncTransforms -= PredictionManagerOnPreReconcile; + } + + public override void OnStopNetwork() + { + TimeManager.OnPrePhysicsSimulation -= TimeManager_OnPostPhysicsSimulation; + } + + /// + /// 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. + private void TimeManager_OnPostPhysicsSimulation(float delta) + { + CheckColliders(TimeManager.LocalTick); + } + + /// + /// Called after physics is simulated when replaying a replicate method. + /// + private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick) + { + CheckColliders(clientTick); + } + + /// + /// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough. + /// + public virtual float GetAdditionalSize() => _additionalSize; + + /// + /// Checks for any trigger changes; + /// + private void CheckColliders(uint tick) + { + //Should not be possible as tick always starts on 1. + if (tick == TimeManagerCls.UNSET_TICK) + return; + + HashSet current = CollectionCaches.RetrieveHashSet(); + Dictionary entered = _enteredColliders; + + /* Previous may not be set here if there were + * no collisions during the previous tick. */ + + // The rotation of the object for box colliders. + Quaternion rotation = transform.rotation; + + //If layers are specified then do not use GOs layers, use specified. + if (_layers != (LayerMask)0) + { + _interactableLayers = _layers; + } + //Use GOs layers. + else + { + int currentLayer = gameObject.layer; + if (_lastGameObjectLayer != currentLayer) + { + _lastGameObjectLayer = currentLayer; + _interactableLayers = Layers.GetInteractableLayersValue(currentLayer); + } + } + + //Check each collider for triggers. + foreach (Collider col in _colliders) + { + if (!col.enabled) + continue; + if (IsTrigger != col.isTrigger) + continue; + + //Number of hits from the checks. + int hits; + if (col is SphereCollider sphereCollider) + hits = GetSphereColliderHits(sphereCollider, _interactableLayers); + else if (col is CapsuleCollider capsuleCollider) + hits = GetCapsuleColliderHits(capsuleCollider, _interactableLayers); + else if (col is BoxCollider boxCollider) + hits = GetBoxColliderHits(boxCollider, rotation, _interactableLayers); + else + hits = 0; + + /* Check hits for enter/exit callbacks. */ + for (int i = 0; i < hits; i++) + { + Collider hit = _hits[i]; + if (hit == null || hit == col) + continue; + + current.Add(hit); + + //Already entered. + if (entered.TryGetValueIL2CPP(hit, out CollisionData collisionData)) + { + /* If entered tick is beyond the tick being checked then + * that means the collider entered at a later time, and something + * is not aligning. Invoke OnExit and OnEnter again. */ + if (collisionData.EnterTick >= tick || collisionData.ExitTick != TimeManagerCls.UNSET_TICK) + { + OnExit?.Invoke(hit); + OnEnter?.Invoke(hit); + //Also update position in collection. + entered[hit] = new CollisionData(tick); + } + } + //Not yet in entered state. + else + { + OnEnter?.Invoke(hit); + //Also update position in collection. + entered[hit] = new CollisionData(tick); + } + + //Always invoke OnStay when collider hits. + OnStay?.Invoke(hit); + } + + List collidersExited = CollectionCaches.RetrieveList(); + /* Check to invoke exit on any colliders which are no longer + * in the entered state. */ + foreach (Collider c in entered.Keys) + { + //Collider was still entered, no need to check exit. + if (current.Contains(c)) + continue; + //Should not be possible to exit the same time as entering unless + if (entered[c].EnterTick == tick) + continue; + + collidersExited.Add(c); + } + + //Invoke for exited and remove from entered. + foreach (Collider c in collidersExited) + { + /* If here then the entered collider was not hit + * this trace. Invoke exit and remove from entered. */ + OnExit?.Invoke(c); + + + + if (base.IsServerStarted) + entered.Remove(c); + else + entered[c] = new(entered[c].EnterTick, tick); + //entered.Remove(c); + } + } + + CollectionCaches.Store(current); + } + + /// + /// Checks for Sphere collisions. + /// + /// Number of colliders hit. + private int GetSphereColliderHits(SphereCollider sphereCollider, int layerMask) + { + sphereCollider.GetSphereOverlapParams(out Vector3 center, out float radius); + radius += GetAdditionalSize(); + return gameObject.scene.GetPhysicsScene().OverlapSphere(center, radius, _hits, layerMask, QueryTriggerInteraction.UseGlobal); + } + + /// + /// Checks for Capsule collisions. + /// + /// Number of colliders hit. + private int GetCapsuleColliderHits(CapsuleCollider capsuleCollider, int layerMask) + { + capsuleCollider.GetCapsuleCastParams(out Vector3 start, out Vector3 end, out float radius); + radius += GetAdditionalSize(); + return gameObject.scene.GetPhysicsScene().OverlapCapsule(start, end, radius, _hits, layerMask, QueryTriggerInteraction.UseGlobal); + } + + /// + /// Checks for Box collisions. + /// + /// Number of colliders hit. + private int GetBoxColliderHits(BoxCollider boxCollider, Quaternion rotation, int layerMask) + { + boxCollider.GetBoxOverlapParams(out Vector3 center, out Vector3 halfExtents); + Vector3 additional = (Vector3.one * GetAdditionalSize()); + halfExtents += additional; + return gameObject.scene.GetPhysicsScene().OverlapBox(center, halfExtents, _hits, rotation, layerMask, QueryTriggerInteraction.UseGlobal); + } + + /// + /// Finds colliders to use. + /// True to rebuild the colliders even if they are already populated. + /// + public void FindColliders(bool rebuild = false) + { + if (_collidersFound && !rebuild) + return; + _collidersFound = true; + + _colliders = GetComponents(); + } + + /// + /// Resets this NetworkBehaviour so that it may be added to an object pool. + /// + public override void ResetState(bool asServer) + { + base.ResetState(asServer); + ClearColliderDataHistory(); + } + + /// + /// Resets datas in collider data history and clears collection. + /// + private void ClearColliderDataHistory() + { + _enteredColliders.Clear(); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs.meta new file mode 100644 index 0000000..b577c85 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4f977181495644345a4d0d821c31579f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs new file mode 100644 index 0000000..58371f7 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs @@ -0,0 +1,505 @@ +using FishNet.Managing; +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using GameKit.Dependencies.Utilities.Types; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using TimeManagerCls = FishNet.Managing.Timing.TimeManager; + +namespace FishNet.Component.Prediction +{ + + public abstract class NetworkCollider2D : NetworkBehaviour + { + #region Types. + private struct Collider2DData : IResettable + { + /// + /// Tick which the collisions happened. + /// + public uint Tick; + /// + /// Hits for Tick. + /// + public HashSet Hits; + + public Collider2DData(uint tick, HashSet hits) + { + Tick = tick; + Hits = hits; + } + + public void InitializeState() { } + public void ResetState() + { + Tick = TimeManagerCls.UNSET_TICK; + CollectionCaches.StoreAndDefault(ref Hits); + } + } + #endregion + + /// + /// Called when another collider enters this collider. + /// + public event Action OnEnter; + /// + /// Called when another collider stays in this collider. + /// + public event Action OnStay; + /// + /// Called when another collider exits this collider. + /// + public event Action OnExit; + /// + /// True to run collisions for colliders which are triggers, false to run collisions for colliders which are not triggers. + /// + [HideInInspector] + protected bool IsTrigger; + /// + /// Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient. + /// + [Tooltip("Maximum number of simultaneous hits to check for. Larger values decrease performance but allow detection to work for more overlapping colliders. Typically the default value of 16 is more than sufficient.")] + [SerializeField] + private ushort _maximumSimultaneousHits = 16; + /// + /// How long of collision history to keep. Lower values will result in marginally better memory usage at the cost of collision histories desynchronizing on clients with excessive latency. + /// + [Tooltip("How long of collision history to keep. Lower values will result in marginally better memory usage at the cost of collision histories desynchronizing on clients with excessive latency.")] + [Range(0.1f, 2f)] + [SerializeField] + private float _historyDuration = 0.5f; + /// + /// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough. + /// + [Tooltip("Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough.")] + [Range(0f, 100f)] + [SerializeField] + private float _additionalSize = 0.1f; + /// + /// Layers to trace on. This is used when value is not nothing. + /// + [Tooltip("Layers to trace on. This is used when value is not nothing.")] + [SerializeField] + private LayerMask _layers = (LayerMask)0; + + /// + /// The colliders on this object. + /// + private Collider2D[] _colliders; + /// + /// The hits from the last check. + /// + private Collider2D[] _hits; + /// + /// The history of collider data. + /// + private ResettableRingBuffer _colliderDataHistory; + /// + /// True if colliders have been searched for at least once. + /// We cannot check the null state on _colliders because Unity has a habit of initializing collections on it's own. + /// + private bool _collidersFound; + /// + /// True to cache collision histories for comparing start and exits. + /// + private bool _useCache => (OnEnter != null || OnExit != null); + /// + /// Last layer of the gameObject. + /// + private int _lastGameObjectLayer = -1; + /// + /// Interactable layers for the layer of this gameObject. + /// + private int _interactableLayers; + + protected virtual void Awake() + { + _colliderDataHistory = new(); + //_colliderDataHistory = ResettableCollectionCaches.RetrieveRingBuffer(); + _hits = CollectionCaches.RetrieveArray(); + if (_hits.Length < _maximumSimultaneousHits) + _hits = new Collider2D[_maximumSimultaneousHits]; + } + + private void OnDestroy() + { + //ResettableCollectionCaches.StoreAndDefault(ref _colliderDataHistory); + CollectionCaches.StoreAndDefault(ref _hits, _hits.Length); + } + + public override void OnStartNetwork() + { + FindColliders(); + + //Initialize the ringbuffer. Server only needs 1 tick worth of history. + uint historyTicks = (base.IsServerStarted) ? 1 : TimeManager.TimeToTicks(_historyDuration); + _colliderDataHistory.Initialize((int)historyTicks); + + //Events needed by server and client. + TimeManager.OnPostPhysicsSimulation += TimeManager_OnPostPhysicsSimulation; + } + + public override void OnStartClient() + { + //Events only needed by the client. + PredictionManager.OnPostPhysicsTransformSync += PredictionManager_OnPostPhysicsTransformSync; + PredictionManager.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay; + } + + public override void OnStopClient() + { + //Events only needed by the client. + PredictionManager.OnPostPhysicsTransformSync -= PredictionManager_OnPostPhysicsTransformSync; + PredictionManager.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay; + + } + + public override void OnStopNetwork() + { + TimeManager.OnPostPhysicsSimulation -= TimeManager_OnPostPhysicsSimulation; + } + + /// + /// Called after Physics SyncTransforms are run after a reconcile. + /// This will only invoke if physics are set to TimeManager, within the TimeManager inspector. + /// + private void PredictionManager_OnPostPhysicsTransformSync(uint clientTick, uint serverTick) + { + /* This callback will only occur when client only. + * SInce this is the case remove histories prior + * to clientTick. */ + if (clientTick > 0) + CleanHistory(clientTick - 1); + CheckColliders(clientTick, true); + } + + /// + /// 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. + private void TimeManager_OnPostPhysicsSimulation(float delta) + { + CheckColliders(TimeManager.LocalTick, false); + } + + /// + /// Called after physics is simulated when replaying a replicate method. + /// + private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick) + { + CheckColliders(clientTick, true); + } + + /// + /// Cleans history up to, while excluding tick. + /// + + private void CleanHistory(uint tick) + { + if (_useCache) + { + int removeCount = 0; + int historyCount = _colliderDataHistory.Count; + for (int i = 0; i < historyCount; i++) + { + if (_colliderDataHistory[i].Tick >= tick) + break; + removeCount++; + } + + _colliderDataHistory.RemoveRange(true, removeCount, resetRemoved: true); + } + //Cache is not used. + else + { + ClearColliderDataHistory(); + } + } + + + /// + /// Units to extend collision traces by. This is used to prevent missed overlaps when colliders do not intersect enough. + /// + protected virtual float GetAdditionalSize() => _additionalSize; + + /// + /// Checks for any trigger changes; + /// + + private void CheckColliders(uint tick, bool replay) + { + //Should not be possible as tick always starts on 1. + if (tick == TimeManagerCls.UNSET_TICK) + return; + + const int INVALID_HISTORY_VALUE = -1; + + HashSet current = CollectionCaches.RetrieveHashSet(); + HashSet previous = null; + + int previousHitsIndex = INVALID_HISTORY_VALUE; + /* Server only keeps 1 history so + * if server is started then + * simply clean one. When the server is + * started replay will never be true, so this + * will only call once per tick. */ + if (base.IsServerStarted && tick > 0) + CleanHistory(tick - 1); + + if (_useCache) + { + if (replay) + { + previousHitsIndex = GetHistoryIndex(tick - 1, false); + if (previousHitsIndex != -1) + previous = _colliderDataHistory[previousHitsIndex].Hits; + } + //Not replaying. + else + { + if (_colliderDataHistory.Count > 0) + { + Collider2DData cd = _colliderDataHistory[_colliderDataHistory.Count - 1]; + /* If the hit tick one before current then it can be used, otherwise + * use a new collection for previous. */ + if (cd.Tick == (tick - 1)) + previous = cd.Hits; + } + } + } + //Not using history, clear it all. + else + { + ClearColliderDataHistory(); + } + + /* Previous may not be set here if there were + * no collisions during the previous tick. */ + + // The rotation of the object for box colliders. + Quaternion rotation = transform.rotation; + + //If layers are specified then do not use GOs layers, use specified. + if (_layers != (LayerMask)0) + { + _interactableLayers = _layers; + } + //Use GOs layers. + else + { + int currentLayer = gameObject.layer; + if (_lastGameObjectLayer != currentLayer) + { + _lastGameObjectLayer = currentLayer; + _interactableLayers = Layers.GetInteractableLayersValue(currentLayer); + } + } + + // Check each collider for triggers. + foreach (Collider2D col in _colliders) + { + if (!col.enabled) + continue; + if (IsTrigger != col.isTrigger) + continue; + + //Number of hits from the checks. + int hits; + if (col is CircleCollider2D circleCollider) + hits = GetCircleCollider2DHits(circleCollider, _interactableLayers); + else if (col is BoxCollider2D boxCollider) + hits = GetBoxCollider2DHits(boxCollider, rotation, _interactableLayers); + else + hits = 0; + + // Check the hits for triggers. + for (int i = 0; i < hits; i++) + { + Collider2D hit = _hits[i]; + if (hit == null || hit == col) + continue; + + /* If not in previous then add and + * invoke enter. */ + if (previous == null || !previous.Contains(hit)) + OnEnter?.Invoke(hit); + + //Also add to current hits. + current.Add(hit); + OnStay?.Invoke(hit); + } + } + + if (previous != null) + { + //Check for stays and exits. + foreach (Collider2D col in previous) + { + //If it was in previous but not current, it has exited. + if (!current.Contains(col)) + OnExit?.Invoke(col); + } + } + + //If not using the cache then clean up collections. + if (_useCache) + { + //If not replaying add onto the end. */ + if (!replay) + { + AddToEnd(); + } + /* If a replay then set current colliders + * to one entry past historyIndex. If the next entry + * beyond historyIndex is for the right tick it can be + * updated, otherwise a result has to be inserted. */ + else + { + /* Previous hits was not found in history so we + * cannot assume current results go right after the previousIndex. + * Find whichever index is the closest to tick and return it. + * + * If an exact match is not found for tick then the entry just after + * tick will be returned. This will let us insert current hits right + * before that entry. */ + if (previousHitsIndex == -1) + { + int currentIndex = GetHistoryIndex(tick, true); + AddDataToIndex(currentIndex); + } + //If previous hits are known then the index to update is right after previous index. + else + { + int insertIndex = (previousHitsIndex + 1); + /* InsertIndex is out of bounds which means + * to add onto the end. */ + if (insertIndex >= _colliderDataHistory.Count) + AddToEnd(); + //Not the last entry to insert in the middle. + else + AddDataToIndex(insertIndex); + } + + /* Adds data to an index. If the tick + * matches on index with the current tick then + * replace the entry. Otherwise insert to the + * correct location. */ + void AddDataToIndex(int index) + { + Collider2DData colliderData = new(tick, current); + /* If insertIndex is the same tick then replace, otherwise + * put in front of. */ + //Replace. + if (_colliderDataHistory[index].Tick == tick) + { + _colliderDataHistory[index].ResetState(); + _colliderDataHistory[index] = colliderData; + } + //Insert before. + else + { + _colliderDataHistory.Insert(index, colliderData); + } + } + } + + void AddToEnd() + { + Collider2DData colliderData = new(tick, current); + _colliderDataHistory.Add(colliderData); + } + + } + /* If not using caching then store results from this run. */ + else + { + CollectionCaches.Store(current); + } + + //Returns history index for a tick. + /* GetClosest will return the closest match which is + * past lTick if lTick could not be found. */ + int GetHistoryIndex(uint lTick, bool getClosest) + { + for (int i = 0; i < _colliderDataHistory.Count; i++) + { + uint localTick = _colliderDataHistory[i].Tick; + if (localTick == lTick) + return i; + /* Tick is too high, any further results + * will also be too high. */ + if (localTick > tick) + { + if (getClosest) + return i; + else + return INVALID_HISTORY_VALUE; + } + } + + //Fall through. + return INVALID_HISTORY_VALUE; + } + } + + /// + /// Checks for circle collisions. + /// + /// Number of colliders hit. + private int GetCircleCollider2DHits(CircleCollider2D circleCollider, int layerMask) + { + circleCollider.GetCircleOverlapParams(out Vector3 center, out float radius); + radius += GetAdditionalSize(); + return gameObject.scene.GetPhysicsScene2D().OverlapCircle(center, radius, _hits, layerMask); + } + + /// + /// Checks for Box collisions. + /// + /// Number of colliders hit. + private int GetBoxCollider2DHits(BoxCollider2D boxCollider, Quaternion rotation, int layerMask) + { + boxCollider.GetBox2DOverlapParams(out Vector3 center, out Vector3 halfExtents); + Vector3 additional = (Vector3.one * GetAdditionalSize()); + halfExtents += additional; + return gameObject.scene.GetPhysicsScene2D().OverlapBox(center, halfExtents, rotation.z, _hits, layerMask); + } + + /// + /// Finds colliders to use. + /// True to rebuild the colliders even if they are already populated. + /// + public void FindColliders(bool rebuild = false) + { + if (_collidersFound && !rebuild) + return; + _collidersFound = true; + + _colliders = GetComponents(); + } + + /// + /// Resets this NetworkBehaviour so that it may be added to an object pool. + /// + + public override void ResetState(bool asServer) + { + base.ResetState(asServer); + ClearColliderDataHistory(); + } + + /// + /// Resets datas in collider data history and clears collection. + /// + private void ClearColliderDataHistory() + { + foreach (Collider2DData cd in _colliderDataHistory) + cd.ResetState(); + _colliderDataHistory.Clear(); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs.meta new file mode 100644 index 0000000..f10876e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: df45081143314fd469a062670a0fe1ea +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollider2D.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs new file mode 100644 index 0000000..2ddb786 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs @@ -0,0 +1,14 @@ +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + public sealed class NetworkCollision : NetworkCollider + { + protected override void Awake() + { + base.IsTrigger = false; + base.Awake(); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs.meta new file mode 100644 index 0000000..bcbb707 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 40bcde17fa5e03f44b87f9963ac281b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs new file mode 100644 index 0000000..f9f6cea --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs @@ -0,0 +1,15 @@ +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + public sealed class NetworkCollision2D : NetworkCollider2D + { + protected override void Awake() + { + base.IsTrigger = false; + base.Awake(); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs.meta new file mode 100644 index 0000000..a05f21c --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: be3cb2952c351714c963b0766f53cef3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkCollision2D.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs new file mode 100644 index 0000000..b1fa1de --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs @@ -0,0 +1,14 @@ +namespace FishNet.Component.Prediction +{ + + public sealed class NetworkTrigger : NetworkCollider + { + protected override void Awake() + { + base.IsTrigger = true; + base.Awake(); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs.meta new file mode 100644 index 0000000..cb23fcf --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3dc97a8b9e628044e83004c93095c14d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs new file mode 100644 index 0000000..44d1d57 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs @@ -0,0 +1,13 @@ +namespace FishNet.Component.Prediction +{ + + public sealed class NetworkTrigger2D : NetworkCollider2D + { + protected override void Awake() + { + base.IsTrigger = true; + base.Awake(); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs.meta new file mode 100644 index 0000000..5b3f7ea --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a60424afcf0b5984dbe878a1f21c10b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/NetworkTrigger2D.cs + uploadId: 762203 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..d038af4 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs @@ -0,0 +1,114 @@ +using FishNet.Managing.Predicting; +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; + /// + /// 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(); + /// + /// 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 PredictionManager 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); + } + + /// + /// 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(uint clientTick, uint serverTick) + { + _rigidbodyPauser.Pause(); + } + + private void _predictionManager_OnPostReconcile(uint clientTick, uint serverTick) + { + _rigidbodyPauser.Unpause(); + } + } + + +} + + + 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..0aa0e8c --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b749f90d4c9961c4991179db1130fa4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/OfflineRigidbody.cs + uploadId: 762203 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..1504c90 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs @@ -0,0 +1,438 @@ +using FishNet.Managing; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using UnityEngine; + +namespace FishNet.Component.Prediction +{ + /// + /// Pauses and unpauses rigidbodies. While paused rigidbodies cannot be interacted with or simulated. + /// + public class RigidbodyPauser : IResettable + { + #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; + /// + /// True if the rigidbody was kinematic prior to being paused. + /// + public bool IsKinematic; + /// + /// Detection mode of the Rigidbody. + /// + public CollisionDetectionMode CollisionDetectionMode; + + public RigidbodyData(Rigidbody rb) + { + Rigidbody = rb; + Velocity = Vector3.zero; + AngularVelocity = Vector3.zero; + IsKinematic = rb.isKinematic; + CollisionDetectionMode = rb.collisionDetectionMode; + } + + public void Update(Rigidbody rb) + { + Velocity = rb.linearVelocity; + AngularVelocity = rb.angularVelocity; + IsKinematic = rb.isKinematic; + CollisionDetectionMode = rb.collisionDetectionMode; + } + } + + /// + /// 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; + /// + /// True if the rigidbody was kinematic prior to being paused. + /// + public bool IsKinematic; + /// + /// True if the rigidbody was simulated prior to being paused. + /// + public bool Simulated; + /// + /// Detection mode of the rigidbody. + /// + public CollisionDetectionMode2D CollisionDetectionMode; + + public Rigidbody2DData(Rigidbody2D rb) + { + Rigidbody2d = rb; + Velocity = Vector2.zero; + AngularVelocity = 0f; + Simulated = rb.simulated; + IsKinematic = rb.isKinematic; + CollisionDetectionMode = rb.collisionDetectionMode; + } + + public void Update(Rigidbody2D rb) + { + Velocity = rb.linearVelocity; + AngularVelocity = rb.angularVelocity; + Simulated = rb.simulated; + IsKinematic = rb.isKinematic; + CollisionDetectionMode = rb.collisionDetectionMode; + } + } + #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(); + /// + /// Rigidbody2D datas for found rigidbodies; + /// + private List _rigidbody2dDatas = new(); + /// + /// True to get rigidbodies in children of transform. + /// + private bool _getInChildren; + /// + /// Transform to get rigidbodies on. + /// + private Transform _transform; + /// + /// Type of prediction movement which is being used. + /// + private RigidbodyType _rigidbodyType; + /// + /// True if initialized at least once. + /// + private bool _initialized; + #endregion + + /// + /// Assigns rigidbodies using initialized settings. + /// + public void UpdateRigidbodies() + { + if (!_initialized) + { + InstanceFinder.NetworkManager.LogError($"T{GetType().Name} has not been initialized yet. This method cannot be used."); + return; + } + + UpdateRigidbodies(_transform, _rigidbodyType, _getInChildren); + } + + /// + /// Assigns rigidbodies manually and initializes component. + /// + public void UpdateRigidbodies(Rigidbody[] rbs) + { + List rigidbodies = CollectionCaches.RetrieveList(); + foreach (Rigidbody rb in rbs) + rigidbodies.Add(rb); + + UpdateRigidbodies(rigidbodies); + + CollectionCaches.Store(rigidbodies); + } + /// + /// Assigns rigidbodies manually and initializes component. + /// + private void UpdateRigidbodies(List rbs) + { + _rigidbodyDatas.Clear(); + + foreach (Rigidbody rb in rbs) + _rigidbodyDatas.Add(new(rb)); + + _initialized = true; + } + + /// + /// Assigns rigidbodies manually and initializes component. + /// + public void UpdateRigidbodies2D(Rigidbody2D[] rbs) + { + List rigidbodies = CollectionCaches.RetrieveList(); + foreach (Rigidbody2D rb in rbs) + rigidbodies.Add(rb); + + UpdateRigidbodies2D(rigidbodies); + + CollectionCaches.Store(rigidbodies); + } + /// + /// Assigns rigidbodies manually and initializes component. + /// + private void UpdateRigidbodies2D(List rbs) + { + _rigidbody2dDatas.Clear(); + + foreach (Rigidbody2D rb in rbs) + _rigidbody2dDatas.Add(new(rb)); + + _initialized = true; + } + + /// + /// Assigns rigidbodies. + /// + /// Rigidbodies2D to use. + public void UpdateRigidbodies(Transform t, RigidbodyType rbType, bool getInChildren) + { + _rigidbodyType = rbType; + _getInChildren = getInChildren; + + //3D. + if (rbType == RigidbodyType.Rigidbody) + { + List rigidbodies = CollectionCaches.RetrieveList(); + + if (getInChildren) + { + Rigidbody[] rbs = t.GetComponentsInChildren(); + for (int i = 0; i < rbs.Length; i++) + rigidbodies.Add(rbs[i]); + } + else + { + Rigidbody rb = t.GetComponent(); + if (rb != null) + rigidbodies.Add(rb); + } + + UpdateRigidbodies(rigidbodies); + CollectionCaches.Store(rigidbodies); + } + //2D. + else + { + List rigidbodies = CollectionCaches.RetrieveList(); + + if (getInChildren) + { + Rigidbody2D[] rbs = t.GetComponentsInChildren(); + for (int i = 0; i < rbs.Length; i++) + rigidbodies.Add(rbs[i]); + } + else + { + Rigidbody2D rb = t.GetComponent(); + if (rb != null) + rigidbodies.Add(rb); + } + + UpdateRigidbodies2D(rigidbodies); + CollectionCaches.Store(rigidbodies); + } + } + + /// + /// Pauses rigidbodies preventing them from interacting. + /// + public void Pause() + { + if (Paused) + return; + Paused = true; + + + /* Iterate move after pausing. + * This ensures when the children RBs update values + * they are not updating from a new scene, where the root + * may have moved them */ + + //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; + rb.collisionDetectionMode = CollisionDetectionMode.Discrete; + rb.isKinematic = true; + //rb.detectCollisions = false; + + 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; + rb.collisionDetectionMode = CollisionDetectionMode2D.Discrete; + rb.isKinematic = true; + rb.simulated = false; + + return true; + } + } + } + + /// + /// 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; + + /* If data has RB updated as kinematic then + * do not unpause. This means either something else + * is handling the kinematic state of the dev + * made it kinematic. */ + if (rbData.IsKinematic) + return true; + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + rb.isKinematic = rbData.IsKinematic; + //rb.detectCollisions = rbData.DetectCollisions; + rb.collisionDetectionMode = rbData.CollisionDetectionMode; + if (!rb.isKinematic) + { + rb.linearVelocity = rbData.Velocity; + rb.angularVelocity = rbData.AngularVelocity; + } + 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; + + //Same as RB, only unpause if data is stored in an unpaused state. + if (rbData.IsKinematic || !rbData.Simulated) + return true; + + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + rb.isKinematic = rbData.IsKinematic; + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + rb.simulated = rbData.Simulated; + rb.collisionDetectionMode = rbData.CollisionDetectionMode; + if (!rb.isKinematic) + { + rb.linearVelocity = rbData.Velocity; + rb.angularVelocity = rbData.AngularVelocity; + } + return true; + } + } + } + + public void ResetState() + { + _rigidbodyDatas.Clear(); + _rigidbody2dDatas.Clear(); + _getInChildren = default; + _transform = default; + _rigidbodyType = default; + _initialized = default; + Paused = default; + } + + public void InitializeState() { } + } +} \ 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..8939b24 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9536377524ca5db43aae431f983ab21f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyPauser.cs + uploadId: 762203 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..4697977 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs @@ -0,0 +1,179 @@ +using FishNet.CodeGenerating; +using FishNet.Serializing; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Component.Prediction +{ + [UseGlobalCustomSerializer] + [Preserve] + public struct RigidbodyState + { + public Vector3 Position; + public Quaternion Rotation; + public bool IsKinematic; + public Vector3 Velocity; + public Vector3 AngularVelocity; + + public RigidbodyState(Rigidbody rb, bool isKinematic) : this(rb) + { + Position = rb.transform.position; + Rotation = rb.transform.rotation; + IsKinematic = isKinematic; + Velocity = rb.linearVelocity; + AngularVelocity = rb.angularVelocity; + } + public RigidbodyState(Rigidbody rb) + { + Position = rb.transform.position; + Rotation = rb.transform.rotation; + IsKinematic = rb.isKinematic; + Velocity = rb.linearVelocity; + AngularVelocity = rb.angularVelocity; + } + } + + [UseGlobalCustomSerializer] + [Preserve] + public struct Rigidbody2DState + { + public Vector3 Position; + public Quaternion Rotation; + public Vector2 Velocity; + public float AngularVelocity; + public bool Simulated; + public bool IsKinematic; + + public Rigidbody2DState(Rigidbody2D rb, bool simulated) + { + Position = rb.transform.position; + Rotation = rb.transform.rotation; + Velocity = rb.linearVelocity; + AngularVelocity = rb.angularVelocity; + Simulated = simulated; + IsKinematic = rb.isKinematic; + } + + public Rigidbody2DState(Rigidbody2D rb) + { + Position = rb.transform.position; + Rotation = rb.transform.rotation; + Velocity = rb.linearVelocity; + AngularVelocity = rb.angularVelocity; + Simulated = rb.simulated; + IsKinematic = rb.isKinematic; + } + } + + [Preserve] + public static class RigidbodyStateSerializers + { + public static void WriteRigidbodyState(this Writer writer, RigidbodyState value) + { + writer.WriteVector3(value.Position); + writer.WriteQuaternion32(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() + { + Position = reader.ReadVector3(), + Rotation = reader.ReadQuaternion32(), + 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.WriteVector3(value.Position); + writer.WriteQuaternion32(value.Rotation); + writer.WriteBoolean(value.Simulated); + writer.WriteBoolean(value.IsKinematic); + + if (value.Simulated) + { + writer.WriteVector2(value.Velocity); + writer.WriteSingle(value.AngularVelocity); + } + } + + public static Rigidbody2DState ReadRigidbody2DState(this Reader reader) + { + Rigidbody2DState state = new() + { + Position = reader.ReadVector3(), + Rotation = reader.ReadQuaternion32(), + Simulated = reader.ReadBoolean(), + IsKinematic = reader.ReadBoolean() + }; + + if (state.Simulated) + { + state.Velocity = reader.ReadVector2(); + state.AngularVelocity = reader.ReadSingle(); + } + + return state; + } + + + } + + [Preserve] + public static class RigidbodyStateExtensions + { + /// + /// Gets a RigidbodyState. + /// + public static RigidbodyState GetState(this Rigidbody rb) + { + return new(rb); + } + /// + /// Sets a state to a rigidbody. + /// + public static void SetState(this Rigidbody rb, RigidbodyState state) + { + Transform t = rb.transform; + t.position = state.Position; + t.rotation = state.Rotation; + rb.linearVelocity = state.Velocity; + rb.angularVelocity = state.AngularVelocity; + } + + /// + /// Gets a Rigidbody2DState. + /// + public static Rigidbody2DState GetState(this Rigidbody2D rb) + { + return new(rb); + } + /// + /// Sets a state to a rigidbody. + /// + public static void SetState(this Rigidbody2D rb, Rigidbody2DState state) + { + Transform t = rb.transform; + t.position = state.Position; + t.rotation = state.Rotation; + rb.linearVelocity = state.Velocity; + rb.angularVelocity = state.AngularVelocity; + } + + } +} 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..038b6da --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 05dbb585c2bc6bf4dbbc592bea73d2fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyState.cs + uploadId: 762203 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..8f01080 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7cf2d3fc2ff7a9042b4b7618db15b482 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Prediction/RigidbodyType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Spawning.meta b/Assets/FishNet/Runtime/Generated/Component/Spawning.meta new file mode 100644 index 0000000..e4b7199 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ae5df816b1e348a4681e184662e59bb5 +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..35ef876 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs @@ -0,0 +1,160 @@ +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. + /// + [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; + + /// + /// Sets the PlayerPrefab to use. + /// + /// + public void SetPlayerPrefab(NetworkObject nob) => _playerPrefab = nob; + + /// + /// 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.")] + public Transform[] Spawns = new Transform[0]; + #endregion + + #region Private. + /// + /// First instance of the NetworkManager found. This will be either the NetworkManager on or above this object, or InstanceFinder.NetworkManager. + /// + private NetworkManager _networkManager; + /// + /// Next spawns to use. + /// + private int _nextSpawn; + #endregion + + private void Awake() + { + InitializeOnce(); + } + + private void OnDestroy() + { + if (_networkManager != null) + _networkManager.SceneManager.OnClientLoadedStartScenes -= SceneManager_OnClientLoadedStartScenes; + } + + /// + /// Initializes this script for use. + /// + private void InitializeOnce() + { + _networkManager = GetComponentInParent(); + if (_networkManager == null) + _networkManager = InstanceFinder.NetworkManager; + + if (_networkManager == null) + { + NetworkManagerExtensions.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) + { + NetworkManagerExtensions.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, position, rotation, true); + _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..59c5c80 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 211a9f6ec51ddc14f908f5acc0cd0423 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Spawning/PlayerSpawner.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs b/Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs new file mode 100644 index 0000000..8292dc8 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs @@ -0,0 +1,102 @@ +using FishNet.Managing; +using FishNet.Object; +using System.Collections.Generic; +using FishNet.Managing.Server; +using FishNet.Transporting; +using UnityEngine; + +namespace FishNet.Component.Spawning +{ + /// + /// Spawns network objects when the server starts. + /// + [AddComponentMenu("FishNet/Component/ServerSpawner")] + public class ServerSpawner : MonoBehaviour + { + #region Serialized + [Tooltip("True to spawn the objects as soon as the server starts. False if you wish to call Spawn manually.")] + [SerializeField] + private bool _automaticallySpawn = true; + /// + /// NetworkObjects to spawn when the server starts. + /// + [Tooltip("NetworkObjects to spawn when the server starts.")] + [SerializeField] + private List _networkObjects = new(); + #endregion + + #region Private. + /// + /// First instance of the ServerManager found. This will be either the ServerManager on or above this object, or InstanceFinder.ServerManager. + /// + private ServerManager _serverManager; + #endregion + + private void Awake() + { + InitializeOnce(); + } + + private void OnDestroy() + { + if (_serverManager == null) + return; + + //Unsubscribe even if not automatically spawning; this is to protect against the user unchecking during play mode. + _serverManager.OnServerConnectionState -= ServerManager_OnServerConnectionState; + } + + /// + /// Initializes this script for use. + /// + private void InitializeOnce() + { + _serverManager = GetComponentInParent(); + if (_serverManager == null) + _serverManager = InstanceFinder.ServerManager; + + if (_serverManager == null) + { + NetworkManagerExtensions.LogWarning($"PlayerSpawner on {gameObject.name} cannot work as NetworkManager wasn't found on this object or within parent objects."); + return; + } + + //Only subscribe if to automatically spawn. + if (_automaticallySpawn) + _serverManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + } + + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs args) + { + //If not started then exit. + if (args.ConnectionState != LocalConnectionState.Started) + return; + + //If more than 1 server is started then exit. This means the user is using multipass and another server already started. + if (!_serverManager.IsOnlyOneServerStarted()) + return; + + Spawn_Internally(); + } + + private void Spawn_Internally() + { + if (_serverManager == null) + return; + + //Spawn the objects now. + foreach (NetworkObject networkObject in _networkObjects) + { + NetworkObject nob = _serverManager.NetworkManager.GetPooledInstantiated(networkObject, asServer: true); + _serverManager.Spawn(nob); + } + } + + /// + /// Spawns all provided NetworkObjects. + /// + /// This will spawn the objects again even if they were already spawned automatically or manually before. + public void Spawn() => Spawn_Internally(); + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs.meta new file mode 100644 index 0000000..a593212 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 859294e5d2aaf9a47b1e00bd20772f2b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Spawning/ServerSpawner.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta new file mode 100644 index 0000000..cda3f00 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3bb69ba1a673375489a94fa875c3aa98 +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..bdcb992 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using FishNet.CodeGenerating; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Object.Synchronizing; +using GameKit.Dependencies.Utilities; +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.")] [SerializeField] + private bool _allowTakeOwnership = true; + private readonly SyncVar _allowTakeOwnershipSyncVar = new(); + + /// + /// 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) => _allowTakeOwnershipSyncVar.Value = value; + + #endregion + + protected virtual void Awake() + { + _allowTakeOwnershipSyncVar.Value = _allowTakeOwnership; + _allowTakeOwnershipSyncVar.UpdateSendRate(0f); + _allowTakeOwnershipSyncVar.OnChange += _allowTakeOwnershipSyncVar_OnChange; + } + + /// + /// Called when SyncVar value changes for AllowTakeOwnership. + /// + private void _allowTakeOwnershipSyncVar_OnChange(bool prev, bool next, bool asServer) + { + if (asServer || !base.IsHostStarted) + _allowTakeOwnership = next; + } + + /// + /// Called on the client after gaining or losing ownership. + /// + /// Previous owner of this object. + public override void OnOwnershipClient(NetworkConnection 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; + } + + [Client] + + [Obsolete("Use TakeOwnership(bool).")] + public virtual void TakeOwnership() => TakeOwnership(includeNested: true); + + /// + /// Gives ownership of this to the local client and allows immediate control. + /// + /// True to also take ownership of nested objects. + public virtual void TakeOwnership(bool includeNested) + { + if (!_allowTakeOwnershipSyncVar.Value) + return; + //Already owner. + if (base.IsOwner) + return; + + NetworkConnection c = base.ClientManager.Connection; + TakingOwnership = true; + //If not server go through the server. + if (!base.IsServerStarted) + { + base.NetworkObject.SetLocalOwnership(c, includeNested); + ServerTakeOwnership(includeNested); + } + //Otherwise take directly without rpcs. + else + { + OnTakeOwnership(c, includeNested); + } + } + + + /// + /// Takes ownership of this object. + /// + [ServerRpc(RequireOwnership = false)] + private void ServerTakeOwnership(bool includeNested, NetworkConnection caller = null) + { + OnTakeOwnership(caller, includeNested); + } + + [Server] + + [Obsolete("Use OnTakeOwnership(bool).")] + protected virtual void OnTakeOwnership(NetworkConnection caller) => OnTakeOwnership(caller, recursive: false); + + /// + /// 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, bool recursive) + { + //Client somehow disconnected between here and there. + if (!caller.IsActive) + return; + //Feature is not enabled. + if (!_allowTakeOwnershipSyncVar.Value) + return; + //Already owner. + if (caller == base.Owner) + return; + + base.GiveOwnership(caller); + if (recursive) + { + List allNested = base.NetworkObject.GetNetworkObjects(GetNetworkObjectOption.AllNestedRecursive); + + foreach (NetworkObject nob in allNested) + { + PredictedOwner po = nob.PredictedOwner; + if (po != null) + po.OnTakeOwnership(caller, recursive: true); + } + + CollectionCaches.Store(allNested); + } + /* 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..df33648 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 03002f7d324007e41b10a9dc87ed3c38 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedOwner.cs + uploadId: 762203 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..4eaa558 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs @@ -0,0 +1,81 @@ +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; + #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 OnTryDespawnServer(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..8c2c66d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e2b597e1828355a4d994a69cbb11ef85 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TakeOwnership/PredictedSpawn.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing.meta new file mode 100644 index 0000000..df7b98c --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd9e54c58fb9c984ba69d3525a8f8d53 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs new file mode 100644 index 0000000..b6b1740 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs @@ -0,0 +1,36 @@ +namespace FishNet.Component.Transforming +{ + public enum AdaptiveInterpolationType + { + /// + /// Adaptive interpolation is disabled. An exact interpolation value is used. + /// + Off = 0, + /// + /// Visual disturbances caused by desynchronization are definite without predicting future states. + /// + ExtremelyLow = 1, + /// + /// Visual disturbances caused by desynchronization are likely without predicting future states. + /// + VeryLow = 2, + /// + /// Visual disturbances caused by desynchronization are still possible but less likely. + /// + Low = 3, + /// + /// Visual disturbances caused by desynchronization are likely without predicting a small amount of future states. + /// + Moderate = 4, + /// + /// Visual disturbances caused by desynchronization are very unlikely. Graphics are using a generous amount interpolation. + /// + High = 5, + /// + /// Visual disturbances caused by desynchronization are extremely unlikely. Graphics are using a generous amount interpolation. + /// + VeryHigh = 6, + } + + +} diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs.meta new file mode 100644 index 0000000..91d4e34 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 612b76499aa863b4b8a1b773a466cc7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/AdaptiveInterpolationType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor.meta new file mode 100644 index 0000000..c4bb034 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 43bf94bbbaa20b6489f07cd1abe42972 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs new file mode 100644 index 0000000..5f769cb --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs @@ -0,0 +1,51 @@ +#if UNITY_EDITOR +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta.Editing +{ + [CustomPropertyDrawer(typeof(MovementSettings))] + public class MovementSettingsDrawer : PropertyDrawer + { + private PropertyDrawerTool _propertyDrawer; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + EditorGUI.BeginProperty(position, label, property); + + _propertyDrawer = new(position); + + //_propertyDrawer.DrawLabel(label, FontStyle.Bold); + + EditorGUI.indentLevel++; + + SerializedProperty enableTeleport = property.FindPropertyRelative("EnableTeleport"); + SerializedProperty teleportThreshold = property.FindPropertyRelative("TeleportThreshold"); + SerializedProperty adaptiveInterpolationValue = property.FindPropertyRelative("AdaptiveInterpolationValue"); + SerializedProperty interpolationValue = property.FindPropertyRelative("InterpolationValue"); + SerializedProperty smoothedProperties = property.FindPropertyRelative("SmoothedProperties"); + SerializedProperty snapNonSmoothedProperties = property.FindPropertyRelative("SnapNonSmoothedProperties"); + + _propertyDrawer.DrawProperty(enableTeleport, "Enable Teleport"); + if (enableTeleport.boolValue == true) + _propertyDrawer.DrawProperty(teleportThreshold, "Teleport Threshold", indent: 1); + + _propertyDrawer.DrawProperty(adaptiveInterpolationValue, "Adaptive Interpolation"); + if ((AdaptiveInterpolationType)adaptiveInterpolationValue.intValue == AdaptiveInterpolationType.Off) + _propertyDrawer.DrawProperty(interpolationValue, "Interpolation Value", indent: 1); + + _propertyDrawer.DrawProperty(smoothedProperties, "Smoothed Properties"); + if ((uint)smoothedProperties.intValue != (uint)TransformPropertiesFlag.Everything) + _propertyDrawer.DrawProperty(snapNonSmoothedProperties, "Snap Non-Smoothed Properties", indent: 1); + + _propertyDrawer.SetIndentToStarting(); + + EditorGUI.EndProperty(); + } + + public override float GetPropertyHeight(SerializedProperty property, GUIContent label) => _propertyDrawer.GetPropertyHeight(); + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs.meta new file mode 100644 index 0000000..3100436 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aa57faea7c2b2ce4b95e4865a64f3551 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/MovementSettingsDrawer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs new file mode 100644 index 0000000..f290a97 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs @@ -0,0 +1,55 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta +{ + + + [CustomEditor(typeof(NetworkTickSmoother), true)] + [CanEditMultipleObjects] + public class NetworkTickSmootherEditor : Editor + { + private SerializedProperty _initializationSettings; + private SerializedProperty _controllerMovementSettings; + private SerializedProperty _spectatorMovementSettings; + + private bool _showControllerSmoothingSettings; + private bool _showSpectatorSmoothingSettings; + + protected virtual void OnEnable() + { + _initializationSettings = serializedObject.FindProperty(nameof(_initializationSettings)); + _controllerMovementSettings = serializedObject.FindProperty(nameof(_controllerMovementSettings)); + _spectatorMovementSettings = serializedObject.FindProperty(nameof(_spectatorMovementSettings)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((NetworkTickSmoother)target), typeof(NetworkTickSmoother), false); + GUI.enabled = true; + + //EditorGUILayout.LabelField("Initialization Settings", EditorStyles.boldLabel); + + EditorGUILayout.PropertyField(_initializationSettings); + + _showControllerSmoothingSettings = EditorGUILayout.Foldout(_showControllerSmoothingSettings, new GUIContent("Controller Smoothing", "Smoothing applied when object controller. This would be the owner, or if there is no owner and are also server.")); + if (_showControllerSmoothingSettings) + EditorGUILayout.PropertyField(_controllerMovementSettings); + + _showSpectatorSmoothingSettings = EditorGUILayout.Foldout(_showSpectatorSmoothingSettings, new GUIContent("Spectator Smoothing", "Smoothing applied when object not the owner. This is when server and there is an owner, or when client and not the owner.")); + if (_showSpectatorSmoothingSettings) + EditorGUILayout.PropertyField(_spectatorMovementSettings); + + + //EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs.meta new file mode 100644 index 0000000..04f61ee --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f51697e093480ab429a4a2f47f545f92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/NetworkTickSmootherEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs new file mode 100644 index 0000000..03d0a9b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs @@ -0,0 +1,50 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta +{ + + + [CustomEditor(typeof(OfflineTickSmoother), true)] + [CanEditMultipleObjects] + public class OfflineTickSmootherEditor : Editor + { + private SerializedProperty _automaticallyInitialize; + private SerializedProperty _initializationSettings; + private SerializedProperty _movementSettings; + + private bool _showMovementSettings; + + protected virtual void OnEnable() + { + _automaticallyInitialize = serializedObject.FindProperty(nameof(_automaticallyInitialize)); + _initializationSettings = serializedObject.FindProperty(nameof(_initializationSettings)); + _movementSettings = serializedObject.FindProperty(nameof(_movementSettings)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((OfflineTickSmoother)target), typeof(OfflineTickSmoother), false); + GUI.enabled = true; + + //EditorGUILayout.LabelField("Initialization Settings", EditorStyles.boldLabel); + EditorGUILayout.PropertyField(_automaticallyInitialize); + EditorGUILayout.PropertyField(_initializationSettings); + + _showMovementSettings = EditorGUILayout.Foldout(_showMovementSettings, "Smoothing"); + if (_showMovementSettings) + EditorGUILayout.PropertyField(_movementSettings); + + + //EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs.meta new file mode 100644 index 0000000..cc80763 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 80812403004e23f44ae42bceba1b6733 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/Editor/OfflineTickSmootherEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs new file mode 100644 index 0000000..2a07321 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs @@ -0,0 +1,66 @@ +using FishNet.Managing.Timing; +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta +{ + [System.Serializable] + public struct InitializationSettings + { + /// + /// While this script is typically placed on a nested graphical object, the targetTransform would be the object which moves every tick; the TargetTransform can be the same object this script resides but may not be a rigidbody if true; + /// + [Tooltip("While this script is typically placed on a nested graphical object, the targetTransform would be the object which moves every tick; the TargetTransform can be the same object this script resides but may not be a rigidbody if true;")] + [SerializeField] + public Transform TargetTransform; + /// + /// The transform which is smoothed. + /// + [Tooltip("The transform which is smoothed.")] + [System.NonSerialized] + internal Transform GraphicalTransform; + /// + /// True to detacth this object from its parent on client start. + /// + [Tooltip("True to detach this object from it's parent on client start.")] + public bool DetachOnStart; + /// + /// True to re-attach this object to it's parent on client stop. + /// + [Tooltip("True to re-attach this object to it's parent on client stop.")] + public bool AttachOnStop; + /// + /// True to begin moving soon as movement data becomes available. Movement will ease in until at interpolation value. False to prevent movement until movement data count meet interpolation. + /// + /// This is not yet used. + [Tooltip("True to begin moving soon as movement data becomes available. Movement will ease in until at interpolation value. False to prevent movement until movement data count meet interpolation.")] + public bool MoveImmediately => false; + /// + /// NetworkBehaviour which initialized these settings. This value may be null if not initialized from a NetworkBehaviour. + /// + [System.NonSerialized] + internal NetworkBehaviour InitializingNetworkBehaviour; + /// + /// TimeManager initializing these settings. + /// + [System.NonSerialized] + internal TimeManager InitializingTimeManager; + + public void SetNetworkedRuntimeValues(NetworkBehaviour initializingNetworkBehaviour, Transform graphicalTransform) + { + InitializingNetworkBehaviour = initializingNetworkBehaviour; + GraphicalTransform = graphicalTransform; + InitializingTimeManager = initializingNetworkBehaviour.TimeManager; + } + /// + /// Sets values used at runtime. NetworkBehaviour is nullified when calling this method. + /// + public void SetOfflineRuntimeValues(TimeManager timeManager, Transform graphicalTransform) + { + InitializingNetworkBehaviour = null; + GraphicalTransform = graphicalTransform; + InitializingTimeManager = timeManager; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs.meta new file mode 100644 index 0000000..70c0906 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0c6e8313aa3aa964585e4ec54b733627 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/InitializationSettings.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs new file mode 100644 index 0000000..74a4c4b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs @@ -0,0 +1,53 @@ +using FishNet.Object; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta +{ + [System.Serializable] + public struct MovementSettings + { + /// + /// True to enable teleport threshold. + /// + [Tooltip("True to enable teleport threshold.")] + public bool EnableTeleport; + /// + /// How far the object must move between ticks to teleport rather than smooth. + /// + [Tooltip("How far the object must move between ticks to teleport rather than smooth.")] + [Range(0f, ushort.MaxValue)] + public float TeleportThreshold; + /// + /// Amount of adaptive interpolation to use. Adaptive interpolation increases interpolation with the local client's latency. Lower values of adaptive interpolation results in smaller interpolation increases. + /// In most cases adaptive interpolation is only used with prediction where objects might be affected by other moving objects. + /// + [Tooltip("Amount of adaptive interpolation to use. Adaptive interpolation increases interpolation with the local client's latency. Lower values of adaptive interpolation results in smaller interpolation increases. In most cases adaptive interpolation is only used with prediction where objects might be affected by other moving objects.")] + public AdaptiveInterpolationType AdaptiveInterpolationValue; + /// + /// Number of ticks to smooth over when not using adaptive interpolation. + /// + [Tooltip("Number of ticks to smooth over when not using adaptive interpolation.")] + public byte InterpolationValue; + /// + /// Properties to smooth. Any value not selected will become offset with every movement. + /// + [Tooltip("Properties to smooth. Any value not selected will become offset with every movement.")] + public TransformPropertiesFlag SmoothedProperties; + /// + /// True to keep non-smoothed properties at their original localspace every tick. A false value will keep the properties in the same world space as they were before each tick. + /// + [Tooltip("True to keep non-smoothed properties at their original localspace every tick. A false value will keep the properties in the same world space as they were before each tick.")] + public bool SnapNonSmoothedProperties; + + public MovementSettings(bool unityReallyNeedsToSupportParameterlessInitializersOnStructsAlready) + { + EnableTeleport = false; + TeleportThreshold = 0f; + AdaptiveInterpolationValue = AdaptiveInterpolationType.Off; + InterpolationValue = 2; + SmoothedProperties = TransformPropertiesFlag.Everything; + SnapNonSmoothedProperties = false; + } + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs.meta new file mode 100644 index 0000000..8667b32 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c01e6529f1dd28f4fa6d97ba1a480e22 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/MovementSettings.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs new file mode 100644 index 0000000..7e1343e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs @@ -0,0 +1,85 @@ +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta +{ + /// + /// Smoothes this object between ticks. + /// + /// This can be configured to smooth over a set interval of time, or to smooth adaptively and make path corrections for prediction. + public class NetworkTickSmoother : NetworkBehaviour + { + #region Public. + /// + /// Logic for owner smoothing. + /// + public TickSmootherController SmootherController { get; private set; } + #endregion + + /// + /// Settings required to initialize the smoother. + /// + [Tooltip("Settings required to initialize the smoother.")] + [SerializeField] + private InitializationSettings _initializationSettings = new(); + /// + /// How smoothing occurs when the controller of the object. + /// + [Tooltip("How smoothing occurs when the controller of the object.")] + [SerializeField] + private MovementSettings _controllerMovementSettings = new(true); + /// + /// How smoothing occurs when spectating the object. + /// + [Tooltip("How smoothing occurs when spectating the object.")] + [SerializeField] + private MovementSettings _spectatorMovementSettings = new(true); + + private void OnDestroy() + { + if (SmootherController != null) + SmootherController.OnDestroy(); + StoreControllers(); + } + + public override void OnStartClient() + { + RetrieveControllers(); + + _initializationSettings.SetNetworkedRuntimeValues(initializingNetworkBehaviour: this, graphicalTransform: transform); + SmootherController.Initialize(_initializationSettings, _controllerMovementSettings, _spectatorMovementSettings); + + SmootherController.StartSmoother(); + } + + public override void OnStopClient() + { + if (SmootherController == null) + return; + + SmootherController.StopSmoother(); + } + + /// + /// Stores smoothers if they have value. + /// + private void StoreControllers() + { + if (SmootherController == null) + return; + + ResettableObjectCaches.Store(SmootherController); + SmootherController = null; + } + + /// + /// Stores current smoothers and retrieves new ones. + /// + private void RetrieveControllers() + { + StoreControllers(); + SmootherController = ResettableObjectCaches.Retrieve(); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs.meta new file mode 100644 index 0000000..13d5853 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 453481867b26f7c43b5bf38802a5f50e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/NetworkTickSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs new file mode 100644 index 0000000..b9892ea --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs @@ -0,0 +1,143 @@ +using FishNet.Managing; +using FishNet.Managing.Timing; +using GameKit.Dependencies.Utilities; +using UnityEngine; +using UnityEngine.Serialization; + +namespace FishNet.Component.Transforming.Beta +{ + /// + /// Smoothes this object between ticks. + /// + /// This can be configured to smooth over a set interval of time, or to smooth adaptively and make path corrections for prediction. + public class OfflineTickSmoother : MonoBehaviour + { + #region Public. + /// + /// Logic for owner smoothing. + /// + public TickSmootherController SmootherController { get; private set; } + /// + /// True if this component is initialized. + /// + /// This API is for internal use and may change at any time. + public bool IsInitialized { get; private set; } + #endregion + + #region Serialized. + /// + /// True to automatically initialize in Awake using InstanceFinder. When false you will need to manually call Initialize. + /// + [Tooltip("True to automatically initialize in Awake using InstanceFinder. When false you will need to manually call Initialize.")] + [SerializeField] + private bool _automaticallyInitialize = true; + /// + /// Settings required to initialize the smoother. + /// + [Tooltip("Settings required to initialize the smoother.")] + [SerializeField] + private InitializationSettings _initializationSettings = new(); + /// + /// How smoothing occurs when the controller of the object. + /// + [FormerlySerializedAs("_controllerMovementSettings")] + [Tooltip("How smoothing occurs when the controller of the object.")] + [SerializeField] + private MovementSettings _movementSettings = new(true); + #endregion + + private void Awake() + { + RetrieveControllers(); + AutomaticallyInitialize(); + } + + private void OnDestroy() + { + if (SmootherController != null) + { + SmootherController.StopSmoother(); + SmootherController.OnDestroy(); + } + + StoreControllers(); + + IsInitialized = false; + } + + /// + /// Automatically initializes if feature is enabled. + /// + private void AutomaticallyInitialize() + { + if (!_automaticallyInitialize) + return; + + TimeManager tm = InstanceFinder.TimeManager; + if (tm == null) + { + NetworkManagerExtensions.LogWarning($"Automatic initialization failed on {gameObject.name}. You must manually call Initialize."); + return; + } + + Initialize(tm); + } + + /// + /// Initializes using a specified TimeManager. + /// + /// + public void Initialize(TimeManager timeManager) + { + if (timeManager == null) + { + NetworkManagerExtensions.LogError($"TimeManager cannot be null when initializing."); + return; + } + + SmootherController.SetTimeManager(timeManager); + + _initializationSettings.SetOfflineRuntimeValues(timeManager, graphicalTransform: transform); + SmootherController.Initialize(_initializationSettings, _movementSettings, default); + SmootherController.StartSmoother(); + + IsInitialized = true; + } + + /// + /// Sets a transform as the target to follow. + /// + /// New value. + public void SetTargetTransform(Transform value) + { + if (IsInitialized) + { + NetworkManagerExtensions.LogError($"Target can only be set before Initialize is called."); + return; + } + + _initializationSettings.TargetTransform = value; + } + + /// + /// Stores smoothers if they have value. + /// + private void StoreControllers() + { + if (SmootherController == null) + return; + + ResettableObjectCaches.Store(SmootherController); + SmootherController = null; + } + + /// + /// Stores current smoothers and retrieves new ones. + /// + private void RetrieveControllers() + { + StoreControllers(); + SmootherController = ResettableObjectCaches.Retrieve(); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs.meta new file mode 100644 index 0000000..4ce98b1 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2361c564b0f77b94dbc549ff9caa72a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/OfflineTickSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs new file mode 100644 index 0000000..7b5c907 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs @@ -0,0 +1,298 @@ +using FishNet.Managing.Predicting; +using FishNet.Managing.Timing; +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Component.Transforming.Beta +{ + /// + /// Smoothes this object between ticks. + /// + /// This can be configured to smooth over a set interval of time, or to smooth adaptively and make path corrections for prediction. + public class TickSmootherController : IResettable + { + #region Public. + /// + /// Logic for owner smoothing. + /// + public UniversalTickSmoother UniversalSmoother { get; private set; } + #endregion + + #region Private. + /// + /// + /// + private InitializationSettings _initializationSettings = new(); + /// + /// + /// + private MovementSettings _ownerMovementSettings = new(); + /// + /// + /// + private MovementSettings _spectatorMovementSettings = new(); + /// + /// True if OnDestroy has been called. + /// + private bool _destroyed; + /// + /// Cached timeManager reference. + /// + private TimeManager _timeManager; + /// + /// NetworkBehaviour which initialized this object. Value may be null when initialized for an Offline smoother. + /// + private NetworkBehaviour _initializingNetworkBehaviour; + /// + /// Transform which initialized this object. + /// + private Transform _graphicalTransform; + /// + /// True if initialized with a null NetworkBehaviour. + /// + private bool _initializedOffline; + /// + /// True if subscribed to events used for adaptiveInterpolation. + /// + private bool _subscribedToAdaptiveEvents; + /// + /// True if currently subscribed to events. + /// + private bool _subscribed; + /// + /// True if initialized. + /// + private bool _isInitialized; + #endregion + + public void Initialize(InitializationSettings initializationSettings, MovementSettings ownerSettings, MovementSettings spectatorSettings) + { + _initializingNetworkBehaviour = initializationSettings.InitializingNetworkBehaviour; + _graphicalTransform = initializationSettings.GraphicalTransform; + + _initializationSettings = initializationSettings; + _ownerMovementSettings = ownerSettings; + _spectatorMovementSettings = spectatorSettings; + + _initializedOffline = (initializationSettings.InitializingNetworkBehaviour == null); + + _isInitialized = true; + } + + public void OnDestroy() + { + ChangeSubscriptions(false); + StoreSmoother(); + _destroyed = true; + _isInitialized = false; + } + + public void StartSmoother() + { + if (!_isInitialized) + return; + + bool canStart = (_initializedOffline) ? StartOffline() : StartOnline(); + + if (!canStart) + return; + + RetrieveSmoothers(); + + UniversalSmoother.Initialize(_initializationSettings, _ownerMovementSettings, _spectatorMovementSettings); + + UniversalSmoother.StartSmoother(); + + bool StartOnline() + { + NetworkBehaviour nb = _initializingNetworkBehaviour; + + SetTimeManager(nb.TimeManager); + + return true; + } + + bool StartOffline() + { + if (_timeManager == null) + return false; + + return true; + } + } + + public void StopSmoother() + { + ChangeSubscriptions(subscribe: false); + + if (!_initializedOffline) + StopOnline(); + + if (UniversalSmoother != null) + UniversalSmoother.StopSmoother(); + + void StopOnline() + { + SetTimeManager(tm: null); + } + + //Intentionally left blank. + //void StopOffline() { } + } + + public void TimeManager_OnUpdate() + { + UniversalSmoother.OnUpdate(Time.deltaTime); + } + + public void TimeManager_OnPreTick() + { + UniversalSmoother.OnPreTick(); + } + + /// + /// Called after a tick completes. + /// + public void TimeManager_OnPostTick() + { + if (_timeManager != null) + UniversalSmoother.OnPostTick(_timeManager.LocalTick); + } + + private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick) + { + UniversalSmoother.OnPostReplicateReplay(clientTick); + } + + + private void TimeManager_OnRoundTripTimeUpdated(long rttMs) + { + UniversalSmoother.UpdateRealtimeInterpolation(); + } + + /// + /// Stores smoothers if they have value. + /// + private void StoreSmoother() + { + if (UniversalSmoother == null) + return; + + ResettableObjectCaches.Store(UniversalSmoother); + UniversalSmoother = null; + } + + /// + /// Stores current smoothers and retrieves new ones. + /// + private void RetrieveSmoothers() + { + StoreSmoother(); + UniversalSmoother = ResettableObjectCaches.Retrieve(); + } + + // /// + // /// Sets a target transform to follow. + // /// + // public void SetTargetTransform(Transform value) + // { + // Transform currentTargetTransform = _initializationSettings.TargetTransform; + // + // if (value == currentTargetTransform) + // return; + // + // bool clientStartCalled = (_initializedOffline && _timeManager != null) || (_initializingNetworkBehaviour != null && _initializingNetworkBehaviour.OnStartClientCalled); + // + // bool previousTargetTransformIsValid = (currentTargetTransform != null); + // + // //If target is different and old is not null then reset. + // if (previousTargetTransformIsValid && clientStartCalled) + // OnStopClient(); + // + // _initializationSettings.TargetTransform = value; + // if (previousTargetTransformIsValid && clientStartCalled) + // OnStartClient(); + // } + + /// + /// Sets a new PredictionManager to use. + /// + public void SetTimeManager(TimeManager tm) + { + if (tm == _timeManager) + return; + + //Unsub from current. + ChangeSubscriptions(false); + //Sub to newest. + _timeManager = tm; + ChangeSubscriptions(true); + } + + /// + /// Changes the subscription to the TimeManager. + /// + private void ChangeSubscriptions(bool subscribe) + { + if (_destroyed) + return; + TimeManager tm = _timeManager; + if (tm == null) + return; + + if (subscribe == _subscribed) + return; + _subscribed = subscribe; + + bool adaptiveIsOff = (_ownerMovementSettings.AdaptiveInterpolationValue == AdaptiveInterpolationType.Off && _spectatorMovementSettings.AdaptiveInterpolationValue == AdaptiveInterpolationType.Off); + + if (subscribe) + { + tm.OnUpdate += TimeManager_OnUpdate; + tm.OnPreTick += TimeManager_OnPreTick; + tm.OnPostTick += TimeManager_OnPostTick; + + if (!adaptiveIsOff) + { + tm.OnRoundTripTimeUpdated += TimeManager_OnRoundTripTimeUpdated; + PredictionManager pm = tm.NetworkManager.PredictionManager; + pm.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay; + _subscribedToAdaptiveEvents = true; + } + } + else + { + tm.OnUpdate -= TimeManager_OnUpdate; + tm.OnPreTick -= TimeManager_OnPreTick; + tm.OnPostTick -= TimeManager_OnPostTick; + + if (_subscribedToAdaptiveEvents) + { + tm.OnRoundTripTimeUpdated -= TimeManager_OnRoundTripTimeUpdated; + PredictionManager pm = tm.NetworkManager.PredictionManager; + pm.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay; + } + } + } + + public void ResetState() + { + _initializationSettings = default; + _ownerMovementSettings = default; + _spectatorMovementSettings = default; + + _destroyed = false; + _timeManager = null; + _initializingNetworkBehaviour = null; + _graphicalTransform = null; + + _subscribed = false; + _subscribedToAdaptiveEvents = false; + + _isInitialized = false; + } + + public void InitializeState() { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs.meta new file mode 100644 index 0000000..b8abe8e --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c189fd5371510434a9a879e928422705 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/TickSmootherController.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs new file mode 100644 index 0000000..55eefdb --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs @@ -0,0 +1,937 @@ +using System; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Component.Transforming.Beta +{ + /// + /// This class is under regular development and it's API may change at any time. + /// + public sealed class UniversalTickSmoother : IResettable + { + #region Types. + [Preserve] + private struct TickTransformProperties + { + public readonly uint Tick; + public readonly TransformProperties Properties; + + public TickTransformProperties(uint tick, Transform t) + { + Tick = tick; + Properties = new(t.localPosition, t.localRotation, t.localScale); + } + + public TickTransformProperties(uint tick, Transform t, Vector3 localScale) + { + Tick = tick; + Properties = new(t.localPosition, t.localRotation, localScale); + } + + public TickTransformProperties(uint tick, TransformProperties tp) + { + Tick = tick; + Properties = tp; + } + + public TickTransformProperties(uint tick, TransformProperties tp, Vector3 localScale) + { + Tick = tick; + tp.Scale = localScale; + Properties = tp; + } + } + #endregion + + #region public. + /// + /// True if currently initialized. + /// + public bool IsInitialized { get; private set; } + #endregion + + #region Private. + /// + /// How quickly to move towards goal values. + /// + private MoveRates _moveRates = new(); + /// + /// True if a pretick occurred since last postTick. + /// + private bool _preTicked; + /// + /// World values of the graphical after it's been aligned to initialized values in PreTick. + /// + private TransformProperties _trackerPreTickWorldValues; + /// + /// World values of the graphical after it's been aligned to initialized values in PreTick. + /// + private TransformProperties _graphicsPreTickWorldValues; + /// + /// Cached value of adaptive interpolation value. + /// + private AdaptiveInterpolationType _cachedAdaptiveInterpolationValue; + /// + /// Cached value of flat interpolation value. + /// + private byte _cachedInterpolationValue; + /// + /// Cached properties to smooth of the graphical. + /// + private TransformPropertiesFlag _cachedSmoothedProperties; + /// + /// Cached value of snapping non-smoothed properties. + /// + private bool _cachedSnapNonSmoothedProperties; + /// + /// Squared distance target must travel to cause a teleport. + /// + private float _cachedTeleportThreshold; + /// + /// True if to detach on network start. + /// + private bool _detachOnStart; + /// + /// True to re-attach on network stop. + /// + private bool _attachOnStop; + /// + /// True to begin moving soon as movement data becomes available. Movement will ease in until at interpolation value. False to prevent movement until movement data count meet interpolation. + /// + private bool _moveImmediately; + /// + /// Transform the graphics should follow. + /// + private Transform _targetTransform; + /// + /// Cached value of the object to smooth. + /// + private Transform _graphicalTransform; + /// + /// Empty gameObject containing a transform which has properties checked after each simulation. + /// If the graphical starts off as nested of targetTransform then this object is created where the graphical object is. + /// Otherwise, this object is placed directly beneath targetTransform. + /// + private Transform _trackerTransform; + /// + /// TimeManager tickDelta. + /// + private float _tickDelta; + /// + /// NetworkBehaviour this is initialized for. Value may be null. + /// + private NetworkBehaviour _initializingNetworkBehaviour; + /// + /// TimeManager this is initialized for. + /// + private TimeManager _initializingTimeManager; + /// + /// Value to multiply movement by. This is used to reduce or increase the rate the movement buffer is consumed. + /// + private float _movementMultiplier = 1f; + /// + /// TransformProperties to move towards. + /// + private BasicQueue _transformProperties; + /// + /// True if to smooth using owner settings, false for spectator settings. + /// This is only used for performance gains. + /// + private bool _useOwnerSettings; + /// + /// Last tick this was teleported on. + /// + private uint _teleportedTick = TimeManager.UNSET_TICK; + /// + /// Current interpolation value, be it a flat value or adaptive. + /// + private byte _realtimeInterpolation; + /// + /// Settings to use for owners. + /// + private MovementSettings _controllerMovementSettings; + /// + /// Settings to use for spectators. + /// + private MovementSettings _spectatorMovementSettings; + /// + /// True if moving has started and has not been stopped. + /// + private bool _isMoving; + #endregion + + #region Const. + /// + /// Maximum allowed entries to be queued over the interpolation amount. + /// + private const int MAXIMUM_QUEUED_OVER_INTERPOLATION = 3; + #endregion + + [Preserve] + public UniversalTickSmoother() { } + + ~UniversalTickSmoother() + { + //This is a last resort for if something didnt deinitialize right. + ResetState(); + } + + [Obsolete("This method is no longer used. Use TrySetGraphicalTrackerLocalProperties(TransformProperties).")] //Remove V5 + public void SetGraphicalInitializedOffsetValues(TransformProperties value) { } + + [Obsolete("This method is no longer used. Use GetGraphicalTrackerLocalProperties.")] //Remove V5 + public TransformProperties GetGraphicalInitializedOffsetValues() => default; + + /// + /// Tries to set local properties for the graphical tracker transform. + /// + /// New values. + /// Returns true if the tracker has been setup and values have been applied to teh tracker transform. + /// When false is returned the values are cached and will be set when tracker is created. A cached value will be used every time the tracker is setup; to disable this behavior call this method with null value. + public bool TrySetGraphicalTrackerLocalProperties(TransformProperties? localValues) + { + if (_trackerTransform == null || localValues == null) + { + _queuedTrackerProperties = localValues; + return false; + } + + + _trackerTransform.SetLocalProperties(localValues.Value); + return true; + } + + [Obsolete("This method is no longer used. Use TrySetGraphicalTrackerLocalProperties(TransformProperties).")] //Remove V5 + public void SetAdditionalGraphicalOffsetValues(TransformProperties localValues) { } + + [Obsolete("This method is no longer used. Use GetGraphicalTrackerLocalProperties.")] //Remove V5 + public TransformProperties GetAdditionalGraphicalOffsetValues() => default; + + public TransformProperties GetGraphicalTrackerLocalProperties() + { + if (_trackerTransform != null) + return new(_trackerTransform.localPosition, _trackerTransform.localRotation, _trackerTransform.localScale); + if (_queuedTrackerProperties != null) + return _queuedTrackerProperties.Value; + + //Fall through. + NetworkManager manager = (_initializingNetworkBehaviour == null) ? null : _initializingNetworkBehaviour.NetworkManager; + manager.LogWarning($"Graphical tracker properties cannot be returned because tracker is not setup yet, and no setup properties have been specified. Use TrySetGraphicalTrackerProperties to set setup properties or call this method after IsInitialized is true."); + return default; + } + + /// + /// Properties for the tracker which are queued to be set when the tracker is setup. + /// + private TransformProperties? _queuedTrackerProperties; + + /// + /// Updates the smoothedProperties value. + /// + /// New value. + /// True if updating owner smoothing settings, or updating settings on an offline smoother. False to update spectator settings + public void SetSmoothedProperties(TransformPropertiesFlag value, bool forOwnerOrOfflineSmoother) + { + _controllerMovementSettings.SmoothedProperties = value; + SetCaches(forOwnerOrOfflineSmoother); + } + + /// + /// Updates the interpolationValue when not using adaptive interpolation. Calling this method will also disable adaptive interpolation. + /// + /// + public void SetInterpolationValue(byte value, bool forOwnerOrOfflineSmoother) => SetInterpolationValue(value, forOwnerOrOfflineSmoother, unsetAdaptiveInterpolation: true); + + /// + /// Updates the interpolationValue when not using adaptive interpolation. Calling this method will also disable adaptive interpolation. + /// + private void SetInterpolationValue(byte value, bool forOwnerOrOfflineSmoother, bool unsetAdaptiveInterpolation) + { + if (value < 1) + value = 1; + + if (forOwnerOrOfflineSmoother) + _controllerMovementSettings.InterpolationValue = value; + else + _spectatorMovementSettings.InterpolationValue = value; + + if (unsetAdaptiveInterpolation) + SetAdaptiveInterpolation(AdaptiveInterpolationType.Off, forOwnerOrOfflineSmoother); + } + + /// + /// Updates the adaptiveInterpolation value. + /// + /// New value. + public void SetAdaptiveInterpolation(AdaptiveInterpolationType value, bool forOwnerOrOfflineSmoother) + { + if (forOwnerOrOfflineSmoother) + _controllerMovementSettings.AdaptiveInterpolationValue = value; + else + _spectatorMovementSettings.AdaptiveInterpolationValue = value; + + UpdateRealtimeInterpolation(); + } + + public void Initialize(InitializationSettings initializationSettings, MovementSettings ownerSettings, MovementSettings spectatorSettings) + { + ResetState(); + + Transform graphicalTransform = initializationSettings.GraphicalTransform; + Transform targetTransform = initializationSettings.TargetTransform; + + if (!TransformsAreValid(graphicalTransform, targetTransform)) + return; + + _transformProperties = CollectionCaches.RetrieveBasicQueue(); + _controllerMovementSettings = ownerSettings; + _spectatorMovementSettings = spectatorSettings; + + /* Unset scale smoothing if not detaching. This is to prevent + * the scale from changing with the parent if nested, as that + * would result in the scale being modified twice, once on the parent + * and once on the graphical. Thanks deo_wh for find! */ + if (!initializationSettings.DetachOnStart) + { + _controllerMovementSettings.SmoothedProperties &= ~TransformPropertiesFlag.Scale; + _spectatorMovementSettings.SmoothedProperties &= ~TransformPropertiesFlag.Scale; + } + + _initializingNetworkBehaviour = initializationSettings.InitializingNetworkBehaviour; + _initializingTimeManager = initializationSettings.InitializingTimeManager; + _targetTransform = targetTransform; + _graphicalTransform = graphicalTransform; + _tickDelta = (float)initializationSettings.InitializingTimeManager.TickDelta; + _detachOnStart = initializationSettings.DetachOnStart; + _attachOnStop = initializationSettings.AttachOnStop; + _moveImmediately = initializationSettings.MoveImmediately; + + SetCaches(GetUseOwnerSettings()); + + //Use set method as it has sanity checks. + SetInterpolationValue(_controllerMovementSettings.InterpolationValue, forOwnerOrOfflineSmoother: true, unsetAdaptiveInterpolation: false); + SetInterpolationValue(_spectatorMovementSettings.InterpolationValue, forOwnerOrOfflineSmoother: false, unsetAdaptiveInterpolation: false); + + SetAdaptiveInterpolation(_controllerMovementSettings.AdaptiveInterpolationValue, forOwnerOrOfflineSmoother: true); + SetAdaptiveInterpolation(_spectatorMovementSettings.AdaptiveInterpolationValue, forOwnerOrOfflineSmoother: false); + + SetupTrackerTransform(); + /* This is called after setting up the tracker transform in the scenario + * the user set additional offsets before this was initialized. */ + if (_queuedTrackerProperties != null) + TrySetGraphicalTrackerLocalProperties(_queuedTrackerProperties.Value); + + void SetupTrackerTransform() + { + _trackerTransform = new GameObject($"{_graphicalTransform.name}_Tracker").transform; + + if (_detachOnStart) + { + _trackerTransform.SetParent(_targetTransform); + } + else + { + Transform trackerParent = _graphicalTransform.IsChildOf(targetTransform) ? _graphicalTransform.parent : targetTransform; + _trackerTransform.SetParent(trackerParent); + } + + _trackerTransform.SetLocalPositionRotationAndScale(_graphicalTransform.localPosition, graphicalTransform.localRotation, graphicalTransform.localScale); + } + + IsInitialized = true; + } + + /// + /// Returns if configured transforms are valid. + /// + /// + private bool TransformsAreValid(Transform graphicalTransform, Transform targetTransform) + { + if (graphicalTransform == null) + { + NetworkManagerExtensions.LogError($"Graphical transform cannot be null."); + return false; + } + if (targetTransform == null) + { + NetworkManagerExtensions.LogError($"Target transform on {graphicalTransform} cannot be null."); + return false; + } + if (targetTransform == graphicalTransform) + { + NetworkManagerExtensions.LogError($"Target transform cannot be the same as graphical transform on {graphicalTransform}."); + return false; + } + + return true; + } + + /// + /// Returns true if to use adaptive interpolation. + /// + /// + private bool GetUseAdaptiveInterpolation() + { + if (_cachedAdaptiveInterpolationValue == AdaptiveInterpolationType.Off || _initializingTimeManager.NetworkManager.IsServerOnlyStarted) + return false; + + return true; + } + + /// + /// Gets if to use owner values. + /// + /// OwnerSettings can be used to read determine this as both owner and spectator settings will have the name InitializingNetworkBehaviour. + /// + private bool GetUseOwnerSettings() => (_initializingNetworkBehaviour == null) || _initializingNetworkBehaviour.IsOwner || !_initializingNetworkBehaviour.Owner.IsValid; + + /// + /// Updates OwnerDuringPreTick value and caches if needed. + /// + private void SetUseOwnerSettings(bool value, bool force = false) + { + if (value == _useOwnerSettings && !force) + return; + + _useOwnerSettings = value; + + SetCaches(value); + } + + /// + /// Updates OwnerDuringPreTick value and caches if needed. + /// + private void SetCaches(bool useOwnerSettings) + { + MovementSettings movementSettings = (useOwnerSettings) ? _controllerMovementSettings : _spectatorMovementSettings; + + _cachedSmoothedProperties = movementSettings.SmoothedProperties; + _cachedSnapNonSmoothedProperties = movementSettings.SnapNonSmoothedProperties; + _cachedAdaptiveInterpolationValue = movementSettings.AdaptiveInterpolationValue; + _cachedInterpolationValue = movementSettings.InterpolationValue; + + _cachedTeleportThreshold = (movementSettings.EnableTeleport) ? (movementSettings.TeleportThreshold * movementSettings.TeleportThreshold) : MoveRates.UNSET_VALUE; + } + + /// + /// Deinitializes this smoother resetting values. + /// + public void Deinitialize() + { + ResetState(); + IsInitialized = false; + } + + /// + /// Updates interpolation based on localClient latency when using adaptive interpolation, or uses set value when adaptive interpolation is off. + /// + public void UpdateRealtimeInterpolation() + { + /* If not networked, server is started, or if not + * using adaptive interpolation then use + * flat interpolation.*/ + if (!GetUseAdaptiveInterpolation()) + { + _realtimeInterpolation = _cachedInterpolationValue; + return; + } + + /* If here then adaptive interpolation is being calculated. */ + + TimeManager tm = _initializingTimeManager; + + //Calculate roughly what client state tick would be. + uint localTick = tm.LocalTick; + //This should never be the case; this is a precautionary against underflow. + if (localTick == TimeManager.UNSET_TICK) + return; + + //Ensure at least 1 tick. + long rttTime = tm.RoundTripTime; + uint rttTicks = tm.TimeToTicks(rttTime) + 1; + + uint clientStateTick = (localTick - rttTicks); + float interpolation = (localTick - clientStateTick); + + //Minimum interpolation is that of adaptive interpolation level. + interpolation += (byte)_cachedAdaptiveInterpolationValue; + + //Ensure interpolation is not more than a second. + if (interpolation > tm.TickRate) + interpolation = tm.TickRate; + else if (interpolation > byte.MaxValue) + interpolation = byte.MaxValue; + + /* Only update realtime interpolation if it changed more than 1 + * tick. This is to prevent excessive changing of interpolation value, which + * could result in noticeable speed ups/slow downs given movement multiplier + * may change when buffer is too full or short. */ + if (_realtimeInterpolation == 0 || Math.Abs(_realtimeInterpolation - interpolation) > 1) + _realtimeInterpolation = (byte)Math.Ceiling(interpolation); + } + + /// + /// This should be called when OnStartClient is invoked on the initializing NetworkBehaviour. + /// + /// This does not need to be called if there is no initializing NetworkBehaviour. + public void StartSmoother() + { + DetachOnStart(); + } + + /// + /// This should be called when OnStopClient is invoked on the initializing NetworkBehaviour. + /// + /// This does not need to be called if there is no initializing NetworkBehaviour. + internal void StopSmoother() + { + AttachOnStop(); + } + + /// + /// Called every frame. + /// + public void OnUpdate(float delta) + { + if (!CanSmooth()) + return; + + MoveToTarget(delta); + } + + /// + /// Called when the TimeManager invokes OnPreTick. + /// + public void OnPreTick() + { + if (!CanSmooth()) + return; + + SetUseOwnerSettings(GetUseOwnerSettings()); + + _preTicked = true; + DiscardExcessiveTransformPropertiesQueue(); + _graphicsPreTickWorldValues = _graphicalTransform.GetWorldProperties(); + _trackerPreTickWorldValues = GetTrackerWorldProperties(); + } + + /// + /// Called when the TimeManager invokes OnPostReplay. + /// + /// Replay tick for the local client. + /// This is dependent on the initializing NetworkBehaviour being set. + public void OnPostReplicateReplay(uint clientTick) + { + if (!NetworkObjectIsReconciling()) + return; + + if (_transformProperties.Count == 0) + return; + if (clientTick <= _teleportedTick) + return; + uint firstTick = _transformProperties.Peek().Tick; + //Already in motion to first entry, or first entry passed tick. + if (clientTick <= firstTick) + return; + + ModifyTransformProperties(clientTick, firstTick); + } + + /// + /// Called when TimeManager invokes OnPostTick. + /// + /// Local tick of the client. + public void OnPostTick(uint clientTick) + { + if (!CanSmooth()) + return; + if (clientTick <= _teleportedTick) + return; + + //If preticked then previous transform values are known. + if (_preTicked) + { + DiscardExcessiveTransformPropertiesQueue(); + + //Only needs to be put to pretick position if not detached. + if (!_detachOnStart) + _graphicalTransform.SetWorldProperties(_graphicsPreTickWorldValues); + + //SnapNonSmoothedProperties(); + AddTransformProperties(clientTick); + } + //If did not pretick then the only thing we can do is snap to instantiated values. + else + { + //Only set to position if not to detach. + if (!_detachOnStart) + _graphicalTransform.SetWorldProperties(GetTrackerWorldProperties()); + } + } + + /// + /// Snaps non-smoothed properties to original positoin if setting is enabled. + /// + private void SnapNonSmoothedProperties() + { + //Feature is not enabled. + if (!_cachedSnapNonSmoothedProperties) + return; + + TransformPropertiesFlag smoothedProperties = _cachedSmoothedProperties; + + //Everything is smoothed. + if (smoothedProperties == TransformPropertiesFlag.Everything) + return; + + TransformProperties goalValeus = GetTrackerWorldProperties(); + + if (!smoothedProperties.FastContains(TransformPropertiesFlag.Position)) + _graphicalTransform.position = goalValeus.Position; + if (!smoothedProperties.FastContains(TransformPropertiesFlag.Rotation)) + _graphicalTransform.rotation = goalValeus.Rotation; + if (!smoothedProperties.FastContains(TransformPropertiesFlag.Scale)) + _graphicalTransform.localScale = goalValeus.Scale; + } + + /// + /// Returns if the initialized NetworkBehaviour's NetworkObject is reconcilling. + /// + private bool NetworkObjectIsReconciling() => (_initializingNetworkBehaviour == null || _initializingNetworkBehaviour.NetworkObject.IsObjectReconciling); + + /// + /// Teleports the graphical to it's starting position and clears the internal movement queue. + /// + public void Teleport() + { + if (_initializingTimeManager == null) + return; + + //If using adaptive interpolation then set the tick which was teleported. + if (_controllerMovementSettings.AdaptiveInterpolationValue != AdaptiveInterpolationType.Off) + { + TimeManager tm = (_initializingTimeManager == null) ? InstanceFinder.TimeManager : _initializingTimeManager; + if (tm != null) + _teleportedTick = tm.LocalTick; + } + + ClearTransformPropertiesQueue(); + + _graphicalTransform.SetWorldProperties(_trackerTransform.GetWorldProperties()); + } + + /// + /// Clears the pending movement queue. + /// + private void ClearTransformPropertiesQueue() + { + _transformProperties.Clear(); + //Also unset move rates since there is no more queue. + _moveRates = new(MoveRates.UNSET_VALUE); + } + + /// + /// Discards datas over interpolation limit from movement queue. + /// + private void DiscardExcessiveTransformPropertiesQueue() + { + int propertiesCount = _transformProperties.Count; + int dequeueCount = (propertiesCount - (_realtimeInterpolation + MAXIMUM_QUEUED_OVER_INTERPOLATION)); + + //If there are entries to dequeue. + if (dequeueCount > 0) + { + TickTransformProperties tpp = default; + for (int i = 0; i < dequeueCount; i++) + tpp = _transformProperties.Dequeue(); + + SetMoveRates(tpp.Properties); + } + } + + /// + /// Adds a new transform properties and sets move rates if needed. + /// + private void AddTransformProperties(uint tick) + { + TickTransformProperties tpp = new(tick, GetTrackerWorldProperties()); + _transformProperties.Enqueue(tpp); + + //If first entry then set move rates. + if (_transformProperties.Count == 1) + { + TransformProperties gfxWorldProperties = _graphicalTransform.GetWorldProperties(); + SetMoveRates(gfxWorldProperties); + } + } + + /// + /// Modifies a transform property for a tick. This does not error check for empty collections. + /// + /// First tick in the queue. If 0 this will be looked up. + private void ModifyTransformProperties(uint clientTick, uint firstTick) + { + int queueCount = _transformProperties.Count; + uint tick = clientTick; + /*Ticks will always be added incremental by 1 so it's safe to jump ahead the difference + * of tick and firstTick. */ + int index = (int)(tick - firstTick); + //Replace with new data. + if (index < queueCount) + { + if (tick != _transformProperties[index].Tick) + { + //Should not be possible. + } + else + { + TransformProperties newProperties = GetTrackerWorldProperties(); + /* Adjust transformProperties to ease into any corrections. + * The corrected value is used the more the index is to the end + * of the queue. */ + /* We want to be fully eased in by the last entry of the queue. */ + + int lastPossibleIndex = (queueCount - 1); + int adjustedQueueCount = (lastPossibleIndex - 1); + if (adjustedQueueCount < 1) + adjustedQueueCount = 1; + float easePercent = ((float)index / adjustedQueueCount); + + //If easing. + if (easePercent < 1f) + { + if (easePercent < 1f) + easePercent = (float)Math.Pow(easePercent, (adjustedQueueCount - index)); + + TransformProperties oldProperties = _transformProperties[index].Properties; + newProperties.Position = Vector3.Lerp(oldProperties.Position, newProperties.Position, easePercent); + newProperties.Rotation = Quaternion.Lerp(oldProperties.Rotation, newProperties.Rotation, easePercent); + newProperties.Scale = Vector3.Lerp(oldProperties.Scale, newProperties.Scale, easePercent); + } + + _transformProperties[index] = new(tick, newProperties); + } + } + else + { + //This should never happen. + } + } + + /// + /// Gets properties of the tracker. + /// + private TransformProperties GetTrackerWorldProperties() + { + /* Return lossyScale if graphical is not attached. Otherwise, + * graphical should retain the tracker localScale so it changes + * with root. */ + + Vector3 scale = (_detachOnStart) ? _trackerTransform.lossyScale : _trackerTransform.localScale; + return new(_trackerTransform.position, _trackerTransform.rotation, scale); + } + + /// + /// Returns if prediction can be used on this rigidbody. + /// + /// + private bool CanSmooth() + { + //No graphical object is set. + if (_graphicalTransform == null) + return false; + + return _initializingTimeManager.NetworkManager.IsClientStarted; + } + + /// + /// Sets new rates based on next entries in transformProperties queue, against a supplied TransformProperties. + /// + private void SetMoveRates(in TransformProperties prevValues) + { + if (_transformProperties.Count == 0) + { + _moveRates = new(MoveRates.UNSET_VALUE); + return; + } + + TransformProperties nextValues = _transformProperties.Peek().Properties; + + float duration = _tickDelta; + + _moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, _cachedTeleportThreshold); + _moveRates.TimeRemaining = duration; + + SetMovementMultiplier(); + } + + private void SetMovementMultiplier() + { + if (_moveImmediately) + { + float percent = Mathf.InverseLerp(0, _realtimeInterpolation, _transformProperties.Count); + _movementMultiplier = percent; + + _movementMultiplier = Mathf.Clamp(_movementMultiplier, 0.5f, 1.05f); + } + //For the time being, not moving immediately uses these multiplier calculations. + else + { + /* If there's more in queue than interpolation then begin to move faster based on overage. + * Move 5% faster for every overage. */ + int overInterpolation = (_transformProperties.Count - _realtimeInterpolation); + //If needs to be adjusted. + if (overInterpolation != 0) + { + _movementMultiplier += (0.015f * overInterpolation); + } + //If does not need to be adjusted. + else + { + //If interpolation is 1 then slow down just barely to accomodate for frame delta variance. + if (_realtimeInterpolation == 1) + _movementMultiplier = 1f; + } + + _movementMultiplier = Mathf.Clamp(_movementMultiplier, 0.95f, 1.05f); + } + } + + /// + /// Moves transform to target values. + /// + private void MoveToTarget(float delta) + { + int tpCount = _transformProperties.Count; + + //No data. + if (tpCount == 0) + return; + + if (_moveImmediately) + { + _isMoving = true; + } + else + { + //Enough in buffer to move. + if (tpCount >= _realtimeInterpolation) + { + _isMoving = true; + } + else if (!_isMoving) + { + return; + } + /* If buffer is considerably under goal then halt + * movement. This will allow the buffer to grow. */ + else if ((tpCount - _realtimeInterpolation) < -4) + { + _isMoving = false; + return; + } + } + + TickTransformProperties ttp = _transformProperties.Peek(); + + TransformPropertiesFlag smoothedProperties = _cachedSmoothedProperties; + + _moveRates.Move(_graphicalTransform, ttp.Properties, smoothedProperties, (delta * _movementMultiplier), useWorldSpace: true); + + float tRemaining = _moveRates.TimeRemaining; + //if TimeLeft is <= 0f then transform is at goal. Grab a new goal if possible. + if (tRemaining <= 0f) + { + //Dequeue current entry and if there's another call a move on it. + _transformProperties.Dequeue(); + + //If there are entries left then setup for the next. + if (_transformProperties.Count > 0) + { + SetMoveRates(ttp.Properties); + //If delta is negative then call move again with abs. + if (tRemaining < 0f) + MoveToTarget(Mathf.Abs(tRemaining)); + } + //No remaining, set to snap. + else + { + ClearTransformPropertiesQueue(); + } + } + } + + private void DetachOnStart() + { + if (!_detachOnStart) + return; + + TransformProperties gfxWorldProperties = _graphicalTransform.GetWorldProperties(); + _graphicalTransform.SetParent(null); + _graphicalTransform.SetWorldProperties(gfxWorldProperties); + } + + /// + /// Attachs to Target transform is possible. + /// + private void AttachOnStop() + { + //Never detached. + if (!_detachOnStart) + return; + //Graphical is null, nothing can be moved. + if (_graphicalTransform == null) + return; + if (ApplicationState.IsQuitting()) + return; + + /* If not to re-attach or if there's no target to reference + * then the graphical must be destroyed. */ + bool destroy = !_attachOnStop || (_targetTransform == null); + //If not to re-attach then destroy graphical if needed. + if (destroy) + { + UnityEngine.Object.Destroy(_graphicalTransform.gameObject); + return; + } + + _graphicalTransform.SetParent(_targetTransform.parent); + _graphicalTransform.SetLocalProperties(_trackerTransform.GetLocalProperties()); + } + + public void ResetState() + { + if (!IsInitialized) + return; + + AttachOnStop(); + + _initializingNetworkBehaviour = null; + _initializingTimeManager = null; + _graphicalTransform = null; + _targetTransform = null; + + _teleportedTick = TimeManager.UNSET_TICK; + _movementMultiplier = 1f; + CollectionCaches.StoreAndDefault(ref _transformProperties); + _moveRates = default; + _preTicked = default; + _queuedTrackerProperties = null; + _trackerPreTickWorldValues = default; + _graphicsPreTickWorldValues = default; + _realtimeInterpolation = default; + _isMoving = default; + + if (_trackerTransform != null) + UnityEngine.Object.Destroy(_trackerTransform.gameObject); + } + + public void InitializeState() { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs.meta new file mode 100644 index 0000000..f06b6b4 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3c9b47c92bed992428c9dbcb60f80916 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/TickSmoothing/UniversalTickSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility.meta b/Assets/FishNet/Runtime/Generated/Component/Utility.meta new file mode 100644 index 0000000..b63102d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a658654fb5a277445af5f1628ae61d88 +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..16c0584 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs @@ -0,0 +1,295 @@ +using FishNet.Managing.Statistic; +using GameKit.Dependencies.Utilities.Types; +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 + } + + public class InOutAverage + { + private RingBuffer _in; + private RingBuffer _out; + + public InOutAverage(byte seconds) + { + _in = new(seconds); + _out = new(seconds); + } + + public void AddIn(ulong value) => _in.Add(value); + public void AddOut(ulong value) => _out.Add(value); + + public ulong GetAverage(bool inAverage) + { + RingBuffer buffer = (inAverage) ? _in : _out; + + int count = buffer.Count; + if (count == 0) + return 0; + + ulong total = 0; + foreach (ulong v in buffer) + total += v; + + return (total / (uint)count); + } + + public void ResetState() + { + _in.Clear(); + _out.Clear(); + } + + public void InitializeState(int capacity) + { + _in.Initialize(capacity); + _out.Initialize(capacity); + } + } + #endregion + + #region Public. +#if UNITY_EDITOR || !UNITY_SERVER + /// + /// Averages for client. + /// + public InOutAverage ClientAverages { get; private set; } + /// + /// Averages for server. + /// + public InOutAverage ServerAverages { get; private set; } +#endif + #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; + /// + /// Number of seconds used to gather data per second. Lower values will show more up to date usage per second while higher values provide a better over-all estimate. + /// + [Tooltip("Number of seconds used to gather data per second. Lower values will show more up to date usage per second while higher values provide a better over-all estimate.")] + [SerializeField] + [Range(1, byte.MaxValue)] + private byte _secondsAveraged = 1; + + /// + /// 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 + +#if UNITY_EDITOR || !UNITY_SERVER + + #region Private. + /// + /// Style for drawn ping. + /// + private readonly GUIStyle _style = new(); + /// + /// 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() + { + SetSecondsAveraged(_secondsAveraged); + + _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_OnServerNetworkTraffic; + } + } + + /// + /// Sets a new number of seconds to average from. + /// + public void SetSecondsAveraged(byte seconds) + { + if (seconds <= 0) + seconds = 1; + + ClientAverages = new(seconds); + ServerAverages = new(seconds); + } + + /// + /// Called when client network traffic is updated. + /// + private void NetworkTraffic_OnClientNetworkTraffic(NetworkTrafficArgs obj) + { + string nl = System.Environment.NewLine; + string result = string.Empty; + + ClientAverages.AddIn(obj.FromServerBytes); + ClientAverages.AddOut(obj.ToServerBytes); + + if (_showIncoming) + result += $"Client In: {NetworkTraficStatistics.FormatBytesToLargest(ClientAverages.GetAverage(inAverage: true))}/s{nl}"; + if (_showOutgoing) + result += $"Client Out: {NetworkTraficStatistics.FormatBytesToLargest(ClientAverages.GetAverage(inAverage: false))}/s{nl}"; + + _clientText = result; + } + + /// + /// Called when server network traffic is updated. + /// + private void NetworkTraffic_OnServerNetworkTraffic(NetworkTrafficArgs obj) + { + string nl = System.Environment.NewLine; + string result = string.Empty; + + ServerAverages.AddIn(obj.ToServerBytes); + ServerAverages.AddOut(obj.FromServerBytes); + + if (_showIncoming) + result += $"Server In: {NetworkTraficStatistics.FormatBytesToLargest(ServerAverages.GetAverage(inAverage: true))}/s{nl}"; + if (_showOutgoing) + result += $"Server Out: {NetworkTraficStatistics.FormatBytesToLargest(ServerAverages.GetAverage(inAverage: false))}/s{nl}"; + + _serverText = result; + } + + private void OnGUI() + { + _style.normal.textColor = _color; + _style.fontSize = 15; + + float width = 100f; + float height = 0f; + if (_showIncoming) + height += 15f; + if (_showOutgoing) + height += 15f; + + bool isClient = InstanceFinder.IsClientStarted; + bool isServer = InstanceFinder.IsServerStarted; + if (!isClient) + ResetCalculationsAndDisplay(forServer: false); + if (!isServer) + ResetCalculationsAndDisplay(forServer: true); + 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(horizontal, vertical, width, height), (_clientText + _serverText), _style); + } + + [ContextMenu("Reset Averages")] + public void ResetAverages() + { + ResetCalculationsAndDisplay(forServer: true); + ResetCalculationsAndDisplay(forServer: false); + } + + private void ResetCalculationsAndDisplay(bool forServer) + { + if (forServer) + { + _serverText = string.Empty; + ServerAverages.ResetState(); + } + else + { + _clientText = string.Empty; + ClientAverages.ResetState(); + } + } +#endif + } +} \ 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..b7e2378 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8bc8f0363ddc75946a958043c5e49a83 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/BandwidthDisplay.cs + uploadId: 762203 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..a3f7a6d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs @@ -0,0 +1,250 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Scened; +using FishNet.Transporting; +using FishNet.Utility; +using GameKit.Dependencies.Utilities; +using GameKit.Dependencies.Utilities.Types; +using System.IO; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.Serialization; +using UnitySceneManager = UnityEngine.SceneManagement.SceneManager; + +namespace FishNet.Component.Scenes +{ + + /// + /// 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 _enableGlobalScenes = 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 OnEnable() + { + Initialize(); + } + + private void OnDestroy() + { + Deinitialize(); + } + + /// + /// Initializes this script for use. + /// + private void Initialize() + { + _networkManager = GetComponentInParent(); + if (_networkManager == null) + { + NetworkManagerExtensions.LogError($"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) + { + + NetworkManagerExtensions.LogWarning("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(); + } + + private void Deinitialize() + { + 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; + } + } + + /// + /// 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.IsOnlyOneServerStarted()) + return; + + //If here can load scene. + SceneLoadData sld = new(GetSceneName(_onlineScene)); + sld.ReplaceScenes = _replaceScenes; + if (_enableGlobalScenes) + _networkManager.SceneManager.LoadGlobalScenes(sld); + else + _networkManager.SceneManager.LoadConnectionScenes(sld); + } + //When server stops load offline scene. + else if (obj.ConnectionState == LocalConnectionState.Stopped && !_networkManager.ServerManager.IsAnyServerStarted()) + { + 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.IsServerStarted) + 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 (_enableGlobalScenes) + return; + if (!authenticated) + return; + + SceneLoadData sld = new(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); + } + } + +} \ No newline at end of file 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..3509c87 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 57ce8bbb58966cb45a7140f32da5327a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/DefaultScene.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs new file mode 100644 index 0000000..b6f111b --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs @@ -0,0 +1,254 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Component.Transforming +{ + /// + /// Detaches the object which this component resides and follows another. + /// + public class DetachableNetworkTickSmoother : NetworkBehaviour + { + #region Serialized. + /// + /// True to attach the object to it's original parent when OnStopClient is called. + /// + [Tooltip("True to attach the object to it's original parent when OnStopClient is called.")] + [SerializeField] + private bool _attachOnStop = true; + + /// + /// Object to follow, and smooth towards. + /// + [Tooltip("Object to follow, and smooth towards.")] + [SerializeField] + private Transform _followObject; + /// + /// How many ticks to interpolate over. + /// + [Tooltip("How many ticks to interpolate over.")] + [Range(1, byte.MaxValue)] + [SerializeField] + private byte _interpolation = 1; + /// + /// True to enable teleport threshhold. + /// + [Tooltip("True to enable teleport threshold.")] + [SerializeField] + private bool _enableTeleport; + /// + /// How far the object must move between ticks to teleport rather than smooth. + /// + [Tooltip("How far the object must move between ticks to teleport rather than smooth.")] + [Range(0f, ushort.MaxValue)] + [SerializeField] + private float _teleportThreshold; + + /// + /// True to synchronize the position of the followObject. + /// + [Tooltip("True to synchronize the position of the followObject.")] + [SerializeField] + private bool _synchronizePosition = true; + /// + /// True to synchronize the rotation of the followObject. + /// + [Tooltip("True to synchronize the rotation of the followObject.")] + [SerializeField] + private bool _synchronizeRotation; + /// + /// True to synchronize the scale of the followObject. + /// + [Tooltip("True to synchronize the scale of the followObject.")] + [SerializeField] + private bool _synchronizeScale; + #endregion + + #region Private. + /// + /// TimeManager subscribed to. + /// + private TimeManager _timeManager; + /// + /// Parent of the object prior to detaching. + /// + private Transform _parent; + /// + /// Local properties of the graphical during instantation. + /// + private TransformProperties _transformInstantiatedLocalProperties; + /// + /// World properties of the followObject during post tick. + /// + private TransformProperties _postTickFollowObjectWorldProperties; + + /// + /// How quickly to move towards target. + /// + private MoveRates _moveRates = new(MoveRates.INSTANT_VALUE); + /// + /// True if initialized. + /// + private bool _initialized; + /// + /// Cached TickDelta of the TimeManager. + /// + private float _tickDelta; + #endregion + + private void Awake() + { + _transformInstantiatedLocalProperties = transform.GetLocalProperties(); + } + + private void OnDestroy() + { + ChangeSubscription(false); + } + + public override void OnStartClient() + { + bool error = false; + if (transform.parent == null) + { + NetworkManagerExtensions.LogError($"{GetType().Name} on gameObject {gameObject.name} requires a parent to detach from."); + error = true; + } + if (_followObject == null) + { + NetworkManagerExtensions.LogError($"{GetType().Name} on gameObject {gameObject}, root {transform.root} requires followObject to be set."); + error = true; + } + + if (error) + return; + + _parent = transform.parent; + transform.SetParent(null); + + SetTimeManager(base.TimeManager); + //Unsub first in the rare chance we already subbed such as a stop callback issue. + ChangeSubscription(false); + ChangeSubscription(true); + + _postTickFollowObjectWorldProperties = _followObject.GetWorldProperties(); + _tickDelta = (float)base.TimeManager.TickDelta; + _initialized = true; + } + + public override void OnStopClient() + { +#if UNITY_EDITOR + if (ApplicationState.IsQuitting()) + return; +#endif + //Reattach to parent. + if (_attachOnStop && _parent != null) + { + //Reparent + transform.SetParent(_parent); + //Set to instantiated local values. + transform.SetLocalProperties(_transformInstantiatedLocalProperties); + } + + _postTickFollowObjectWorldProperties.ResetState(); + ChangeSubscription(false); + + _initialized = false; + } + + [Client(Logging = LoggingType.Off)] + private void Update() + { + MoveTowardsFollowTarget(); + } + + /// + /// Called after a tick completes. + /// + private void _timeManager_OnPostTick() + { + if (!_initialized) + return; + + _postTickFollowObjectWorldProperties.Update(_followObject); + //Unset values if not following the transform property. + if (!_synchronizePosition) + _postTickFollowObjectWorldProperties.Position = transform.position; + if (!_synchronizeRotation) + _postTickFollowObjectWorldProperties.Rotation = transform.rotation; + if (!_synchronizeScale) + _postTickFollowObjectWorldProperties.Scale = transform.localScale; + SetMoveRates(); + } + + /// + /// Sets a new PredictionManager to use. + /// + /// + private void SetTimeManager(TimeManager tm) + { + if (tm == _timeManager) + return; + + //Unsub from current. + ChangeSubscription(false); + //Sub to newest. + _timeManager = tm; + ChangeSubscription(true); + } + + + /// + /// Changes the subscription to the TimeManager. + /// + private void ChangeSubscription(bool subscribe) + { + if (_timeManager == null) + return; + + if (subscribe) + _timeManager.OnPostTick += _timeManager_OnPostTick; + else + _timeManager.OnPostTick -= _timeManager_OnPostTick; + } + + /// + /// Moves towards targetObject. + /// + private void MoveTowardsFollowTarget() + { + if (!_initialized) + return; + + _moveRates.Move(transform, _postTickFollowObjectWorldProperties, Time.deltaTime, useWorldSpace: true); + } + + private void SetMoveRates() + { + if (!_initialized) + return; + + float duration = (_tickDelta * _interpolation); + /* If interpolation is 1 then add on a tiny amount + * of more time to compensate for frame time, so that + * the smoothing does not complete before the next tick, + * as this would result in jitter. */ + if (_interpolation == 1) + duration += Mathf.Max(Time.deltaTime, (1f / 50f)); + + float teleportT = (_enableTeleport) ? _teleportThreshold : MoveRates.UNSET_VALUE; + _moveRates = MoveRates.GetWorldMoveRates(transform, _followObject, duration, teleportT); + } + + + } + + +} + diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs.meta new file mode 100644 index 0000000..67011ae --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c631fa10037fa844292bacd140d7c7f9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/DetachableNetworkTickSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/Editor.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/Editor.meta new file mode 100644 index 0000000..28cdeb9 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: edb137ec1a2c56540a187929d6b97b54 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs new file mode 100644 index 0000000..df6c83d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs @@ -0,0 +1,78 @@ +#if UNITY_EDITOR +using GameKit.Dependencies.Utilities; +using UnityEditor; +using LayoutTools = GameKit.Dependencies.Utilities.EditorGuiLayoutTools; + +namespace FishNet.Component.Transforming.Editing +{ + + + [CustomEditor(typeof(DetachableNetworkTickSmoother), true)] + [CanEditMultipleObjects] + public class DetachableNetworkTickSmootherEditor : Editor + { + private SerializedProperty _attachOnStop; + private SerializedProperty _followObject; + private SerializedProperty _interpolation; + private SerializedProperty _enableTeleport; + private SerializedProperty _teleportThreshold; + private SerializedProperty _synchronizePosition; + private SerializedProperty _synchronizeRotation; + private SerializedProperty _synchronizeScale; + + protected virtual void OnEnable() + { + _attachOnStop = serializedObject.FindProperty(nameof(_attachOnStop)); + _followObject = serializedObject.FindProperty(nameof(_followObject)); + _interpolation = serializedObject.FindProperty(nameof(_interpolation)); + _enableTeleport = serializedObject.FindProperty(nameof(_enableTeleport)); + _teleportThreshold = serializedObject.FindProperty(nameof(_teleportThreshold)); + _synchronizePosition = serializedObject.FindProperty(nameof(_synchronizePosition)); + _synchronizeRotation = serializedObject.FindProperty(nameof(_synchronizeRotation)); + _synchronizeScale = serializedObject.FindProperty(nameof(_synchronizeScale)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + LayoutTools.AddObjectField("Script:", MonoScript.FromMonoBehaviour((DetachableNetworkTickSmoother)target), typeof(DetachableNetworkTickSmoother), false, EditorLayoutEnableType.Disabled); + + EditorGUILayout.HelpBox("This component will be obsoleted soon. Use NetworkTickSmoother or OfflineTickSmoother.", MessageType.Warning); + //Misc. + EditorGUILayout.LabelField("Misc", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_attachOnStop); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Smoothing. + EditorGUILayout.LabelField("Smoothing", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_followObject); + EditorGUILayout.PropertyField(_interpolation); + + EditorGUILayout.PropertyField(_enableTeleport); + if (_enableTeleport.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_teleportThreshold); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + //Authority. + EditorGUILayout.LabelField("Synchronizing", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_synchronizePosition); + EditorGUILayout.PropertyField(_synchronizeRotation); + EditorGUILayout.PropertyField(_synchronizeScale); + EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs.meta new file mode 100644 index 0000000..2f65df6 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 342594fe005d75a4985e1a8ca218f822 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/Editor/DetachableNetworkTickSmootherEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs b/Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs new file mode 100644 index 0000000..9e8605d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs @@ -0,0 +1,156 @@ +using FishNet.Managing.Logging; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Object.Prediction; +using GameKit.Dependencies.Utilities; +using UnityEngine; +#pragma warning disable CS0618 // Type or member is obsolete + +namespace FishNet.Component.Transforming +{ + /// + /// Smoothes an object between ticks. + /// This can be used on objects without NetworkObject components. + /// + public class MonoTickSmoother : MonoBehaviour + { + //Lazy way to display obsolete message w/o using a custom editor. + [Header("This component will be obsoleted soon.")] + [Header("Use NetworkTickSmoother or OfflineTickSmoother.")] + [Header(" ")] + + #region Serialized. + /// + /// True to use InstanceFinder to locate the TimeManager. When false specify which TimeManager to use by calling SetTimeManager. + /// + [Tooltip("True to use InstanceFinder to locate the TimeManager. When false specify which TimeManager to use by calling SetTimeManager.")] + [SerializeField] + private bool _useInstanceFinder = true; + /// + /// GraphicalObject you wish to smooth. + /// + [Tooltip("GraphicalObject you wish to smooth.")] + [SerializeField] + private Transform _graphicalObject; + /// + /// True to enable teleport threshhold. + /// + [Tooltip("True to enable teleport threshold.")] + [SerializeField] + private bool _enableTeleport; + /// + /// How far the object must move between ticks to teleport rather than smooth. + /// + [Tooltip("How far the object must move between ticks to teleport rather than smooth.")] + [Range(0f, ushort.MaxValue)] + [SerializeField] + private float _teleportThreshold; + #endregion + + #region Private. + /// + /// TimeManager subscribed to. + /// + private TimeManager _timeManager; + /// + /// BasicTickSmoother for this script. + /// + private LocalTransformTickSmoother _tickSmoother; + #endregion + + private void OnEnable() + { + Initialize(); + } + + private void OnDisable() + { + _tickSmoother.ResetState(); + ChangeSubscription(false); + ObjectCaches.StoreAndDefault(ref _tickSmoother); + } + + [Client(Logging = LoggingType.Off)] + private void Update() + { + _tickSmoother?.Update(); + } + + /// + /// Initializes this script for use. + /// + private void Initialize() + { + _tickSmoother = ObjectCaches.Retrieve(); + if (_useInstanceFinder) + { + _timeManager = InstanceFinder.TimeManager; + ChangeSubscription(true); + } + } + + /// + /// Sets a new PredictionManager to use. + /// + /// + public void SetTimeManager(TimeManager tm) + { + if (tm == _timeManager) + return; + + //Unsub from current. + ChangeSubscription(false); + //Sub to newest. + _timeManager = tm; + ChangeSubscription(true); + } + + + /// + /// Changes the subscription to the TimeManager. + /// + private void ChangeSubscription(bool subscribe) + { + if (_timeManager == null) + return; + + if (subscribe) + { + if (_tickSmoother != null) + { + float tDistance = (_enableTeleport) ? _teleportThreshold : MoveRates.UNSET_VALUE; + _tickSmoother.InitializeOnce(_graphicalObject, tDistance, (float)_timeManager.TickDelta, 1); + } + _timeManager.OnPreTick += _timeManager_OnPreTick; + _timeManager.OnPostTick += _timeManager_OnPostTick; + } + else + { + _timeManager.OnPreTick -= _timeManager_OnPreTick; + _timeManager.OnPostTick -= _timeManager_OnPostTick; + } + } + + + /// + /// Called before a tick starts. + /// + private void _timeManager_OnPreTick() + { + _tickSmoother.OnPreTick(); + } + + /// + /// Called after a tick completes. + /// + private void _timeManager_OnPostTick() + { + _tickSmoother.OnPostTick(); + } + + + } + + +} + diff --git a/Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs.meta b/Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs.meta new file mode 100644 index 0000000..4e3fb18 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b3c0f5e921f9d784986ee1c8d311ccba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/MonoTickSmoother.cs + uploadId: 762203 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..5446ca3 --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs @@ -0,0 +1,111 @@ +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 + +#if UNITY_EDITOR || !UNITY_SERVER + + #region Private. + /// + /// Style for drawn ping. + /// + private GUIStyle _style = new(); + #endregion + + private void OnGUI() + { + //Only clients can see pings. + if (!InstanceFinder.IsClientStarted) + 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 * 2000d); + + ping = (long)Mathf.Max(1, ping - deduction); + } + + GUI.Label(new(horizontal, vertical, width, height), $"Ping: {ping}ms", _style); + } +#endif + + } + + +} \ 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..f87207d --- /dev/null +++ b/Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f9b6b565cd9533c4ebc18003f0fc18a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Generated/Component/Utility/PingDisplay.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/InstanceFinder.cs b/Assets/FishNet/Runtime/InstanceFinder.cs new file mode 100644 index 0000000..2e5ae09 --- /dev/null +++ b/Assets/FishNet/Runtime/InstanceFinder.cs @@ -0,0 +1,317 @@ +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 GameKit.Dependencies.Utilities; +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; + + //Do not log using NetworkManager extensions, it will try to use InstanceFinder, resulting in this causing a stack overflow. + 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; + } + } + + #region Obsoletes + + [Obsolete("Use IsClientOnlyStarted. Note the difference between IsClientOnlyInitialized and IsClientOnlyStarted.")] + public static bool IsClientOnly => IsClientOnlyStarted; + + [Obsolete("Use IsServerOnlyStarted. Note the difference between IsServerOnlyInitialized and IsServerOnlyStarted.")] + public static bool IsServerOnly => IsServerOnlyStarted; + + [Obsolete("Use IsHostStarted. Note the difference between IsHostInitialized and IsHostStarted.")] + public static bool IsHost => IsHostStarted; + + [Obsolete("Use IsClientStarted. Note the difference between IsClientInitialized and IsClientStarted.")] + public static bool IsClient => IsClientStarted; + + [Obsolete("Use IsServerStarted. Note the difference between IsServerInitialized and IsServerStarted.")] + public static bool IsServer => IsServerStarted; + + #endregion + + /// + /// True if the server is active. + /// + public static bool IsServerStarted => (NetworkManager == null) ? false : NetworkManager.IsServerStarted; + + /// + /// True if only the server is started. + /// + public static bool IsServerOnlyStarted => (NetworkManager == null) ? false : NetworkManager.IsServerOnlyStarted; + + /// + /// True if the client is started and authenticated. + /// + public static bool IsClientStarted => (NetworkManager == null) ? false : NetworkManager.IsClientStarted; + + /// + /// True if only the client is started and authenticated. + /// + public static bool IsClientOnlyStarted => (NetworkManager == null) ? false : NetworkManager.IsClientOnlyStarted; + + /// + /// True if client and server are started. + /// + public static bool IsHostStarted => (NetworkManager == null) ? false : NetworkManager.IsHostStarted; + + /// + /// True if client nor server are started. + /// + public static bool IsOffline + { + get + { + return (NetworkManager == null) ? true : NetworkManager.IsOffline; + } + } + #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 + { + if (NetworkManager != null) + 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 + { + if (NetworkManager != null) + NetworkManager.UnregisterInvokeOnInstance(handler); + } + + /// + /// Returns class of type if found within CodegenBase classes. + /// + /// + /// + public static T GetInstance() where T : UnityEngine.Component + { + return (NetworkManager == null) ? default : NetworkManager.GetInstance(); + } + + /// + /// Returns if class of type is registered with the NetworkManager. + /// + /// Type to check for. + /// + public static bool HasInstance() where T : UnityEngine.Component + { + return (NetworkManager == null) ? false : NetworkManager.HasInstance(); + } + + /// + /// 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 + { + if (NetworkManager != null) + NetworkManager.RegisterInstance(component, replace); + } + + /// + /// Tries to registers a new component to this NetworkManager. + /// This will not register the instance if another already exists. + /// + /// Type to register. + /// Reference of the component being registered. + /// True if was able to register, false if an instance is already registered. + public static bool TryRegisterInstance(T component) where T : UnityEngine.Component + { + return (NetworkManager == null) ? false : NetworkManager.TryRegisterInstance(component); + } + + /// + /// Returns class of type from registered instances. + /// + /// Outputted component. + /// Type to get. + /// True if was able to get instance. + public static bool TryGetInstance(out T component) where T : UnityEngine.Component + { + if (NetworkManager == null) + { + component = default; + return false; + } + else + { + return NetworkManager.TryGetInstance(out component); + } + } + + /// + /// Unregisters a component from this NetworkManager. + /// + /// Type to unregister. + public static void UnregisterInstance() where T : UnityEngine.Component + { + if (NetworkManager != null) + 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..bb9ca71 --- /dev/null +++ b/Assets/FishNet/Runtime/InstanceFinder.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 528668525d755164d989cddc73e3eee5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/InstanceFinder.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing.meta b/Assets/FishNet/Runtime/Managing.meta new file mode 100644 index 0000000..8aa5914 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9f3e6400a1a8b4141a9fa24f0e8aa430 +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..fd0d207 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8b8fdb465dad5174893b51876d9cc64c +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..cac3b51 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs @@ -0,0 +1,103 @@ +using FishNet.Broadcast; +using FishNet.Broadcast.Helping; +using FishNet.Managing.Utility; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Client +{ + public sealed partial class ClientManager : MonoBehaviour + { + #region Private. + /// + /// Handler for registered broadcasts. + /// + private readonly Dictionary _broadcastHandlers = new(); + #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 + { + if (handler == null) + { + NetworkManager.LogError($"Broadcast cannot be registered because handler is null. This may occur when trying to register to objects which require initialization, such as events."); + return; + } + + ushort key = BroadcastExtensions.GetKey(); + //Create new IBroadcastHandler if needed. + BroadcastHandlerBase bhs; + if (!_broadcastHandlers.TryGetValueIL2CPP(key, out bhs)) + { + bhs = new ServerBroadcastHandler(); + _broadcastHandlers.Add(key, bhs); + } + //Register handler to IBroadcastHandler. + bhs.RegisterHandler(handler); + } + + /// + /// 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 = BroadcastExtensions.GetKey(); + if (_broadcastHandlers.TryGetValueIL2CPP(key, out BroadcastHandlerBase bhs)) + bhs.UnregisterHandler(handler); + } + + /// + /// Parses a received broadcast. + /// + + 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 BroadcastHandlerBase bhs)) + bhs.InvokeHandlers(reader, channel); + 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; + } + + PooledWriter writer = WriterPool.Retrieve(); + BroadcastsSerializers.WriteBroadcast(NetworkManager, writer, message, ref channel); + ArraySegment segment = writer.GetArraySegment(); + + NetworkManager.TransportManager.SendToServer((byte)channel, segment); + writer.Store(); + } + + } + + +} 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..5b7b672 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 97ef4be4cfcc6a54d80a65a5c8d325d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/ClientManager.Broadcast.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs new file mode 100644 index 0000000..c1f539e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace FishNet.Managing.Client +{ + public sealed partial class ClientManager : MonoBehaviour + { + + } + + +} diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs.meta b/Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs.meta new file mode 100644 index 0000000..df711c7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fa61343b56a8f4942b8e6c998ed319ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/ClientManager.QOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs new file mode 100644 index 0000000..a6258a3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs @@ -0,0 +1,679 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Connection; +using FishNet.Managing.Debugging; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Transporting.Multipass; +using GameKit.Dependencies.Utilities; +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. + /// + /// This is set true if the server has notified the client it is using a development build. + /// Value is set before authentication. + /// + public bool IsServerDevelopment { get; private set; } + /// + /// Called after local client has authenticated. + /// + public event Action OnAuthenticated; + /// + /// Called when the local client connection to the server has timed out. + /// This is called immediately before disconnecting. + /// + public event Action OnClientTimeOut; + /// + /// 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; + /// + /// Called when the server sends all currently connected clients. + /// This is only available when using ServerManager.ShareIds. + /// + public event Action OnConnectedClients; + /// + /// 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(); + /// + /// NetworkManager for client. + /// + [HideInInspector] + public NetworkManager NetworkManager { get; private set; } + #endregion + + #region Serialized. + /// + /// What platforms to enable remote server timeout. + /// + [Tooltip("What platforms to enable remote server timeout.")] + [SerializeField] + private RemoteTimeoutType _remoteServerTimeout = RemoteTimeoutType.Development; + /// + /// How long in seconds server must go without sending any packets before the local client disconnects. This is independent of any transport settings. + /// + [Tooltip("How long in seconds server must go without sending any packets before the local client disconnects. This is independent of any transport settings.")] + [Range(1, ServerManager.MAXIMUM_REMOTE_CLIENT_TIMEOUT_DURATION)] + [SerializeField] + private ushort _remoteServerTimeoutDuration = 60; + + /// + /// Sets timeout settings. Can be used at runtime. + /// + /// + public void SetRemoteServerTimeout(RemoteTimeoutType timeoutType, ushort duration) + { + _remoteServerTimeout = timeoutType; + duration = (ushort)Mathf.Clamp(duration, 1, ServerManager.MAXIMUM_REMOTE_CLIENT_TIMEOUT_DURATION); + _remoteServerTimeoutDuration = duration; + } + + //todo add remote server timeout (see ServerManager.RemoteClientTimeout). + /// + /// 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 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; + [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; + + /// Sets the maximum frame rate the client may run at. Calling this method will enable ChangeFrameRate. + /// + /// New value. + public void SetFrameRate(ushort value) + { + _frameRate = (ushort)Mathf.Clamp(value, 0, NetworkManager.MAXIMUM_FRAMERATE); + _changeFrameRate = true; + if (NetworkManager != null) + NetworkManager.UpdateFramerate(); + } + #endregion + + #region Private. + /// + /// Last unscaled time client got a packet. + /// + private float _lastPacketTime; + /// + /// Used to read splits. + /// + private SplitReader _splitReader = new(); + #endregion + + private void OnDestroy() + { + Objects?.SubscribeToSceneLoaded(false); + } + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + NetworkManager = manager; + Objects = new(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, Channel channel) + { + //If connecting invoke after added to clients, otherwise invoke before removed. + RemoteConnectionStateArgs rcs = new((args.Connected) ? RemoteConnectionState.Started : RemoteConnectionState.Stopped, args.Id, -1); + + if (args.Connected) + { + Clients[args.Id] = new(NetworkManager, args.Id, -1, false); + OnRemoteConnectionState?.Invoke(rcs); + } + else + { + OnRemoteConnectionState?.Invoke(rcs); + if (Clients.TryGetValue(args.Id, out NetworkConnection c)) + { + c.ResetState(); + Clients.Remove(args.Id); + } + } + } + + /// + /// Called when the server sends all currently connected clients. + /// + /// + private void OnConnectedClientsBroadcast(ConnectedClientsBroadcast args, Channel channel) + { + NetworkManager.ClearClientsCollection(Clients); + + List collection = args.Values; + //No connected clients except self. + if (collection == null) + { + collection = new(); + } + //Other clients. + else + { + int count = collection.Count; + for (int i = 0; i < count; i++) + { + int id = collection[i]; + Clients[id] = new(NetworkManager, id, -1, false); + } + } + + OnConnectedClients?.Invoke(new(collection)); + } + + /// + /// 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; + NetworkManager.TimeManager.OnPostTick += TimeManager_OnPostTick; + } + else + { + NetworkManager.TransportManager.OnIterateIncomingEnd -= TransportManager_OnIterateIncomingEnd; + NetworkManager.TransportManager.Transport.OnClientReceivedData -= Transport_OnClientReceivedData; + NetworkManager.TransportManager.Transport.OnClientConnectionState -= Transport_OnClientConnectionState; + NetworkManager.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + } + + /// + /// Gets the transport index being used for the local client. + /// If only one transport is used this will return 0. If Multipass is being used this will return the client's transport in multipass. + /// + /// + public int GetTransportIndex() + { + if (NetworkManager.TransportManager.Transport is Multipass mp) + return mp.ClientTransport.Index; + else + return 0; + } + + /// + /// Stops the local client connection. + /// + public bool StopConnection() + { + return NetworkManager.TransportManager.Transport.StopConnection(false); + } + + /// + /// Starts the local client connection. + /// + public bool StartConnection() + { + return NetworkManager.TransportManager.Transport.StartConnection(false); + } + + /// + /// Sets the transport address and starts the local client connection. + /// + public bool StartConnection(string address) + { + NetworkManager.TransportManager.Transport.SetClientAddress(address); + return StartConnection(); + } + + /// + /// Sets the transport address and port, and starts the local client connection. + /// + public bool StartConnection(string address, ushort port) + { + NetworkManager.TransportManager.Transport.SetClientAddress(address); + NetworkManager.TransportManager.Transport.SetPort(port); + return 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); + } + else + { + _lastPacketTime = Time.unscaledTime; + //Send version. + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(PacketId.Version); + writer.WriteString(NetworkManager.FISHNET_VERSION); + NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment()); + WriterPool.Store(writer); + } + + if (NetworkManager.CanLog(LoggingType.Common)) + { + Transport t = NetworkManager.TransportManager.GetTransport(args.TransportIndex); + string tName = (t == null) ? "Unknown" : t.GetType().Name; + string socketInformation = string.Empty; + if (state == LocalConnectionState.Starting) + socketInformation = $" Server IP is {t.GetClientAddress()}, port is {t.GetPort()}."; + NetworkManager.Log($"Local client is {state.ToString().ToLower()} for {tName}.{socketInformation}"); + } + + NetworkManager.UpdateFramerate(); + OnClientConnectionState?.Invoke(args); + } + + /// + /// Called when a socket receives data. + /// + private void Transport_OnClientReceivedData(ClientReceivedDataArgs args) + { + 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) + { + _lastPacketTime = Time.unscaledTime; + + ArraySegment segment; + if (NetworkManager.TransportManager.HasIntermediateLayer) + segment = NetworkManager.TransportManager.ProcessIntermediateIncoming(args.Data, true); + else + segment = args.Data; + + NetworkManager.StatisticsManager.NetworkTraffic.LocalClientReceivedData((ulong)segment.Count); + if (segment.Count <= TransportManager.UNPACKED_TICK_LENGTH) + return; + + PooledReader reader = ReaderPool.Retrieve(segment, NetworkManager, Reader.DataSource.Server); + TimeManager tm = NetworkManager.TimeManager; + tm.LastPacketTick.Update(reader.ReadTickUnpacked(), EstimatedTick.OldTickOption.Discard, false); + ParseReader(reader, args.Channel); + ReaderPool.Store(reader); + } + + internal void ParseReader(PooledReader reader, Channel channel, bool print = false) + { + PacketId packetId = PacketId.Unset; +#if !DEVELOPMENT + try + { +#endif + Reader.DataSource dataSource = Reader.DataSource.Server; + /* 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) + { +#if DEVELOPMENT + NetworkManager.PacketIdHistory.ReceivedPacket(PacketId.Split, packetFromServer: true); +#endif + //Skip packetId. + reader.ReadPacketId(); + int expectedMessages; + _splitReader.GetHeader(reader, out expectedMessages); + _splitReader.Write(NetworkManager.TimeManager.LastPacketTick.LastRemoteTick, 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; + + reader.Initialize(fullMessage, NetworkManager, dataSource); + } + + while (reader.Remaining > 0) + { + packetId = reader.ReadPacketId(); +#if DEVELOPMENT + NetworkManager.PacketIdHistory.ReceivedPacket(packetId, packetFromServer: true); + // if (!NetworkManager.IsServerStarted) + // print = true; + // if (print) + // { + // if (packetId == PacketId.ObserversRpc) + // Debug.Log($"PacketId {packetId} - Remaining {reader.Remaining}."); + // else + // Debug.LogWarning($"PacketId {packetId} - Remaining {reader.Remaining}."); + // } + // print = false; +#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.ReadSpawn(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, channel); + } + else if (packetId == PacketId.StateUpdate) + { + NetworkManager.PredictionManager.ParseStateUpdate(reader, channel); + } + else if (packetId == PacketId.Replicate) + { + Objects.ParseReplicateRpc(reader, null, channel); + } + else if (packetId == PacketId.Reconcile) + { + Objects.ParseReconcileRpc(reader, channel); + } + else if (packetId == PacketId.ObserversRpc) + { + Objects.ParseObserversRpc(reader, channel); + } + else if (packetId == PacketId.TargetRpc) + { + Objects.ParseTargetRpc(reader, channel); + } + else if (packetId == PacketId.Broadcast) + { + ParseBroadcast(reader, channel); + } + else if (packetId == PacketId.PingPong) + { + ParsePingPong(reader); + } + else if (packetId == PacketId.SyncType) + { + Objects.ParseSyncType(reader, channel); + } + else if (packetId == PacketId.PredictedSpawnResult) + { + Objects.ParsePredictedSpawnResult(reader); + } + else if (packetId == PacketId.TimingUpdate) + { + NetworkManager.TimeManager.ParseTimingUpdate(reader); + } + else if (packetId == PacketId.OwnershipChange) + { + Objects.ParseOwnershipChange(reader); + } + else if (packetId == PacketId.Authenticated) + { + ParseAuthenticated(reader); + } + else if (packetId == PacketId.Disconnect) + { + reader.Clear(); + StopConnection(); + } + else if (packetId == PacketId.Version) + { + ParseVersion(reader); + } + else + { + NetworkManager.LogError($"Client received an unhandled PacketId of {(ushort)packetId} on channel {channel}. Remaining data has been purged."); +#if DEVELOPMENT + NetworkManager.LogError(NetworkManager.PacketIdHistory.GetReceivedPacketIds(packetsFromServer: true)); +#endif + return; + } + } + +#if DEVELOPMENT + if (print) + Debug.Log($"Reader remaining {reader.Remaining}"); +#endif + } + + /* 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 !DEVELOPMENT + } + catch (Exception e) + { + NetworkManagerExtensions.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.ReadTickUnpacked(); + NetworkManager.TimeManager.ModifyPing(clientTick); + } + + /// + /// Parses a Version packet. + /// + /// + private void ParseVersion(PooledReader reader) + { + IsServerDevelopment = reader.ReadBoolean(); + } + + /// + /// 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.IsServerStarted) + { + Clients.TryGetValueIL2CPP(connectionId, out Connection); + /* This is bad and should never happen unless the connection is dropping + * while receiving authenticated. Would have to be a crazy race condition + * but with the network anything is possible. */ + if (Connection == null) + { + NetworkManager.LogWarning($"Client connection could not be found while parsing authenticated status. This usually occurs when the client is receiving a packet immediately before losing connection."); + Connection = new(networkManager, connectionId, GetTransportIndex(), false); + } + } + /* 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(networkManager, connectionId, GetTransportIndex(), false); + } + } + + //If predicted spawning is enabled also get reserved Ids. + if (NetworkManager.ServerManager.GetAllowPredictedSpawning()) + { + int count = (int)reader.ReadSignedPackedWhole(); + 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. */ + if (!networkManager.IsServerStarted) + networkManager.TimeManager.Tick = networkManager.TimeManager.LastPacketTick.LastRemoteTick; + + //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(); + } + + /// + /// Called when the TimeManager calls OnPostTick. + /// + private void TimeManager_OnPostTick() + { + CheckServerTimeout(); + } + + /// + /// Checks to timeout client connections. + /// + private void CheckServerTimeout() + { + /* Not connected or host. There should be no way + * for server to drop and client not know about it as host. + * This would mean a game crash or force close in which + * the client would be gone as well anyway. */ + if (!Started || NetworkManager.IsServerStarted) + return; + if (_remoteServerTimeout == RemoteTimeoutType.Disabled) + return; +#if DEVELOPMENT + //If development but not set to development return. + else if (_remoteServerTimeout != RemoteTimeoutType.Development) + return; +#endif + //Wait two timing intervals to give packets a chance to come through. + if (NetworkManager.SceneManager.IsIteratingQueue(2f)) + return; + + /* ServerManager version only checks every so often + * to perform iterations over time so the checks are not + * impactful on the CPU. The client however can check every tick + * since it's simple math. */ + if (Time.unscaledTime - _lastPacketTime > _remoteServerTimeoutDuration) + { + OnClientTimeOut?.Invoke(); + NetworkManager.Log($"Server has timed out. You can modify this feature on the ClientManager component."); + StopConnection(); + } + } + } +} \ No newline at end of file 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..9291366 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/ClientManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aca43cf6f20e77c4f8fcc078fd85081f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/ClientManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Client/Editor.meta b/Assets/FishNet/Runtime/Managing/Client/Editor.meta new file mode 100644 index 0000000..3356dca --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 311ccd7ecfc60694bb8a40a3af3a76f6 +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..56fa1b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs @@ -0,0 +1,60 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Client.Editing +{ + + + [CustomEditor(typeof(ClientManager), true)] + [CanEditMultipleObjects] + public class ClientManagerEditor : Editor + { + private SerializedProperty _remoteServerTimeout; + private SerializedProperty _remoteServerTimeoutDuration; + private SerializedProperty _changeFrameRate; + private SerializedProperty _frameRate; + + protected virtual void OnEnable() + { + _remoteServerTimeout = serializedObject.FindProperty(nameof(_remoteServerTimeout)); + _remoteServerTimeoutDuration = serializedObject.FindProperty(nameof(_remoteServerTimeoutDuration)); + _changeFrameRate = serializedObject.FindProperty(nameof(_changeFrameRate)); + _frameRate = serializedObject.FindProperty(nameof(_frameRate)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((ClientManager)target), typeof(ClientManager), false); + GUI.enabled = true; + + EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(_remoteServerTimeout); + if ((RemoteTimeoutType)_remoteServerTimeout.intValue != RemoteTimeoutType.Disabled) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_remoteServerTimeoutDuration, new GUIContent("Timeout")); + EditorGUI.indentLevel--; + } + + EditorGUILayout.PropertyField(_changeFrameRate); + if (_changeFrameRate.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_frameRate); + EditorGUI.indentLevel--; + } + + EditorGUI.indentLevel--; + + 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..a00bc73 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a652d51a1efa8a442966e885e2736599 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/Editor/ClientManagerEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Client/Object.meta b/Assets/FishNet/Runtime/Managing/Client/Object.meta new file mode 100644 index 0000000..b410dd1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3956bb4fb9de7644d9070720d0e5c4c7 +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..0e19045 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs @@ -0,0 +1,101 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif + +using FishNet.Managing.Object; +using FishNet.Managing.Utility; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using FishNet.Serializing.Helping; + +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(); + #endregion + + /// + /// Parses a received RPCLink. + /// + /// + /// + internal void ParseRpcLink(PooledReader reader, ushort index, Channel channel) + { +#if DEVELOPMENT + NetworkBehaviour.ReadDebugForValidatedRpc(base.NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount); +#endif + + int dataLength; + //Link index isn't stored. + if (!_rpcLinks.TryGetValueIL2CPP(index, out RpcLink link)) + { + dataLength = Packets.GetPacketLength(ushort.MaxValue, reader, channel); + SkipDataLength(index, reader, dataLength); + } + //Found NetworkObject for link. + else if (Spawned.TryGetValueIL2CPP(link.ObjectId, out NetworkObject nob)) + { + //Still call GetPacketLength to remove any extra bytes at the front of the reader. + NetworkBehaviour nb = nob.NetworkBehaviours[link.ComponentIndex]; + if (link.RpcPacketId == PacketId.TargetRpc) + { + Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel); + nb.ReadTargetRpc(fromRpcLink: true, link.RpcHash, reader, channel); + } + else if (link.RpcPacketId == PacketId.ObserversRpc) + { + Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel); + nb.ReadObserversRpc(fromRpcLink: true, link.RpcHash, reader, channel); + } + else if (link.RpcPacketId == PacketId.Reconcile) + { + Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel); + nb.OnReconcileRpc(link.RpcHash, reader, channel); + } + } + //Could not find NetworkObject. + else + { + dataLength = Packets.GetPacketLength(index, reader, channel); + SkipDataLength(index, reader, dataLength, link.ObjectId); + } + +#if DEVELOPMENT + NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: true, base.NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel); +#endif + } + + /// + /// 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..4f29083 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2a20cc3f399aa614c931c9b45205937b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.RpcLinks.cs + uploadId: 762203 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..85462f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs @@ -0,0 +1,721 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +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 GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using FishNet.Serializing.Helping; +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(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. */ + + /* Only perform this step if the transport being stopped + * is the one which client is connected to. */ + if (NetworkManager.IsClientStarted && args.TransportIndex == base.NetworkManager.ClientManager.GetTransportIndex()) + 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.IsServerStarted) + { + base.DespawnWithoutSynchronization(recursive: true, asServer: false); + } + //Otherwise invoke stop callbacks only for client side. + else + { + foreach (NetworkObject n in Spawned.Values) + { + if (!n.CanDeinitialize(asServer: false)) + continue; + + n.InvokeStopCallbacks(false, true); + n.SetInitializedStatus(false, 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_Internal.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.IsClientStarted) + 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); + } + + /// + /// Adds a NetworkObject to Spawned. + /// + internal override void AddToSpawned(NetworkObject nob, bool asServer) + { + base.AddToSpawned(nob, asServer); + //If being added as client and is also server. + if (NetworkManager.IsServerStarted) + nob.SetRenderersVisible(true); + } + + /// + /// 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.TryPeek(out int objectId)) + { + 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."); + StoreNetworkObject(); + return; + } + + networkObject.InitializePredictedObject_Client(base.NetworkManager, objectId, ownerConnection, base.NetworkManager.ClientManager.Connection); + NetworkManager.ClientManager.Objects.AddToSpawned(networkObject, false); + networkObject.Initialize(asServer: false, invokeSyncTypeCallbacks: true); + + PooledWriter writer = WriterPool.Retrieve(); + if (WriteSpawn(networkObject, writer, connection: null)) + { + base.NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment()); + //Also dequeue entry, since we only peeked it earlier. + predictedObjectIds.Dequeue(); + } + else + { + StoreNetworkObject(); + } + + void StoreNetworkObject() + { + networkObject.SetIsDestroying(); + networkObject.Deinitialize(asServer: false); + + NetworkManager.StorePooledOrDestroyInstantiated(networkObject, asServer: false); + } + + writer.Store(); + } + + /// + /// Sends a predicted despawn to the server. + /// + internal void PredictedDespawn(NetworkObject networkObject) + { + PooledWriter writer = WriterPool.Retrieve(); + WriteDepawn(networkObject, writer); + base.NetworkManager.TransportManager.SendToServer((byte)Channel.Reliable, writer.GetArraySegment()); + writer.Store(); + + base.Despawn(networkObject, networkObject.GetDefaultDespawnType(), asServer: false); + } + + /// + /// Writes a predicted despawn. + /// + public void WriteDepawn(NetworkObject nob, Writer writer) + { + writer.WritePacketIdUnpacked(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) + { + List nobs = CollectionCaches.RetrieveList(); + Scenes.GetSceneNetworkObjects(s, false, true, true, ref nobs); + + bool isServerStarted = base.NetworkManager.IsServerStarted; + + int nobsCount = nobs.Count; + for (int i = 0; i < nobsCount; i++) + { + NetworkObject nob = nobs[i]; + if (!nob.IsSceneObject) + continue; + + //Only set initialized values if not server, as server would have already done so. + if (!isServerStarted) + nob.SetInitializedValues(parentNob: null, force: false); + + if (nob.GetIsNetworked()) + { + base.AddToSceneObjects(nob); + //Only run if not also server, as this already ran on server. + if (!base.NetworkManager.IsServerStarted) + nob.gameObject.SetActive(false); + } + } + + CollectionCaches.Store(nobs); + } + + /// + /// Called when a NetworkObject runs Deactivate. + /// + /// + internal override void NetworkObjectDestroyed(NetworkObject nob, bool asServer) + { + nob.RemoveClientRpcLinkIndexes(); + base.NetworkObjectDestroyed(nob, asServer); + } + + /// + /// Parses an OwnershipChange packet. + /// + /// + internal void ParseOwnershipChange(PooledReader reader) + { + NetworkObject nob = reader.ReadNetworkObject(); + NetworkConnection newOwner = reader.ReadNetworkConnection(); + if (nob != null && nob.IsSpawned) + nob.GiveOwnership(newOwner, asServer: false, recursive: false); + else + NetworkManager.LogWarning($"NetworkBehaviour could not be found when trying to parse OwnershipChange packet."); + } + + /// + /// Parses a received syncVar. + /// + /// + internal void ParseSyncType(PooledReader reader, Channel channel) + { + //cleanup this is unique to synctypes where length comes first. + //this will change once I tidy up synctypes. + ushort packetId = (ushort)PacketId.SyncType; + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int length = (int)ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.SYNCTYPE_RESERVE_BYTES); + + if (nb != null && nb.IsSpawned) + { + /* 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 (length > 0) + nb.ReadSyncType(reader, length); + } + else + { + SkipDataLength(packetId, reader, length); + } + } + + /// + /// Parses a + /// + /// + internal void ParsePredictedSpawnResult(PooledReader reader) + { + bool success = reader.ReadBoolean(); + int usedObjectId = reader.ReadNetworkObjectId(); + int nextObjectId = reader.ReadNetworkObjectId(); + if (nextObjectId != NetworkObject.UNSET_OBJECTID_VALUE) + NetworkManager.ClientManager.Connection.PredictedObjectIds.Enqueue(nextObjectId); + + //Server would not allow the predicted spawn. + if (!success) + { + if (Spawned.TryGetValueIL2CPP(usedObjectId, out NetworkObject nob)) + { + //TODO support pooling. This first requires a rework of the initialization / clientHost message system. + nob.SetIsDestroying(DespawnType.Destroy); + UnityEngine.Object.Destroy(nob.gameObject); + //nob.Deinitialize(asServer: false); + //NetworkManager.StorePooledInstantiated(nob, false); + } + } + } + + /// + /// Parses a ReconcileRpc. + /// + /// + internal void ParseReconcileRpc(PooledReader reader, Channel channel) + { +#if DEVELOPMENT + NetworkBehaviour.ReadDebugForValidatedRpc(base.NetworkManager, reader, out int readerRemainingAfterLength, out string rpcInformation, out uint expectedReadAmount); +#endif + + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.Reconcile, reader, channel); + + if (nb != null && nb.IsSpawned) + nb.OnReconcileRpc(null, reader, channel); + else + SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength); + +#if DEVELOPMENT + NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, base.NetworkManager, reader, readerRemainingAfterLength, rpcInformation, expectedReadAmount, channel); +#endif + } + + /// + /// Parses an ObserversRpc. + /// + /// + internal void ParseObserversRpc(PooledReader reader, Channel channel) + { +#if DEVELOPMENT + NetworkBehaviour.ReadDebugForValidatedRpc(base.NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount); +#endif + + NetworkBehaviour nb = reader.ReadNetworkBehaviour(logException: false); + int dataLength = Packets.GetPacketLength((ushort)PacketId.ObserversRpc, reader, channel); + if (nb != null && nb.IsSpawned) + { + nb.ReadObserversRpc(fromRpcLink: false, methodHash: 0, reader, channel); + } + else + { + base.NetworkManager.Log($"NetworkBehaviour not found for an ObserverRpc. Rpc data will be discarded."); + SkipDataLength((ushort)PacketId.ObserversRpc, reader, dataLength); + } + +#if DEVELOPMENT + NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, base.NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel); +#endif + } + + /// + /// Parses a TargetRpc. + /// + /// + internal void ParseTargetRpc(PooledReader reader, Channel channel) + { +#if DEVELOPMENT + NetworkBehaviour.ReadDebugForValidatedRpc(base.NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount); +#endif + + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.TargetRpc, reader, channel); + + if (nb != null && nb.IsSpawned) + nb.ReadTargetRpc(fromRpcLink: false, methodHash: 0, 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 ReadSpawn(PooledReader reader) + { + SpawnType st = (SpawnType)reader.ReadUInt8Unpacked(); + + bool sceneObject = st.FastContains(SpawnType.Scene); + + ReadNestedSpawnIds(reader, st, out byte? nobComponentId, out int? parentObjectId, out byte? parentComponentId, _objectCache.ReadSpawningObjects); + + //NeworkObject and owner information. + int objectId = reader.ReadNetworkObjectForSpawn(out int initializeOrder, out ushort collectionId); + int ownerId = reader.ReadNetworkConnectionId(); + //Read transform values which differ from serialized values. + Vector3? localPosition; + Quaternion? localRotation; + Vector3? localScale; + base.ReadTransformProperties(reader, out localPosition, out localRotation, out localScale); + + int prefabId = 0; + ulong sceneId = 0; + string sceneName = string.Empty; + string objectName = string.Empty; + + if (sceneObject) + { + base.ReadSceneObjectId(reader, out sceneId); +#if DEVELOPMENT + if (NetworkManager.ClientManager.IsServerDevelopment) + base.CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName); +#endif + } + else + { + prefabId = reader.ReadNetworkObjectId(); + } + + ArraySegment payload = base.ReadPayload(reader); + ArraySegment rpcLinks = ReadRpcLinks(reader); + ArraySegment syncTypes = ReadSyncTypesForSpawn(reader); + + bool isPredictedSpawner = st.FastContains(SpawnType.IsPredictedSpawner); + + //If found in spawn already. + if (base.Spawned.TryGetValue(objectId, out NetworkObject nob)) + { + /* If not server then extra checks must be done. Client should never + * receive spawn messages for already spawned objects, unless they locally + * predicted spawned the object. */ + if (!base.NetworkManager.IsServerStarted) + { + //Not predicted spawner. + if (!st.FastContains(SpawnType.IsPredictedSpawner)) + { + NetworkManager.LogWarning($"Received a spawn objectId of {objectId} which was already found in spawned, and was not predicted. This sometimes may occur on clientHost when the server destroys an object unexpectedly before the clientHost gets the spawn message."); + } + //Is predicted spawner. + else + { + PooledReader segmentReader = ReaderPool.Retrieve(ArraySegment.Empty, NetworkManager); + + //RpcLinks. + segmentReader.Initialize(rpcLinks, NetworkManager, Reader.DataSource.Server); + ApplyRpcLinks(nob, segmentReader); + + //Payload. + segmentReader.Initialize(payload, NetworkManager, Reader.DataSource.Server); + ReadPayload(sender: null, nob, segmentReader, segmentReader.Length); + + //SyncTypes. + segmentReader.Initialize(syncTypes, NetworkManager, Reader.DataSource.Server); + ApplySyncTypesForSpawn(nob, segmentReader); + } + + /* Nob isn't added to spawn if predicted spawner. + * We only wanted to read and apply initial data from the server. */ + return; + } + } + else + { + /* If predicted spawner and not in spawned then simply exit early. + * The predicted spawner destroyed the object locally. */ + if (isPredictedSpawner) + return; + } + + + _objectCache.AddSpawn(base.NetworkManager, collectionId, objectId, initializeOrder, ownerId, st, nobComponentId, parentObjectId, parentComponentId, prefabId, localPosition, localRotation, localScale, sceneId, sceneName, objectName, payload, rpcLinks, syncTypes); + } + + /// + /// 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.ReadNetworkObjectForDespawn(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.ParentObjectId.Value; + byte componentIndex = cnob.ComponentId.Value; + + /* 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) + { + //Only log if not clientHost. + if (!base.NetworkManager.IsServerStarted) + 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.InitializedNestedNetworkObjects; + + //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) + { + //Only log if not clientHost. + if (!base.NetworkManager.IsServerStarted) + 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. + /// This method will exit early if reader is null. + /// + internal void ApplyRpcLinks(NetworkObject nob, PooledReader reader) + { + if (reader == null) + return; + + List rpcLinkIndexes = new(); + + while (reader.Remaining > 0) + { + byte componentId = reader.ReadNetworkBehaviourId(); + ushort count = reader.ReadUInt16Unpacked(); + + for (int i = 0; i < count; i++) + { + //Index of RpcLink. + ushort linkIndex = reader.ReadUInt16Unpacked(); + RpcLink link = new(nob.ObjectId, componentId, + //RpcHash. + reader.ReadUInt16Unpacked(), + //packetId for rpc. + reader.ReadPacketId()); + //Add to links. + SetRpcLink(linkIndex, link); + rpcLinkIndexes.Add(linkIndex); + } + } + + nob.SetRpcLinkIndexes(rpcLinkIndexes); + } + + /// + /// Applies initial SyncTypes to a NetworkObject. + /// A null reader will exit the method early. + /// + internal void ApplySyncTypesForSpawn(NetworkObject nob, PooledReader reader) + { + if (reader == null) + return; + /* 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. */ + List behaviours = nob.NetworkBehaviours; + + while (reader.Remaining > 0) + { + byte behaviourId = reader.ReadUInt8Unpacked(); + behaviours[behaviourId].ReadSyncTypesForSpawn(reader); + } + } + + /// + /// 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.IsHostStarted) + { + Transform parentTransform = null; + //Set parentTransform if there's a parent object. + if (cnob.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 + { + byte componentIndex = cnob.ComponentId.Value; + 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 not be found within NetworkObject {nob.name} with ObjectId {objectId}. Prefab {prefab.name} will be instantiated without parent synchronization."); + } + } + } + + ObjectPoolRetrieveOption retrieveOptions = (ObjectPoolRetrieveOption.MakeActive | ObjectPoolRetrieveOption.LocalSpace); + result = networkManager.GetPooledInstantiated(prefabId, collectionId, retrieveOptions, parentTransform, cnob.Position, cnob.Rotation, cnob.Scale, asServer: false); + + //Only need to set IsGlobal also if not host. + bool isGlobal = cnob.SpawnType.FastContains(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.LogWarning($"ObjectId {cnob.ObjectId} could not be found in Server spawned, nor Server pending despawn. This may occur as clientHost when objects are destroyed before the client receives a despawn packet. In most cases this may be ignored."); + } + + 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; + } + } + } +} \ 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..d674c27 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: da027fa27b0c0994ebfa317968862970 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/Object/ClientObjects.cs + uploadId: 762203 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..f148ffe --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs @@ -0,0 +1,659 @@ +using FishNet.Connection; +using FishNet.Managing.Object; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +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. + /// + /// Objects which are being spawned during iteration. + /// + internal Dictionary IteratedSpawningObjects = new(); + /// + /// ObjectIds which have been read this tick. + /// + internal HashSet ReadSpawningObjects = new(); + #endregion + + #region Private. + /// + /// Cached objects buffer. Contains spawns and despawns. + /// + private List _cachedObjects = new(); + /// + /// NetworkObjects which have been spawned already during the current iteration. + /// + private HashSet _iteratedSpawns = new(); + /// + /// Despawns which are occurring the same tick as their spawn. + /// + private HashSet _conflictingDespawns = new(); + /// + /// 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.Count; + List collection = _cachedObjects; + 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, int initializeOrder, int ownerId, SpawnType ost, byte? nobComponentId, int? parentObjectId, byte? parentComponentId, int? prefabId, Vector3? localPosition, Quaternion? localRotation, Vector3? localScale, ulong sceneId, string sceneName, string objectName, ArraySegment payload, 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 = ResettableObjectCaches.Retrieve(); + _cachedObjects.Add(cnob); + } + //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.Count; + for (int i = 0; i < written; i++) + { + CachedNetworkObject item = _cachedObjects[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 = ResettableObjectCaches.Retrieve(); + _cachedObjects.Insert(i, cnob); + break; + } + } + + //If here and cnob is null then it was not inserted; add to end. + if (cnob == null) + { + cnob = ResettableObjectCaches.Retrieve(); + _cachedObjects.Add(cnob); + } + } + + cnob.InitializeSpawn(manager, collectionId, objectId, initializeOrder, ownerId, ost, nobComponentId, parentObjectId, parentComponentId, prefabId, localPosition, localRotation, localScale, sceneId, sceneName, objectName, payload, rpcLinks, syncValues); + + ReadSpawningObjects.Add(objectId); + } + + public void AddDespawn(int objectId, DespawnType despawnType) + { + CachedNetworkObject cnob = ResettableObjectCaches.Retrieve(); + _cachedObjects.Add(cnob); + cnob.InitializeDespawn(objectId, despawnType); + } + + /// + /// Iterates any written objects. + /// + public void Iterate() + { + int written = _cachedObjects.Count; + if (written == 0) + return; + + try + { + //Indexes which have already been processed. + HashSet processedIndexes = new(); + List collection = _cachedObjects; + _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.HasParent) + { + bool nested = cnob.IsInitializedNested; + //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 = cnob.ParentObjectId.Value; + NetworkObject nob = GetSpawnedObject(targetObjectId); + //If not spawned yet. + if (nob == null) + { + bool isClientHost = _networkManager.IsServerStarted; + + 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) + { + if (!isClientHost) + { + errMsg = (nested) ? $"ObjectId {targetObjectId} was found for a nested spawn, but ActionType is not spawn. ComponentIndex {cnob.ComponentId} 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. Only log if not clientHost. + if (!found) + { + if (!isClientHost) + { + errMsg = (nested) ? $"ObjectId {targetObjectId} could not be found for a nested spawn. ComponentIndex {cnob.ComponentId} 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 the NetworkObject is null on lookup then something happened in the retrieval. Exit early. + * This can be normal on clientHost when client side gets packets late. When + * clientHost this will fail silently.*/ + + if (spawn) + { + if (cnob.IsSceneObject) + { + cnob.NetworkObject = _clientObjects.GetSceneNetworkObject(cnob.SceneId, cnob.SceneName, cnob.ObjectName); + if (cnob.NetworkObject != null) + SetParentAndTransformProperties(cnob); + } + //Is nested in a prefab. + else if (cnob.IsInitializedNested) + { + cnob.NetworkObject = _clientObjects.GetNestedNetworkObject(cnob); + if (cnob.NetworkObject != null) + cnob.NetworkObject.transform.SetLocalPositionRotationAndScale(cnob.Position, cnob.Rotation, cnob.Scale); + } + /* Not sceneObject or initializedNested. Could still be runtime + * nested but this also requires instantiation. The instantiation process + * handles parenting and position. */ + else + { + cnob.NetworkObject = _clientObjects.GetInstantiatedNetworkObject(cnob); + //Parenting and transform is done during the instantiation process. + } + } + //Despawn. + else + { + cnob.NetworkObject = _clientObjects.GetSpawnedNetworkObject(cnob); + + /* //TODO cache recent predicted despawns for up to a few seconds. + * If not found and the Id is in recently predicted despawned then simply + * do nothing. Otherwise log missing Id. */ + // /* Do not log unless not nested. Nested nobs sometimes + // * could be destroyed if parent was first. */ + // if (!_networkManager.IsHostStarted && cnob.NetworkObject == null && !cnob.IsInitializedNested) + // _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) + { + NetworkConnection owner; + int objectId; + //If not server then initialize by using lookups. + if (!_networkManager.IsServerStarted) + { + objectId = cnob.ObjectId; + int ownerId = cnob.OwnerId; + //If local client is owner then use localconnection reference. + NetworkConnection localConnection = _networkManager.ClientManager.Connection; + //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; + } + } + //Otherwise initialize using server values. + else + { + owner = nob.Owner; + objectId = nob.ObjectId; + } + + //Preinitialize client side. + nob.InitializeEarly(_networkManager, objectId, owner, false); + //Read payload. + if (cnob.PayloadReader != null) + _networkManager.ClientManager.Objects.ReadPayload(NetworkManager.EmptyConnection, nob, cnob.PayloadReader, cnob.PayloadReader.Length); + + _clientObjects.AddToSpawned(cnob.NetworkObject, false); + IteratedSpawningObjects.Add(cnob.ObjectId, cnob.NetworkObject); + /* Fixes https://github.com/FirstGearGames/FishNet/issues/323 + * The redundancy may have been caused by a rework. It would seem + * IterateSpawn was always running after the above lines, and not + * from anywhere else. So there's no reason we cannot inline it + * here. */ + _clientObjects.ApplyRpcLinks(cnob.NetworkObject, cnob.RpcLinkReader); + //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.IsServerStarted && 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. */ + NetworkObject n = cnob.NetworkObject; + if (_iteratedSpawns.Contains(n)) + { + // 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); + n.gameObject.SetActive(true); + n.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) + { + _clientObjects.ApplySyncTypesForSpawn(cnob.NetworkObject, cnob.SyncTypesReader); + + /* 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.InvokeOnStartSyncTypeCallbacks(false); + } + } + finally + { + //Once all have been iterated reset. + Reset(); + } + } + + /// + /// Sets parent using information on a CachedNetworkObject then applies transform properties. + /// + /// + private void SetParentAndTransformProperties(CachedNetworkObject cnob) + { + if (!_networkManager.IsHostStarted && cnob.NetworkObject != null) + { + //Apply runtime parent if needed. + if (cnob.HasParent) + { + if (_networkManager.ClientManager.Objects.Spawned.TryGetValueIL2CPP(cnob.ParentObjectId.Value, out NetworkObject parentNob)) + { + //If parented to the NOB directly. + if (!cnob.ParentComponentId.HasValue) + cnob.NetworkObject.SetParent(parentNob); + //Parented to a NB. + else + cnob.NetworkObject.SetParent(parentNob.NetworkBehaviours[cnob.ParentComponentId.Value]); + } + else + { + _networkManager.Log($"Parent NetworkObject Id {cnob.ParentObjectId} could not be found in spawned. NetworkObject {cnob.NetworkObject} will not have it's parent set."); + } + + //cnob.NetworkObject.transform.SetLocalPositionRotationAndScale(cnob.Position, cnob.Rotation, cnob.Scale); + } + + // else + // { + // cnob.NetworkObject.transform.SetWorldPositionRotationAndScale(cnob.Position, cnob.Rotation, cnob.Scale); + // } + cnob.NetworkObject.transform.SetLocalPositionRotationAndScale(cnob.Position, cnob.Rotation, cnob.Scale); + } + } + + /// + /// 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 (!IteratedSpawningObjects.TryGetValue(objectId, out result)) + { + Dictionary spawned = (_networkManager.IsHostStarted) ? _networkManager.ServerManager.Objects.Spawned : _networkManager.ClientManager.Objects.Spawned; + spawned.TryGetValue(objectId, out result); + } + + return result; + } + + /// + /// Resets cache. + /// + public void Reset() + { + _initializeOrderChanged = false; + foreach (CachedNetworkObject item in _cachedObjects) + ResettableObjectCaches.Store(item); + + _cachedObjects.Clear(); + _iteratedSpawns.Clear(); + IteratedSpawningObjects.Clear(); + ReadSpawningObjects.Clear(); + } + } + + /// + /// A cached network object which exist in world but has not been Initialized yet. + /// + [Preserve] + internal class CachedNetworkObject : IResettable + { + #region Types. + public enum ActionType + { + Unset = 0, + Spawn = 1, + Despawn = 2, + } + #endregion + + /// + /// True if cached object is nested during initialization. + /// + public bool IsInitializedNested => (ComponentId > 0); + + /// + /// True if a scene object. + /// + public bool IsSceneObject => (SceneId != NetworkObject.UNSET_SCENEID_VALUE); + + /// + /// True if this object has a parent. + /// + public bool HasParent => (ParentObjectId != null && ParentComponentId != null); + + public ushort CollectionId; + public int ObjectId; + public int InitializeOrder; + public int OwnerId; + public SpawnType SpawnType; + public DespawnType DespawnType; + public byte? ComponentId; + public int? ParentObjectId; + public byte? ParentComponentId; + public int? PrefabId; + public Vector3? Position; + public Quaternion? Rotation; + public Vector3? Scale; + public ulong SceneId; + public string SceneName = string.Empty; + public string ObjectName = string.Empty; + + /// + /// True if spawning. + /// + public ActionType Action { get; private set; } + + /// + /// Cached NetworkObject. + /// +#pragma warning disable 0649 + public NetworkObject NetworkObject; + /// + /// Reader containing payload for the NetworkObject behaviours. + /// + public PooledReader PayloadReader; + /// + /// Reader containing rpc links for the NetworkObject. + /// + public PooledReader RpcLinkReader; + /// + /// Reader containing sync values for the NetworkObject. + /// + public PooledReader SyncTypesReader; +#pragma warning restore 0649 + + public void InitializeSpawn(NetworkManager manager, ushort collectionId, int objectId, int initializeOrder, int ownerId, SpawnType objectSpawnType, byte? nobComponentId, int? parentObjectId, byte? parentComponentId, int? prefabId, Vector3? position, Quaternion? rotation, Vector3? scale, ulong sceneId, string sceneName, string objectName, ArraySegment payload, ArraySegment rpcLinks, ArraySegment syncTypes) + { + ResetState(); + Action = ActionType.Spawn; + CollectionId = collectionId; + ObjectId = objectId; + InitializeOrder = initializeOrder; + OwnerId = ownerId; + SpawnType = objectSpawnType; + ComponentId = nobComponentId; + ParentObjectId = parentObjectId; + ParentComponentId = parentComponentId; + PrefabId = prefabId; + Position = position; + Rotation = rotation; + Scale = scale; + SceneId = sceneId; + SceneName = sceneName; + ObjectName = objectName; + + if (payload.Count > 0) + PayloadReader = ReaderPool.Retrieve(payload, manager); + if (rpcLinks.Count > 0) + RpcLinkReader = ReaderPool.Retrieve(rpcLinks, manager); + if (syncTypes.Count > 0) + SyncTypesReader = ReaderPool.Retrieve(syncTypes, manager); + } + + /// + /// Initializes for a despawned NetworkObject. + /// + /// + public void InitializeDespawn(int objectId, DespawnType despawnType) + { + ResetState(); + Action = ActionType.Despawn; + DespawnType = despawnType; + ObjectId = objectId; + } + + /// + /// Resets values which could malform identify the cached object. + /// + public void ResetState() + { + SceneName = string.Empty; + ObjectName = string.Empty; + NetworkObject = null; + + ReaderPool.StoreAndDefault(ref PayloadReader); + ReaderPool.StoreAndDefault(ref RpcLinkReader); + ReaderPool.StoreAndDefault(ref SyncTypesReader); + } + + public void InitializeState() { } + + ~CachedNetworkObject() + { + NetworkObject = null; + } + } +} \ 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..ac022d0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 54cb2af8ab4557d479acb7fed98bd0c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Client/Object/ObjectCaching.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Debugging.meta b/Assets/FishNet/Runtime/Managing/Debugging.meta new file mode 100644 index 0000000..1fc5e77 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7fdb708ebb7c8be48bd50bb6fb36299e +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..57e4364 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs @@ -0,0 +1,51 @@ +using UnityEngine; + +namespace FishNet.Managing.Debugging +{ + /// + /// A container for debugging. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/DebugManager")] + public class DebugManager : MonoBehaviour + { + /// + /// True to write additional information about scene objects being sent in spawn messages. This is primarily used to resolve sceneId not found errors. + /// + [Tooltip("True to write additional information about scene objects being sent in spawn messages. This is primarily used to resolve sceneId not found errors.")] + public bool WriteSceneObjectDetails; + /// + /// True to validate written versus read length of Rpcs. Errors will be thrown if read length is not equal to written length. + /// + [Tooltip("True to validate written versus read length of Rpcs. Errors will be thrown if read length is not equal to written length.")] + public bool ValidateRpcLengths; + /// + /// True to disable RpcLinks for Observer RPCs. + /// + [Tooltip("True to disable RpcLinks for Observer RPCs.")] + public bool DisableObserversRpcLinks; + /// + /// True to disable RpcLinks for Target RPCs. + /// + [Tooltip("True to disable RpcLinks for Target RPCs.")] + public bool DisableTargetRpcLinks; + /// + /// True to disable RpcLinks for Server RPCs. + /// + [Tooltip("True to disable RpcLinks for Server RPCs.")] + public bool DisableServerRpcLinks; + /// + /// True to disable RpcLinks for Replicate RPCs. + /// + [Tooltip("True to disable RpcLinks for Replicate RPCs.")] + public bool DisableReplicateRpcLinks; + /// + /// True to disable RpcLinks for Reconcile RPCs. + /// + [Tooltip("True to disable RpcLinks for Reconcile RPCs.")] + public bool DisableReconcileRpcLinks; + + } + + +} 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..db0404c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6d0962ead4b02a34aae248fccce671ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Debugging/DebugManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Debugging/Editor.meta b/Assets/FishNet/Runtime/Managing/Debugging/Editor.meta new file mode 100644 index 0000000..64a9594 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f59bd8d1da17e3c49b541b79d1adfbf9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs b/Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs new file mode 100644 index 0000000..8b5ddf7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs @@ -0,0 +1,70 @@ +#if UNITY_EDITOR +using FishNet.Managing.Debugging; +using GameKit.Dependencies.Utilities; +using UnityEditor; +using UnityEngine; +using LayoutTools = GameKit.Dependencies.Utilities.EditorGuiLayoutTools; + +namespace FishNet.Managing.Editing +{ + [CustomEditor(typeof(DebugManager))] + public class DebugManagerEditor : Editor + { + private SerializedProperty _writeSceneObjectDetails; + private SerializedProperty _validateRpcLengths; + private SerializedProperty _disableObserversRpcLinks; + private SerializedProperty _disableTargetRpcLinks; + private SerializedProperty _disableServerRpcLinks; + private SerializedProperty _disableReplicateRpcLinks; + private SerializedProperty _disableReconcileRpcLinks; + + private void OnEnable() + { + _writeSceneObjectDetails = serializedObject.FindProperty(nameof(_writeSceneObjectDetails).MemberToPascalCase()); + _validateRpcLengths = serializedObject.FindProperty(nameof(_validateRpcLengths).MemberToPascalCase()); + _disableObserversRpcLinks = serializedObject.FindProperty(nameof(_disableObserversRpcLinks).MemberToPascalCase()); + _disableTargetRpcLinks = serializedObject.FindProperty(nameof(_disableTargetRpcLinks).MemberToPascalCase()); + _disableServerRpcLinks = serializedObject.FindProperty(nameof(_disableServerRpcLinks).MemberToPascalCase()); + _disableReplicateRpcLinks = serializedObject.FindProperty(nameof(_disableReplicateRpcLinks).MemberToPascalCase()); + _disableReconcileRpcLinks = serializedObject.FindProperty(nameof(_disableReconcileRpcLinks).MemberToPascalCase()); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + DebugManager DebugManager = (DebugManager)target; + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(DebugManager), typeof(DebugManager), false); + GUI.enabled = true; + + LayoutTools.AddHelpBox("Debug features will only be run in Unity Editor, and development builds. Enabling debug features will increase bandwidth consumption and likely create garbage allocations.", MessageType.Warning); + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Detail Writing",EditorStyles.boldLabel); + EditorGUI.indentLevel++; + LayoutTools.AddPropertyField(_writeSceneObjectDetails, "Scene Objects"); + EditorGUI.indentLevel--; + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Packet Validation",EditorStyles.boldLabel); + EditorGUI.indentLevel++; + LayoutTools.AddPropertyField(_validateRpcLengths, "Rpc Lengths"); + EditorGUI.indentLevel--; + + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Disable RpcLinks",EditorStyles.boldLabel); + EditorGUI.indentLevel++; + LayoutTools.AddPropertyField(_disableObserversRpcLinks, "ObserversRpcs"); + LayoutTools.AddPropertyField(_disableTargetRpcLinks, "TargetRpcs"); + LayoutTools.AddPropertyField(_disableServerRpcLinks, "ServerRpcs"); + LayoutTools.AddPropertyField(_disableReplicateRpcLinks, "ReplicateRpcs"); + LayoutTools.AddPropertyField(_disableReconcileRpcLinks, "ReconcileRpcs"); + EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs.meta new file mode 100644 index 0000000..6651f5b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e6de332294b6706489cb1de30b878ac7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Debugging/Editor/DebugManagerEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs b/Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs new file mode 100644 index 0000000..69d9025 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs @@ -0,0 +1,103 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +#if DEVELOPMENT +using FishNet.Managing.Logging; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using System; +using System.Collections.Generic; +using System.Text; +using FishNet.Transporting.Tugboat; + +namespace FishNet.Managing.Debugging +{ + internal class PacketIdHistory + { + /// + /// Last several non-split packetIds to be received on the client. + /// + private readonly Queue _serverPacketsReceived = new(); + /// + /// Last several non-split packetIds to be received on the server. + /// + private readonly Queue _clientPacketsReceived = new(); + /// + /// StringBuilder to limit garbage allocation. + /// + private static StringBuilder _stringBuilder = new(); + /// + /// Maximum number of packets allowed to be queued. + /// + private const int PACKET_COUNT = 5; + + /// + /// Resets data. + /// + internal void ResetState(bool packetsFromServer) + { + if (packetsFromServer) + _serverPacketsReceived.Clear(); + else + _clientPacketsReceived.Clear(); + } + + /// + /// Adds a packet to data. + /// + internal void ReceivedPacket(PacketId pId, bool packetFromServer) + { + Queue queue = (packetFromServer) ? _serverPacketsReceived : _clientPacketsReceived; + + queue.Enqueue(pId); + + while (queue.Count > PACKET_COUNT) + queue.Dequeue(); + } + + /// + /// Prints current data. + /// + internal string GetReceivedPacketIds(bool packetsFromServer, bool resetReceived = false) + { + string packetOriginTxt = (packetsFromServer) ? "from Server" : "from Client"; + + _stringBuilder.Clear(); + Queue queue = GetQueue(packetsFromServer); + + _stringBuilder.AppendLine($"The last {queue.Count} packets to arrive {packetOriginTxt} are:"); + foreach (PacketId item in queue) + _stringBuilder.AppendLine($"{item.ToString()}"); + + //Attach nob information. + _stringBuilder.Append($"The last parsed NetworkObject is "); + NetworkObject lastNob = Reader.LastNetworkObject; + if (lastNob != null) + _stringBuilder.Append($"Id {lastNob.ObjectId} on gameObject {lastNob.name}"); + else + _stringBuilder.Append("Unset"); + + //Attach nb information. + _stringBuilder.Append($", and NetworkBehaviour "); + NetworkBehaviour lastNb = Reader.LastNetworkBehaviour; + if (lastNb == null) + _stringBuilder.Append("Unset"); + else + _stringBuilder.Append($"{lastNb.GetType().Name}"); + + _stringBuilder.Append("."); + + if (resetReceived) + ResetState(packetsFromServer); + + return _stringBuilder.ToString(); + } + + /// + /// Returns which packet queue to use. + /// + private Queue GetQueue(bool packetsFromServer) => (packetsFromServer) ? _serverPacketsReceived : _clientPacketsReceived; + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs.meta b/Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs.meta new file mode 100644 index 0000000..741729f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: afc241e869d97a44f8339510586dce73 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Debugging/PacketIdHistory.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Editor.meta b/Assets/FishNet/Runtime/Managing/Editor.meta new file mode 100644 index 0000000..a2de8d4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13b772a9b225b1343bbb9313504db84f +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..19b69ee --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4e8e16b3e97106a4980b954c56d7bbc5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Editor/NetworkManagerEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Logging.meta b/Assets/FishNet/Runtime/Managing/Logging.meta new file mode 100644 index 0000000..d45b961 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6cb1c158bca76a644b857ca01845204b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Logging/Editor.meta b/Assets/FishNet/Runtime/Managing/Logging/Editor.meta new file mode 100644 index 0000000..bf9ab10 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 980bce33e6d8c5247bedb2ddc2310b77 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs b/Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs new file mode 100644 index 0000000..dc90c11 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs @@ -0,0 +1,67 @@ +#if UNITY_EDITOR +using GameKit.Dependencies.Utilities; +using UnityEditor; + +namespace FishNet.Managing.Logging.Editing +{ + [CustomEditor(typeof(LevelLoggingConfiguration), true)] + [CanEditMultipleObjects] + public class LevelLoggingConfigurationEditor : Editor + { + private SerializedProperty _isEnabled; + private SerializedProperty _addLocalTick; + + private SerializedProperty _addTimestamps; + private SerializedProperty _enableTimestampsInEditor; + + private SerializedProperty _developmentLogging; + private SerializedProperty _guiLogging; + private SerializedProperty _headlessLogging; + + protected virtual void OnEnable() + { + _isEnabled = serializedObject.FindProperty(nameof(_isEnabled).MemberToPascalCase()); + + _addLocalTick = serializedObject.FindProperty(nameof(_addLocalTick)); + + _addTimestamps = serializedObject.FindProperty(nameof(_addTimestamps)); + _enableTimestampsInEditor = serializedObject.FindProperty(nameof(_enableTimestampsInEditor)); + + _developmentLogging = serializedObject.FindProperty(nameof(_developmentLogging)); + _guiLogging = serializedObject.FindProperty(nameof(_guiLogging)); + _headlessLogging = serializedObject.FindProperty(nameof(_headlessLogging)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + EditorGUILayout.PropertyField(_isEnabled); + + if (_isEnabled.boolValue == false) + return; + + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(_addLocalTick); + + EditorGUILayout.PropertyField(_addTimestamps); + if (_addTimestamps.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_enableTimestampsInEditor); + EditorGUI.indentLevel--; + } + + EditorGUILayout.PropertyField(_developmentLogging); + EditorGUILayout.PropertyField(_guiLogging); + EditorGUILayout.PropertyField(_headlessLogging); + + EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs.meta b/Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs.meta new file mode 100644 index 0000000..cdf70f5 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b815d3a75a4bcc54b907a07e83c7b7cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Logging/Editor/LevelLoggingConfigurationEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs new file mode 100644 index 0000000..b8b86c2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs @@ -0,0 +1,196 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Documenting; +using System; +using System.Runtime.CompilerServices; +using System.Text; +using FishNet.Managing.Timing; +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. + /// + /// True to add localtick to logs. + /// + [Tooltip("True to add localtick to logs.")] + [SerializeField] + private bool _addLocalTick; + /// + /// True to add timestamps to logs. + /// + [Tooltip("True to add timestamps to logs.")] + [SerializeField] + private bool _addTimestamps = true; + /// + /// True to add timestamps when in editor. False to only include timestamps in builds. + /// + [Tooltip("True to add timestamps when in editor. False to only include timestamps in builds.")] + [SerializeField] + private bool _enableTimestampsInEditor; + /// + /// 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; + /// + /// Sequential stringbuilder for performance. + /// + private static StringBuilder _stringBuilder = new(); + #endregion + + [APIExclude] + public void LoggingConstructor(bool loggingEnabled, LoggingType development, LoggingType gui, LoggingType headless) + { + base.IsEnabled = loggingEnabled; + _developmentLogging = development; + _guiLogging = gui; + _headlessLogging = headless; + } + + /// + /// Initializes script for use. + /// + /// + public override void InitializeOnce() + { + byte currentHighest = (byte)LoggingType.Off; +#if UNITY_SERVER + currentHighest = Math.Max(currentHighest, (byte)_headlessLogging); +#elif DEVELOPMENT + currentHighest = Math.Max(currentHighest, (byte)_developmentLogging); +#else + 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.IsEnabled) + return false; + + if (!_initialized) + { +#if DEVELOPMENT + if (Application.isPlaying) + NetworkManagerExtensions.LogError("CanLog called before being initialized."); + else + return true; +#endif + return false; + } + + return ((byte)loggingType <= (byte)_highestLoggingType); + } + + /// + /// Logs a common value if can log. + /// + public override void Log(string value) + { + if (CanLog(LoggingType.Common)) + Debug.Log(AddSettingsToLog(value)); + } + + /// + /// Logs a warning value if can log. + /// + public override void LogWarning(string value) + { + if (CanLog(LoggingType.Warning)) + Debug.LogWarning(AddSettingsToLog(value)); + } + + /// + /// Logs an error value if can log. + /// + public override void LogError(string value) + { + if (CanLog(LoggingType.Error)) + { + Debug.LogError(AddSettingsToLog(value)); + } + } + + /// + /// Clones this logging configuration. + /// + /// + public override LoggingConfiguration Clone() + { + LevelLoggingConfiguration copy = ScriptableObject.CreateInstance(); + copy.LoggingConstructor(base.IsEnabled, _developmentLogging, _guiLogging, _headlessLogging); + copy._addTimestamps = _addTimestamps; + copy._addLocalTick = _addLocalTick; + copy._enableTimestampsInEditor = _enableTimestampsInEditor; + + return copy; + } + + /// + /// Adds onto logging message if settings are enabled to. + /// + private string AddSettingsToLog(string value) + { + _stringBuilder.Clear(); + + + if (_addTimestamps && (!Application.isEditor || _enableTimestampsInEditor)) + _stringBuilder.Append($"[{DateTime.Now:yyyy.MM.dd HH:mm:ss}] "); + + if (_addLocalTick) + { + TimeManager tm = InstanceFinder.TimeManager; + uint tick = (tm == null) ? TimeManager.UNSET_TICK : tm.LocalTick; + _stringBuilder.Append($"LocalTick [{tick}] "); + } + + //If anything was added onto string builder then add value, and set value to string builder. + if (_stringBuilder.Length > 0) + { + _stringBuilder.Append(value); + value = _stringBuilder.ToString(); + } + + return value; + } + } +} \ 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..b00755f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 925fc096350b81f4f82f4fe4ac0c4dda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Logging/LevelLoggingConfiguration.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs new file mode 100644 index 0000000..ec782d6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs @@ -0,0 +1,63 @@ +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 IsEnabled = true; + [Obsolete("Use IsEnabled.")] //Remove V5 + public bool LoggingEnabled + { + get => IsEnabled; + set => IsEnabled = value; + } + #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..392fa3a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 438d7e99b7655114891d4fa6e9f68c7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Logging/LoggingConfiguration.cs + uploadId: 762203 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..48788bd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8bf0a7ab3f60fe44984fcfd16d8fe7b4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Logging/LoggingType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs new file mode 100644 index 0000000..53af0ed --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs @@ -0,0 +1,190 @@ +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. + /// + /// Type of logging being filtered. + /// + internal bool InternalCanLog(LoggingType loggingType) + { + return _logging.CanLog(loggingType); + } + + /// + /// Performs a common log, should logging settings permit it. + /// + internal void InternalLog(string value) + { + _logging.Log(value); + } + + /// + /// Performs a log using the loggingType, should logging settings permit it. + /// + internal void InternalLog(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. + /// + internal void InternalLogWarning(string value) + { + _logging.LogWarning(value); + } + + /// + /// Performs an error log, should logging settings permit it. + /// + internal void InternalLogError(string value) + { + _logging.LogError(value); + } + } + + public static class NetworkManagerExtensions + { + + /// + /// True if can log for loggingType. + /// + + internal static bool CanLog(this NetworkManager networkManager, LoggingType loggingType) + { + if (GetNetworkManager(ref networkManager)) + return networkManager.InternalCanLog(loggingType); + else + return false; + } + + /// + /// Performs a log using the loggingType, should logging settings permit it. + /// + + public static void Log(this NetworkManager networkManager, LoggingType loggingType, string value) + { + if (loggingType == LoggingType.Common) + networkManager.Log(value); + else if (loggingType == LoggingType.Warning) + networkManager.LogWarning(value); + else if (loggingType == LoggingType.Error) + networkManager.LogError(value); + } + + /// + /// Performs a common log, should logging settings permit it. + /// + + public static void Log(this NetworkManager networkManager, string message) + { + if (GetNetworkManager(ref networkManager)) + networkManager.InternalLog(message); + else + Debug.Log(message); + } + /// + /// Performs a warning log, should logging settings permit it. + /// + + public static void LogWarning(this NetworkManager networkManager, string message) + { + if (GetNetworkManager(ref networkManager)) + networkManager.InternalLogWarning(message); + else + Debug.LogWarning(message); + } + + /// + /// Performs an error log, should logging settings permit it. + /// + + public static void LogError(this NetworkManager networkManager, string message) + { + if (GetNetworkManager(ref networkManager)) + networkManager.InternalLogError(message); + else + Debug.LogError(message); + } + + /// + /// Gets a NetworkManager, first using a preferred option. + /// + /// True if a NetworkManager was found. + private static bool GetNetworkManager(ref NetworkManager preferredNm) + { + if (preferredNm != null) + return true; + + preferredNm = InstanceFinder.NetworkManager; + return (preferredNm != null); + } + + #region Backwards compatibility. + /// + /// Performs a common log, should logging settings permit it. + /// + + public static void Log(string msg) => NetworkManagerExtensions.Log(null, msg); + /// + /// Performs a warning log, should logging settings permit it. + /// + + public static void LogWarning(string msg) => NetworkManagerExtensions.LogWarning(null, msg); + /// + /// Performs an error log, should logging settings permit it. + /// + + public static void LogError(string msg) => NetworkManagerExtensions.LogError(null, msg); + /// + /// True if can log for loggingType. + /// + + public static bool CanLog(LoggingType lt) => NetworkManagerExtensions.CanLog(null, lt); + + + #endregion + } + +} \ 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..6ca9180 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4200d74f31ee8144fb606ce88ad1b747 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/NetworkManager.Logging.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs new file mode 100644 index 0000000..12e95b3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs @@ -0,0 +1,145 @@ +using FishNet.Object; +using FishNet.Utility.Performance; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing +{ + public sealed partial class NetworkManager : MonoBehaviour + { + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(NetworkObject prefab, Transform parent, bool asServer) => GetPooledInstantiated(prefab.PrefabId, prefab.SpawnableCollectionId, ObjectPoolRetrieveOption.MakeActive, parent, position: null, rotation: null, scale: null, asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(NetworkObject prefab, bool asServer) => GetPooledInstantiated(prefab.PrefabId, prefab.SpawnableCollectionId, ObjectPoolRetrieveOption.MakeActive, parent: null, position: null, rotation: null, scale: null, asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(NetworkObject prefab, Vector3 position, Quaternion rotation, bool asServer) => GetPooledInstantiated(prefab.PrefabId, prefab.SpawnableCollectionId, ObjectPoolRetrieveOption.MakeActive, parent: null, position, rotation, scale: null, asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(GameObject prefab, bool asServer) + { + if (SetPrefabInformation(prefab, out _, out int prefabId, out ushort collectionId)) + return GetPooledInstantiated(prefabId, collectionId, ObjectPoolRetrieveOption.MakeActive, parent: null, position: null, rotation: null, scale: null, asServer); + //Fallthrough, failure. + return null; + } + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(GameObject prefab, Transform parent, bool asServer) + { + if (SetPrefabInformation(prefab, out _, out int prefabId, out ushort collectionId)) + return GetPooledInstantiated(prefabId, collectionId, ObjectPoolRetrieveOption.MakeActive, parent, position: null, rotation: null, scale: null, asServer); + //Fallthrough, failure. + return null; + } + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(GameObject prefab, Vector3 position, Quaternion rotation, bool asServer) + { + if (SetPrefabInformation(prefab, out _, out int prefabId, out ushort collectionId)) + return GetPooledInstantiated(prefabId, collectionId, ObjectPoolRetrieveOption.MakeActive, parent: null, position, rotation, scale: null, asServer); + //Fallthrough, failure. + return null; + } + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(NetworkObject prefab, Vector3 position, Quaternion rotation, Transform parent, bool asServer) => GetPooledInstantiated(prefab.PrefabId, prefab.SpawnableCollectionId, ObjectPoolRetrieveOption.MakeActive, parent, position, rotation, scale: null, asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent, bool asServer) + { + if (SetPrefabInformation(prefab, out _, out int prefabId, out ushort collectionId)) + return GetPooledInstantiated(prefabId, collectionId, ObjectPoolRetrieveOption.MakeActive, parent, position, rotation, scale: null, asServer); + //Fallthrough, failure. + return null; + } + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(int prefabId, ushort collectionId, bool asServer) => GetPooledInstantiated(prefabId, collectionId, ObjectPoolRetrieveOption.MakeActive, parent: null, position: null, rotation: null, scale: null, asServer: asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(int prefabId, ushort collectionId, Vector3 position, Quaternion rotation, bool asServer) => GetPooledInstantiated(prefabId, collectionId, ObjectPoolRetrieveOption.MakeActive, parent: null, position, rotation, scale: null, asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + /// True to make the NetworkObject active if not already. Using false will not prevent an object from activating via instantation, but rather indicates to not set active manually prior to returning a NetworkObject. + [Obsolete("Use GetPooledInstantiated(int, ushort, RetrieveOption, parent, Vector3?, Quaternion? Vector3?, bool) instead.")] //Remove in V5 + public NetworkObject GetPooledInstantiated(int prefabId, ushort collectionId, Transform parent, Vector3? position, Quaternion? rotation, Vector3? scale, bool makeActive, bool asServer) => _objectPool.RetrieveObject(prefabId, collectionId, parent, position, rotation, scale, makeActive, asServer); + + /// + /// Returns an instantiated or pooled object using supplied values. When a value is not specified it uses default values to the prefab or NetworkManager. + /// + public NetworkObject GetPooledInstantiated(int prefabId, ushort collectionId, ObjectPoolRetrieveOption options, Transform parent, Vector3? position, Quaternion? rotation, Vector3? scale, bool asServer) => _objectPool.RetrieveObject(prefabId, collectionId, options, parent, position, rotation, scale, 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); + + /// + /// Stores a NetworkObject if it has pooling enabled, otherwise destroys it. + /// + /// Object which was instantiated. + /// True to store for the server. + public void StorePooledOrDestroyInstantiated(NetworkObject instantiated, bool asServer) + { + if (instantiated.GetDefaultDespawnType() == DespawnType.Destroy) + Destroy(instantiated.gameObject); + else + _objectPool.StoreObject(instantiated, asServer); + } + + /// + /// 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) => _objectPool.CacheObjects(prefab, count, asServer); + + /// + /// Outputs a prefab, along with it's Id and collectionId. Returns if the information could be found. + /// + private bool SetPrefabInformation(GameObject prefab, out NetworkObject nob, out int prefabId, out ushort collectionId) + { + if (!prefab.TryGetComponent(out nob)) + { + prefabId = 0; + collectionId = 0; + InternalLogError($"NetworkObject was not found on {prefab}. An instantiated NetworkObject cannot be returned."); + return false; + } + else + { + prefabId = nob.PrefabId; + collectionId = nob.SpawnableCollectionId; + return true; + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs.meta b/Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs.meta new file mode 100644 index 0000000..b07810f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a5f425d13e7c7d648971c4d6c85af23b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/NetworkManager.ObjectPooling.cs + uploadId: 762203 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..0375795 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cb7fdb186794b674788273f3b211ec5b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/NetworkManager.Pro.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs new file mode 100644 index 0000000..8b4139e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs @@ -0,0 +1,342 @@ +using FishNet.Managing.Object; +using FishNet.Object; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityComponent = UnityEngine.Component; + + +namespace FishNet.Managing +{ + public partial class NetworkManager : MonoBehaviour + { + #region Public. + #region Obsoletes + [Obsolete("Use IsClientOnlyStarted. Note the difference between IsClientOnlyInitialized and IsClientOnlyStarted.")] + public bool IsClientOnly => IsClientOnlyStarted; + [Obsolete("Use IsServerOnlyStarted. Note the difference between IsServerOnlyInitialized and IsServerOnlyStarted.")] + public bool IsServerOnly => IsServerOnlyStarted; + [Obsolete("Use IsHostStarted. Note the difference between IsHostInitialized and IsHostStarted.")] + public bool IsHost => IsHostStarted; + [Obsolete("Use IsClientStarted. Note the difference between IsClientInitialized and IsClientStarted.")] + public bool IsClient => IsClientStarted; + [Obsolete("Use IsServerStarted. Note the difference between IsServerInitialized and IsServerStarted.")] + public bool IsServer => IsServerStarted; + #endregion + + /// + /// True if server is started. + /// + public bool IsServerStarted => ServerManager.Started; + /// + /// True if only the server is started. + /// + public bool IsServerOnlyStarted => (IsServerStarted && !IsClientStarted); + /// + /// True if the client is authenticated. + /// + public bool IsClientStarted => (ClientManager.Started && ClientManager.Connection.IsAuthenticated); + /// + /// True if only the client is authenticated. + /// + public bool IsClientOnlyStarted => (!IsServerStarted && IsClientStarted); + /// + /// True if client is authenticated, and the server is started. + /// + public bool IsHostStarted => (IsServerStarted && IsClientStarted); + /// + /// True if client nor server are started. + /// + public bool IsOffline => (!IsServerStarted && !IsClientStarted); + + #endregion + + #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(); + /// + /// 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(); + /// + /// Currently registered components. + /// + private Dictionary _registeredComponents = new(); + #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) + { + InternalLogError($"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; + //If not found yet make a pending invoke. + if (!TryGetInstance(out result)) + { + string tName = GetInstanceName(); + List> handlers; + if (!_pendingInvokes.TryGetValue(tName, out handlers)) + { + handlers = new(); + _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 from recreation. + } + /// + /// Returns if an instance exists for type. + /// + /// Type to check. + /// + public bool HasInstance() where T : UnityComponent + { + return TryGetInstance(out _); + } + + /// + /// Returns class of type from registered instances. + /// A warning will display if not found. + /// + /// Type to get. + /// + public T GetInstance() where T : UnityComponent + { + T result; + if (TryGetInstance(out result)) + return result; + else + InternalLogWarning($"Component {GetInstanceName()} is not registered. To avoid this warning use TryGetInstance(T)."); + + return default(T); + } + /// + /// Returns class of type from registered instances. + /// + /// Outputted component. + /// Type to get. + /// True if was able to get instance. + public bool TryGetInstance(out T result) where T : UnityComponent + { + string tName = GetInstanceName(); + if (_registeredComponents.TryGetValue(tName, out UnityComponent v)) + { + result = (T)v; + return true; + } + else + { + result = default; + return false; + } + } + /// + /// 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) + { + InternalLogWarning($"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(); + } + } + } + + /// + /// Tries to registers a new component to this NetworkManager. + /// This will not register the instance if another already exists. + /// + /// Type to register. + /// Reference of the component being registered. + /// True if was able to register, false if an instance is already registered. + + public bool TryRegisterInstance(T component) where T : UnityComponent + { + string tName = GetInstanceName(); + if (_registeredComponents.ContainsKey(tName)) + return false; + else + RegisterInstance(component, false); + + return true; + } + + /// + /// 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..f85e1ed --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5761633dda73e7447a3a41b87354d06e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/NetworkManager.QOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/NetworkManager.cs b/Assets/FishNet/Runtime/Managing/NetworkManager.cs new file mode 100644 index 0000000..d22b6b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.cs @@ -0,0 +1,533 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif + +#if UNITY_EDITOR +using FishNet.Editing.PrefabCollectionGenerator; +using UnityEditor; +#endif + +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.Object; +using FishNet.Documenting; +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.Managing.Statistic; +using FishNet.Utility.Performance; +using FishNet.Component.ColliderRollback; +using FishNet.Managing.Predicting; +using GameKit.Dependencies.Utilities; + + + +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. + /// + /// 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(); + + /// + /// Currently initialized NetworkManagers. + /// + public static IReadOnlyList 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; + } + } + + /// + /// 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; } + + /// + /// 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(); + #endregion + + #region Internal. + /// + /// Starting index for RpcLinks. + /// + internal static ushort StartingRpcLinkIndex; +#if DEVELOPMENT + /// + /// Logs data about parser to help debug. + /// + internal PacketIdHistory PacketIdHistory = new(); +#endif + #endregion + + #region Serialized. +#if UNITY_EDITOR + /// + /// 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; +#endif + /// + /// 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. + /// + public ObjectPool ObjectPool => _objectPool; + + [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. + /// + /// Version of this release. + /// + public const string FISHNET_VERSION = "4.6.9"; + /// + /// Maximum framerate allowed. + /// + internal const ushort MAXIMUM_FRAMERATE = 500; + #endregion + + private void Awake() + { + InitializeLogging(); + if (!ValidateSpawnablePrefabs(true)) + return; + + if (StartingRpcLinkIndex == 0) + StartingRpcLinkIndex = (ushort)(Enums.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(initializeAdded: false); + 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(), checkForDuplicates: false, initializeAdded: false); + instancedDpo.Sort(); + SpawnablePrefabs = instancedDpo; + } + + _canPersist = CanInitialize(); + if (!_canPersist) + return; + + if (TryGetComponent(out _)) + InternalLogError($"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(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 && !UNITY_EDITOR + ushort minimumServerFramerate = (ushort)(TimeManager.TickRate + 15); + 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(); + //Let's object pooler do regular work. + _objectPool.LateUpdate(); + } + + + /// + /// 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) + { + InternalLog($"NetworkManager on object {gameObject.name} is being destroyed due to persistence type {_persistence}. A NetworkManager instance already exist on {firstInstance.name}."); + DestroyImmediate(gameObject); + //This one is being destroyed because its the newest. + return false; + } + //If to destroy the oldest. + else if (_persistence == PersistenceType.DestroyOldest) + { + InternalLog($"NetworkManager on object {firstInstance.name} is being destroyed due to persistence type {_persistence}. A NetworkManager instance has been created on {gameObject.name}."); + DestroyImmediate(firstInstance.gameObject); + //This being the new one will persist, allow initialization. + return true; + } + //Unhandled. + else + { + InternalLog($"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)) + { + //First try to fetch the file, only if editor and not in play mode. +#if UNITY_EDITOR + if (!ApplicationState.IsPlaying()) + { + SpawnablePrefabs = Generator.GetDefaultPrefabObjects(); + if (SpawnablePrefabs != null) + { + Debug.Log($"SpawnablePrefabs was set to DefaultPrefabObjects automatically on object {gameObject.name} in scene {gameObject.scene.name}."); + EditorUtility.SetDirty(this); + return true; + } + } +#endif + //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, int transportIndex = -1) + { + //True to dispose all connections. + bool disposeAll = (transportIndex < 0); + List cache = CollectionCaches.RetrieveList(); + + + foreach (KeyValuePair kvp in clients) + { + NetworkConnection value = kvp.Value; + //If to check transport index. + if (!disposeAll) + { + if (value.TransportIndex == transportIndex) + { + cache.Add(kvp.Key); + value.ResetState(); + } + } + //Not using transport index, no check required. + else + { + value.ResetState(); + } + } + + //If all are being disposed the collection can be cleared. + if (disposeAll) + { + clients.Clear(); + } + //Otherwise, only remove those which were disposed. + else + { + foreach (int item in cache) + clients.Remove(item); + } + + CollectionCaches.Store(cache); + } + + #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..8524096 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/NetworkManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d2c95dfde7d73b54dbbdc23155d35d36 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/NetworkManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Object.meta b/Assets/FishNet/Runtime/Managing/Object.meta new file mode 100644 index 0000000..28e292b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b0cc8067328d24b4ba59a946ec4b8622 +folderAsset: yes +DefaultImporter: + externalObjects: {} + 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..6ade3f5 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 76840b2b810d8fc45aeccef03122763c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/DualPrefab.cs + uploadId: 762203 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..fe1b5d1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs @@ -0,0 +1,525 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using System; +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using FishNet.Serializing.Helping; +using UnityEngine; + +namespace FishNet.Managing.Object +{ + public abstract partial class ManagedObjects + { + #region Consts. + /// + /// Number of bytes to reserve for a predicted spawn length. + /// + internal const byte PREDICTED_SPAWN_BYTES = 2; + #endregion + + /// + /// Reads and outputs a transforms values. + /// + protected void ReadTransformProperties(Reader reader, out Vector3? localPosition, out Quaternion? localRotation, out Vector3? localScale) + { + //Read changed. + TransformPropertiesFlag tpf = (TransformPropertiesFlag)reader.ReadUInt8Unpacked(); + //Position. + if (tpf.FastContains(TransformPropertiesFlag.Position)) + localPosition = reader.ReadVector3(); + else + localPosition = null; + //Rotation. + if (tpf.FastContains(TransformPropertiesFlag.Rotation)) + localRotation = reader.ReadQuaternion(NetworkManager.ServerManager.SpawnPacking.Rotation); + else + localRotation = null; + //Scale. + if (tpf.FastContains(TransformPropertiesFlag.Scale)) + localScale = reader.ReadVector3(); + else + localScale = null; + } + + /// + /// Writes a spawn to a client or server. + /// If connection is not null the spawn is sent ot a client, otherwise it will be considered a predicted spawn. + /// + /// True if spawn was written. + internal bool WriteSpawn(NetworkObject nob, PooledWriter writer, NetworkConnection connection) + { + writer.WritePacketIdUnpacked(PacketId.ObjectSpawn); + + ReservedLengthWriter asClientReservedWriter = ReservedWritersExtensions.Retrieve(); + bool predictedSpawn = (connection == null); + + if (predictedSpawn) + asClientReservedWriter.Initialize(writer, PREDICTED_SPAWN_BYTES); + + 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; + + if (connection == nob.PredictedSpawner) + st |= SpawnType.IsPredictedSpawner; + + //Call before writing SpawnType so nested can be appended to it if needed. + PooledWriter nestedWriter = WriteNestedSpawn(nob, ref st); + + writer.WriteUInt8Unpacked((byte)st); + //Write parent here if writer for parent is valid. + if (nestedWriter != null) + { + writer.WriteArraySegment(nestedWriter.GetArraySegment()); + WriterPool.Store(nestedWriter); + } + + writer.WriteSpawnedNetworkObject(nob); + writer.WriteNetworkConnection(nob.Owner); + + //Properties on the transform which diff from serialized value. + WriteChangedTransformProperties(nob, sceneObject, writer); + + /* Writing a scene object. */ + if (sceneObject) + { + writer.WriteUInt64Unpacked(nob.SceneId); +#if DEVELOPMENT + CheckWriteSceneObjectDetails(nob, writer); +#endif + } + /* Writing a spawned object. */ + else + { + writer.WriteNetworkObjectId(nob.PrefabId); + } + + NetworkConnection payloadSender = (predictedSpawn) ? NetworkManager.EmptyConnection : connection; + WritePayload(payloadSender, nob, writer); + + /* RPCLinks and SyncTypes are ONLY written by the server. + * Although not necessary, both sides will write the length + * to keep the reading of spawns consistent. */ + WriteRpcLinks(nob, writer); + WriteSyncTypesForSpawn(nob, writer, connection); + + bool canWrite; + //Need to validate predicted spawn length. + if (predictedSpawn) + { + int maxContentLength; + if (PREDICTED_SPAWN_BYTES == 2) + { + maxContentLength = ushort.MaxValue; + } + else +#pragma warning disable CS0162 // Unreachable code detected + { + NetworkManager.LogError($"Unhandled spawn bytes value of {PREDICTED_SPAWN_BYTES}."); + maxContentLength = 0; + } +#pragma warning restore CS0162 // Unreachable code detected + + //Too much content; this really should absolutely never happen. + canWrite = (asClientReservedWriter.Length <= maxContentLength); + if (!canWrite) + NetworkManager.LogError($"A single predicted spawns may not exceed {maxContentLength} bytes in length. Written length is {asClientReservedWriter.Length}. Predicted spawn for {nob.name} will be despawned immediately."); + //Not too large. + else + asClientReservedWriter.WriteLength(); + } + + //Not predicted, server can always write. + else + { + canWrite = true; + } + + asClientReservedWriter.Store(); + return canWrite; + } + + /// + /// Writes RPCLinks for a NetworkObject. + /// + protected void WriteRpcLinks(NetworkObject nob, PooledWriter writer) + { + ReservedLengthWriter rw = ReservedWritersExtensions.Retrieve(); + + rw.Initialize(writer, NetworkBehaviour.RPCLINK_RESERVED_BYTES); + + if (NetworkManager.IsServerStarted) + { + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + nb.WriteRpcLinks(writer); + } + + rw.WriteLength(); + + rw.Store(); + } + + /// + /// Reads RpcLinks from a spawn into an arraySegment. + /// + protected ArraySegment ReadRpcLinks(PooledReader reader) + { + uint segmentSize = ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.RPCLINK_RESERVED_BYTES); + return reader.ReadArraySegment((int)segmentSize); + } + + /// + /// Writes SyncTypes for a NetworkObject. + /// + protected void WriteSyncTypesForSpawn(NetworkObject nob, PooledWriter writer, NetworkConnection connection) + { + ReservedLengthWriter rw = ReservedWritersExtensions.Retrieve(); + + //SyncTypes. + rw.Initialize(writer, NetworkBehaviour.SYNCTYPE_RESERVE_BYTES); + + if (NetworkManager.IsServerStarted) + { + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + nb.WriteSyncTypesForSpawn(writer, connection); + } + + rw.WriteLength(); + rw.Store(); + } + + /// + /// Reads SyncTypes from a spawn into an arraySegment. + /// + protected ArraySegment ReadSyncTypesForSpawn(PooledReader reader) + { + uint segmentSize = ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.SYNCTYPE_RESERVE_BYTES); + return reader.ReadArraySegment((int)segmentSize); + } + + /// + /// Writers a nested spawn and returns writer used. + /// If nested was not written null is returned. + /// + internal PooledWriter WriteNestedSpawn(NetworkObject nob, ref SpawnType st) + { + //Check to write parent behaviour or nob. + NetworkBehaviour parentNb; + Transform t = nob.transform.parent; + if (t != null) + { + parentNb = nob.CurrentParentNetworkBehaviour; + /* 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) + { + return null; + } + //No parent. + else + { + if (!parentNb.IsSpawned) + { + NetworkManager.LogWarning($"Parent {t.name} is not spawned. {nob.name} will not have it's parent sent in the spawn message."); + return null; + } + else + { + st |= SpawnType.Nested; + PooledWriter writer = WriterPool.Retrieve(); + writer.WriteUInt8Unpacked(nob.ComponentIndex); + writer.WriteNetworkBehaviour(parentNb); + return writer; + } + } + } + //CurrentNetworkBehaviour is not set. + else + { + return null; + } + } + + /// + /// If flags indicate there is a nested spawn the objectId and NetworkBehaviourId are output. + /// Otherwise, output value sare set to null. + /// + internal void ReadNestedSpawnIds(PooledReader reader, SpawnType st, out byte? nobComponentIndex, out int? parentObjectId, out byte? parentComponentIndex, HashSet readSpawningObjects = null) + { + if (st.FastContains(SpawnType.Nested)) + { + nobComponentIndex = reader.ReadUInt8Unpacked(); + reader.ReadNetworkBehaviour(out int objectId, out byte componentIndex, readSpawningObjects); + if (objectId != NetworkObject.UNSET_OBJECTID_VALUE) + { + parentObjectId = objectId; + parentComponentIndex = componentIndex; + return; + } + } + + //Fall through, not nested. + nobComponentIndex = null; + parentObjectId = null; + parentComponentIndex = null; + } + + /// + /// Finishes reading a scene object. + /// + protected void ReadSceneObjectId(PooledReader reader, out ulong sceneId) + { + sceneId = reader.ReadUInt64Unpacked(); + } + + /// + /// Writes changed transform proeprties to writer. + /// + protected void WriteChangedTransformProperties(NetworkObject nob, bool sceneObject, Writer headerWriter) + { + /* Write changed transform properties. */ + TransformPropertiesFlag tpf; + /* If a scene object or nested during initialization then + * write changes compared to initialized values. */ + if (sceneObject || nob.InitializedParentNetworkBehaviour != null) + { + tpf = nob.GetTransformChanges(nob.SerializedTransformProperties); + } + else + { + //This should not be possible when spawning non-nested. + if (nob.PrefabId == NetworkObject.UNSET_PREFABID_VALUE) + { + NetworkManager.LogWarning($"NetworkObject {nob.ToString()} unexpectedly has an unset PrefabId while it's not nested. Please report this warning."); + tpf = TransformPropertiesFlag.Everything; + } + else + { + PrefabObjects po = NetworkManager.GetPrefabObjects(nob.SpawnableCollectionId, false); + tpf = nob.GetTransformChanges(po.GetObject(asServer: true, nob.PrefabId).gameObject); + } + } + + headerWriter.WriteUInt8Unpacked((byte)tpf); + //If properties have changed. + if (tpf != TransformPropertiesFlag.Unset) + { + //Write any changed properties. + if (tpf.FastContains(TransformPropertiesFlag.Position)) + headerWriter.WriteVector3(nob.transform.localPosition); + if (tpf.FastContains(TransformPropertiesFlag.Rotation)) + headerWriter.WriteQuaternion(nob.transform.localRotation, NetworkManager.ServerManager.SpawnPacking.Rotation); + if (tpf.FastContains(TransformPropertiesFlag.Scale)) + headerWriter.WriteVector3(nob.transform.localScale); + } + } + + /// + /// Writes a despawn. + /// + protected void WriteDespawn(NetworkObject nob, DespawnType despawnType, Writer everyoneWriter) + { + everyoneWriter.WritePacketIdUnpacked(PacketId.ObjectDespawn); + everyoneWriter.WriteNetworkObjectForDespawn(nob, despawnType); + } + + /// + /// Finds a scene NetworkObject and sets transform values. + /// + internal NetworkObject GetSceneNetworkObject(ulong sceneId, string sceneName, string objectName) + { + NetworkObject nob; + SceneObjects_Internal.TryGetValueIL2CPP(sceneId, out nob); + //If found in scene objects. + if (nob == null) + { +#if DEVELOPMENT + string missingObjectDetails = (sceneName == string.Empty) ? "For more information on the missing object add DebugManager to your NetworkManager and enable WriteSceneObjectDetails" : $"Scene containing the object is '{sceneName}', object name is '{objectName}"; + NetworkManager.LogError($"SceneId of {sceneId} not found in SceneObjects. {missingObjectDetails}. 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."); +#else + 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."); +#endif + } + + 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, 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. Add a PredictedSpawn component to the object and configure appropriately."); + + if (reader != null) + reader.Clear(); + return false; + } + + // //Parenting is not yet supported. + // if (nob.CurrentParentNetworkBehaviour != 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."); + // + // if (reader != null) + // reader.Clear(); + // return false; + // } + + //Nested nobs not yet supported. + if (nob.InitializedNestedNetworkObjects.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."); + + if (reader != null) + 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.InitializedNestedNetworkObjects.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.OnTryDespawnServer(despawner)) || (!asServer && !nob.PredictedSpawn.OnTryDespawnClient())) + { + return false; + } + + return true; + } + + /// + /// Reads a payload for a NetworkObject. + /// + internal void ReadPayload(NetworkConnection sender, NetworkObject nob, PooledReader reader, int? payloadLength = null) + { + if (!payloadLength.HasValue) + payloadLength = (int)ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.PAYLOAD_RESERVE_BYTES); + //If there is a payload. + if (payloadLength > 0) + { + if (nob != null) + { + foreach (NetworkBehaviour networkBehaviour in nob.NetworkBehaviours) + networkBehaviour.ReadPayload(sender, reader); + } + //NetworkObject could be null if payload is for a predicted spawn. + else + { + reader.Skip((int)payloadLength); + } + } + } + + /// + /// Reads the payload returning it as an arraySegment. + /// + /// + internal ArraySegment ReadPayload(PooledReader reader) + { + int payloadLength = (int)ReservedLengthWriter.ReadLength(reader, NetworkBehaviour.PAYLOAD_RESERVE_BYTES); + return reader.ReadArraySegment(payloadLength); + } + + /// + /// /Writers a payload for a NetworkObject. + /// + protected void WritePayload(NetworkConnection sender, NetworkObject nob, PooledWriter writer) + { + ReservedLengthWriter rw = ReservedWritersExtensions.Retrieve(); + + rw.Initialize(writer, NetworkBehaviour.PAYLOAD_RESERVE_BYTES); + + foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + nb.WritePayload(sender, writer); + + rw.WriteLength(); + } + + // /// + // /// Writes a payload for a NetworkObject. + // /// + // protected ArraySegment ReadPayload(PooledReader reader) + // { + // PooledWriter nbWriter = WriterPool.Retrieve(); + // foreach (NetworkBehaviour nb in nob.NetworkBehaviours) + // { + // nbWriter.Reset(); + // nb.WritePayload(conn, nbWriter); + // if (nbWriter.Length > 0) + // { + // writer.WriteUInt8Unpacked(nb.ComponentIndex); + // writer.WriteArraySegment(nbWriter.GetArraySegment()); + // } + // } + // } + } +} \ No newline at end of file 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..724d616 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1fcb759226359ad48926ff17cbf0ec6d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/ManagedObjects.Spawning.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs new file mode 100644 index 0000000..30d66b8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs @@ -0,0 +1,507 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Component.Observing; +using FishNet.Connection; +using FishNet.Managing.Logging; +using FishNet.Managing.Utility; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Security.Cryptography; +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(); + #endregion + + #region Protected. + /// + /// Returns the next ObjectId to use. + /// + protected internal virtual bool GetNextNetworkObjectId(out int nextNetworkObjectId) + { + nextNetworkObjectId = NetworkObject.UNSET_OBJECTID_VALUE; + return false; + } + + /// + /// 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_Internal = new(); + /// + /// 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. + /// + public IReadOnlyDictionary SceneObjects => SceneObjects_Internal; + #endregion + + #region Private. + /// + /// Cached HashGrid. Will be null if not used. + /// + private HashGrid _hashGrid; + #endregion + + protected virtual void Initialize(NetworkManager manager) + { + NetworkManager = manager; + manager.TryGetInstance(out _hashGrid); + } + + /// + /// 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 NetworkObjectDestroyed(NetworkObject nob, bool asServer) + { + if (nob == null) + return; + + RemoveFromSpawned(nob, fromOnDestroy: true, asServer); + } + + /// + /// Removes a NetworkedObject from spawned. + /// + protected virtual void RemoveFromSpawned(NetworkObject nob, bool fromOnDestroy, bool asServer) + { + Spawned.Remove(nob.ObjectId); + //Do the same with SceneObjects. + if (fromOnDestroy && 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; + } + + /* If not asServer and the object is not initialized on client + * then it likely is already despawned. This bit of code should + * never be reached as checks should be placed before-hand. */ + if (!asServer && !nob.IsClientInitialized) + { + NetworkManager.LogError($"Object {nob.ToString()} is already despawned. Please report this error."); + return; + } + + + //True if should be destroyed, false if deactivated. + bool destroy = false; + bool wasRemovedFromPending = 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.IsServerStarted; + //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. */ + wasRemovedFromPending = NetworkManager.ServerManager.Objects.RemoveFromPending(nob); + destroy = (!isServer || wasRemovedFromPending); + } + } + } + + TryUnsetParent(); + /* If this had a parent set at runtime then + * unset parent before checks are completed. + * If we did not do this then this nob would + * just be disabled beneath its runtime parent, + * when it should be pooled separately or destroyed. */ + void TryUnsetParent() + { + if (!asServer || wasRemovedFromPending) + { + if (nob.RuntimeParentNetworkBehaviour != null) + { + nob.UnsetParent(); + /* DespawnType also has to be updated to use default + * for the networkObject since this despawn is happening + * automatically. */ + despawnType = nob.GetDefaultDespawnType(); + } + } + } + + nob.SetIsDestroying(despawnType); + //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) + UnityEngine.Object.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 the object can be disabled. + * + * Also, if clientHost and clientHost is not an observer, the object + * can be disabled. */ + //If not clientHost then the object can be disabled. + if (!NetworkManager.IsClientStarted || !nob.Observers.Contains(NetworkManager.ClientManager.Connection)) + nob.gameObject.SetActive(false); + } + //Not as server. + else + { + //If the server is not active then the object can be disabled. + if (!NetworkManager.IsServerStarted) + { + 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) + { + List childNobs = nob.GetNetworkObjects(GetNetworkObjectOption.AllNested); + foreach (NetworkObject childNob in childNobs) + { + if (childNob != null && !childNob.IsDeinitializing) + Despawn(childNob, despawnType, asServer: true); + } + } + } + } + + /// + /// 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) + { + const int invalidIndex = -1; + if (index == invalidIndex) + { + Debug.LogError($"An index of {invalidIndex} cannot be assigned as a PrefabId for {prefab.name}."); + return; + } + + if (prefab == null) + return; + + prefab.PrefabId = (ushort)index; + if (collectionId != null) + prefab.SpawnableCollectionId = collectionId.Value; + + prefab.SetInitializedValues(null, force: true); + } + + /// + /// Despawns Spawned NetworkObjects. Scene objects will be disabled, others will be destroyed. + /// + internal virtual void DespawnWithoutSynchronization(bool recursive, bool asServer) + { + foreach (NetworkObject nob in Spawned.Values) + { + if (nob == null) + continue; + + DespawnWithoutSynchronization(nob, recursive, asServer, nob.GetDefaultDespawnType(), removeFromSpawned: false); + } + + Spawned.Clear(); + } + + /// + /// Despawns a network object. + /// + /// + protected virtual void DespawnWithoutSynchronization(NetworkObject nob, bool recursive, bool asServer, DespawnType despawnType, bool removeFromSpawned) + { +#if FISHNET_STABLE_RECURSIVE_DESPAWNS + recursive = false; +#endif + + GetNetworkObjectOption getOption = (recursive) ? GetNetworkObjectOption.All : GetNetworkObjectOption.Self; + List allNobs = nob.GetNetworkObjects(getOption); + + //True if can deactivate or destroy. + bool canCleanup = (asServer || !NetworkManager.IsServerStarted); + + foreach (NetworkObject lNob in allNobs) + { + lNob.SetIsDestroying(despawnType); + lNob.Deinitialize(asServer); + + if (canCleanup && removeFromSpawned) + RemoveFromSpawned(lNob, fromOnDestroy: false, asServer); + } + + /* Only need to check the first nob. If it's stored, deactivated, + * or destroyed, the rest will follow. */ + if (canCleanup) + { + NetworkObject firstNob = allNobs[0]; + + if (firstNob.IsSceneObject || firstNob.IsInitializedNested) + { + firstNob.gameObject.SetActive(value: false); + } + else + { + if (despawnType == DespawnType.Destroy) + UnityEngine.Object.Destroy(firstNob.gameObject); + else + NetworkManager.StorePooledInstantiated(firstNob, asServer); + } + } + + CollectionCaches.Store(allNobs); + } + + /// + /// Adds a NetworkObject to Spawned. + /// + internal virtual void AddToSpawned(NetworkObject nob, bool asServer) + { + Spawned[nob.ObjectId] = nob; + } + + /// + /// Adds a NetworkObject to SceneObjects. + /// + protected internal void AddToSceneObjects(NetworkObject nob) + { + SceneObjects_Internal[nob.SceneId] = nob; + } + + /// + /// Removes a NetworkObject from SceneObjects. + /// + /// + protected internal void RemoveFromSceneObjects(NetworkObject nob) + { + SceneObjects_Internal.Remove(nob.SceneId); + } + + /// + /// Removes a NetworkObject from SceneObjects. + /// + /// + protected internal void RemoveFromSceneObjects(ulong sceneId) + { + SceneObjects_Internal.Remove(sceneId); + } + + /// + /// Finds a NetworkObject within Spawned. + /// + /// + /// + 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 + NetworkManager.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(); + } + } + + /// + /// Parses a ReplicateRpc. + /// + internal void ParseReplicateRpc(PooledReader reader, NetworkConnection conn, Channel channel) + { +#if DEVELOPMENT + NetworkBehaviour.ReadDebugForValidatedRpc(NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount); +#endif + + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.ServerRpc, reader, channel); + if (nb != null && nb.IsSpawned) + nb.OnReplicateRpc(null, reader, conn, channel); + else + SkipDataLength((ushort)PacketId.ServerRpc, reader, dataLength); + +#if DEVELOPMENT + NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel); +#endif + } + +#if DEVELOPMENT + /// + /// Checks to write a scene object's details into a writer. + /// + protected void CheckWriteSceneObjectDetails(NetworkObject nob, Writer w) + { + //Check to write additional information if a scene object. + if (NetworkManager.DebugManager.WriteSceneObjectDetails) + { + w.WriteString(nob.gameObject.scene.name); + w.WriteString(nob.gameObject.name); + } + } + + /// + /// Checks to read a scene object's details and populates values if read was successful. + /// + protected void CheckReadSceneObjectDetails(Reader r, ref string sceneName, ref string objectName) + { + if (NetworkManager.DebugManager.WriteSceneObjectDetails) + { + sceneName = r.ReadStringAllocated(); + objectName = r.ReadStringAllocated(); + } + } +#endif + } +} \ No newline at end of file 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..d0e25ff --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e1363007244792145846afddc31ac12c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/ManagedObjects.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs new file mode 100644 index 0000000..21c692f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs @@ -0,0 +1,45 @@ +using FishNet.Documenting; + +namespace FishNet.Managing.Object +{ + [System.Flags] + internal enum SpawnType : byte + { + Unset = 0, + /// + /// Is nested beneath a NetworkBehaviour. + /// + Nested = 1, + /// + /// Is a scene object. + /// + Scene = 2, + /// + /// Instantiate into active scene. + /// + Instantiated = 4, + /// + /// Instantiate into the global scene. + /// + InstantiatedGlobal = 8, + /// + /// Indicates the receiver is the predicted spawner. + /// + IsPredictedSpawner = 16, + } + + [APIExclude] + internal static class SpawnTypeExtensions + { + /// + /// Returns if whole contains part. + /// + public static bool FastContains(this 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..3d7f2a6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ed15edf5a1a100d45b05f6adace574cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/ObjectSpawnType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta new file mode 100644 index 0000000..07dbbe3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4f3c9013358962e4786b12d363c7a3fc +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..85473d0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs @@ -0,0 +1,105 @@ +using FishNet.Documenting; +using FishNet.Object.Helping; +using System.Collections.Generic; +using UnityEngine; +using GameKit.Dependencies.Utilities; +#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.GetStableHashU64(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(); + List hashcodes = new(); + + 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(); + foreach (ulong hc in hashcodes) + sortedNobs.Add(hashcodesAndNobs[hc]); + + base.Clear(); + base.AddObjects(sortedNobs, checkForDuplicates: false, initializeAdded: 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..aae6bb0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3ad70174b079c2f4ebc7931d3dd1af6f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DefaultPrefabObjects.cs + uploadId: 762203 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..83d0c77 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs @@ -0,0 +1,133 @@ +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(); + /// + /// 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) + { + NetworkManagerExtensions.LogError($"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"; + NetworkManagerExtensions.LogError($"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--; + } + } + } + + public override void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false, bool initializeAdded = true) + { + AddObjects(new DualPrefab[] { dualPrefab }, checkForDuplicates, initializeAdded); + } + + public override void AddObjects(List dualPrefabs, bool checkForDuplicates = false, bool initializeAdded = true) + { + AddObjects(dualPrefabs.ToArray(), checkForDuplicates, initializeAdded); + } + + public override void AddObjects(DualPrefab[] dualPrefabs, bool checkForDuplicates = false, bool initializeAdded = true) + { + if (!checkForDuplicates) + { + _prefabs.AddRange(dualPrefabs); + } + else + { + foreach (DualPrefab dp in dualPrefabs) + AddUniqueNetworkObjects(dp); + } + + if (initializeAdded && 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); + } + + + 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, bool initializeAdded = true) + { + NetworkManagerExtensions.LogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead."); + } + + public override void AddObjects(List networkObjects, bool checkForDuplicates = false, bool initializeAdded = true) + { + NetworkManagerExtensions.LogError($"Single prefabs are not supported with DualPrefabObjects. Make a SinglePrefabObjects asset instead."); + } + + public override void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false, bool initializeAdded = true) + { + NetworkManagerExtensions.LogError($"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..82455dd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e4b890523e001c74a9a2bf0d6340e5f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/PrefabObjects/DualPrefabObjects.cs + uploadId: 762203 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..ff751d2 --- /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, bool initializeAdded = true); + public abstract void AddObjects(List networkObjects, bool checkForDuplicates = false, bool initializeAdded = true); + public abstract void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false, bool initializeAdded = true); + public abstract void AddObject(DualPrefab dualPrefab, bool checkForDuplicates = false, bool initializeAdded = true); + public abstract void AddObjects(List dualPrefab, bool checkForDuplicates = false, bool initializeAdded = true); + public abstract void AddObjects(DualPrefab[] dualPrefab, bool checkForDuplicates = false, bool initializeAdded = true); + 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..95ff118 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c5a7beb0d6ee75a4fb1f058eb3e2640a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/PrefabObjects/PrefabObjects.cs + uploadId: 762203 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..ef0d4d9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs @@ -0,0 +1,125 @@ +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(); + /// + /// 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) + { + NetworkManagerExtensions.LogError($"PrefabId {id} is out of range."); + return null; + } + else + { + NetworkObject nob = _prefabs[id]; + if (nob == null) + NetworkManagerExtensions.LogError($"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--; + } + } + } + + public override void AddObject(NetworkObject networkObject, bool checkForDuplicates = false, bool initializeAdded = true) + { + if (!checkForDuplicates) + _prefabs.Add(networkObject); + else + AddUniqueNetworkObject(networkObject); + + if (initializeAdded && Application.isPlaying) + InitializePrefabRange(0); + } + + public override void AddObjects(List networkObjects, bool checkForDuplicates = false, bool initializeAdded = true) + { + if (!checkForDuplicates) + { + _prefabs.AddRange(networkObjects); + } + else + { + foreach (NetworkObject nob in networkObjects) + AddUniqueNetworkObject(nob); + } + + if (initializeAdded && Application.isPlaying) + InitializePrefabRange(0); + } + public override void AddObjects(NetworkObject[] networkObjects, bool checkForDuplicates = false, bool initializeAdded = true) + { + AddObjects(networkObjects.ToList(), checkForDuplicates, initializeAdded); + } + + private void AddUniqueNetworkObject(NetworkObject nob) + { + if (!_prefabs.Contains(nob)) + _prefabs.Add(nob); + } + + + 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, bool initializeAdded = true) + { + NetworkManagerExtensions.LogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead."); + } + + public override void AddObjects(List dualPrefab, bool checkForDuplicates = false, bool initializeAdded = true) + { + NetworkManagerExtensions.LogError($"Dual prefabs are not supported with SinglePrefabObjects. Make a DualPrefabObjects asset instead."); + } + + public override void AddObjects(DualPrefab[] dualPrefab, bool checkForDuplicates = false, bool initializeAdded = true) + { + NetworkManagerExtensions.LogError($"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..5347617 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4489d77032a81ef42b0067acf2737d4d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs new file mode 100644 index 0000000..3be6d1c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs @@ -0,0 +1,10 @@ +// namespace FishNet.Managing.Object //Remove in V5 +// { +// 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..1b40ddd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cbace351ced9ff94eb294dbb2e1d6a75 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Object/SpawnParentType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Observing.meta b/Assets/FishNet/Runtime/Managing/Observing.meta new file mode 100644 index 0000000..aea5280 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 66e179864f87850459121eef3b80e0d9 +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..1f1fa39 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d3549ac1479ae014986f98b9f63e3011 +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..7c399e7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs @@ -0,0 +1,48 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace FishNet.Managing.Observing.Editing +{ + + + [CustomEditor(typeof(ObserverManager), true)] + [CanEditMultipleObjects] + public class ObserverManagerEditor : Editor + { + private SerializedProperty _updateHostVisibility; + private SerializedProperty _maximumTimedObserversDuration; + private SerializedProperty _defaultConditions; + + protected virtual void OnEnable() + { + _updateHostVisibility = serializedObject.FindProperty(nameof(_updateHostVisibility)); + _maximumTimedObserversDuration = serializedObject.FindProperty(nameof(_maximumTimedObserversDuration)); + _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.LabelField("Settings", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(_updateHostVisibility); + if (_maximumTimedObserversDuration.floatValue < 1d) + EditorGUILayout.HelpBox("Using low values may reduce server performance while under load.", MessageType.Warning); + EditorGUILayout.PropertyField(_maximumTimedObserversDuration); + EditorGUILayout.PropertyField(_defaultConditions); + + EditorGUI.indentLevel--; + + 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..42301eb --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 86e21ffc228c07d4891b6e74080b8c90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Observing/Editor/ObserverManagerEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs new file mode 100644 index 0000000..f7412b1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs @@ -0,0 +1,225 @@ +using FishNet.Component.Observing; +using FishNet.Connection; +using FishNet.Object; +using FishNet.Observing; +using FishNet.Utility; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.Serialization; + +[assembly: InternalsVisibleTo(UtilityConstants.DEMOS_ASSEMBLY_NAME)] +[assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)] + +namespace FishNet.Managing.Observing +{ + /// + /// Additional options for managing the observer system. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/ObserverManager")] + public sealed class ObserverManager : MonoBehaviour + { + #region Serialized. + /// + /// 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("True to update visibility for clientHost based on if they are an observer or not.")] + [SerializeField] + private bool _updateHostVisibility = true; + + /// + /// Maximum duration the server will take to update timed observer conditions as server load increases. Lower values will result in timed conditions being checked quicker at the cost of performance.. + /// + public float MaximumTimedObserversDuration + { + get => _maximumTimedObserversDuration; + private set => _maximumTimedObserversDuration = value; + } + + [Tooltip("Maximum duration the server will take to update timed observer conditions as server load increases. Lower values will result in timed conditions being checked quicker at the cost of performance.")] + [SerializeField] + [Range(MINIMUM_TIMED_OBSERVERS_DURATION, MAXIMUM_TIMED_OBSERVERS_DURATION)] + private float _maximumTimedObserversDuration = 10f; + + /// + /// Sets the MaximumTimedObserversDuration value. + /// + /// New maximum duration to update timed observers over. + public void SetMaximumTimedObserversDuration(float value) => MaximumTimedObserversDuration = System.Math.Clamp(value, MINIMUM_TIMED_OBSERVERS_DURATION, MAXIMUM_TIMED_OBSERVERS_DURATION); + + /// + /// + /// + [Tooltip("Default observer conditions for networked objects.")] + [SerializeField] + private List _defaultConditions = new(); + + #endregion + + #region Private. + + /// + /// NetworkManager on object. + /// + private NetworkManager _networkManager; + #endregion + + #region Consts. + /// + /// Minimum time allowed for timed observers to rebuild. + /// + private const float MINIMUM_TIMED_OBSERVERS_DURATION = 0.1f; + /// + /// Maxmimum time allowed for timed observers to rebuild. + /// + private const float MAXIMUM_TIMED_OBSERVERS_DURATION = 20f; + #endregion + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + //Update the current value to itself so it becomes clamped. This is just to protect against the user manually setting it outside clamp somehow. + SetMaximumTimedObserversDuration(MaximumTimedObserversDuration); + } + + /// + /// 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.IsServerStarted && 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 + { + //If already setup by this manager then return. + if (result.ConditionsSetByObserverManager) + return result; + + 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); + } + } + + result.ConditionsSetByObserverManager = true; + + return result; + } + + + } +} \ 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..09abdb3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d331f979d46e8e4a9fc90070c596d44 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Observing/ObserverManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Prediction.meta b/Assets/FishNet/Runtime/Managing/Prediction.meta new file mode 100644 index 0000000..150f199 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd17b65ce1c2f3248980bdc8408c808e +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..c9776b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5500daad38c4cd742b6f37bc59c91849 +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..3e866b1 --- /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 _queuedInputs; + private SerializedProperty _dropExcessiveReplicates; + private SerializedProperty _maximumServerReplicates; + private SerializedProperty _maximumConsumeCount; + private SerializedProperty _createLocalStates; + private SerializedProperty _stateInterpolation; + private SerializedProperty _stateOrder; + + protected virtual void OnEnable() + { + _dropExcessiveReplicates = serializedObject.FindProperty(nameof(_dropExcessiveReplicates)); + _maximumServerReplicates = serializedObject.FindProperty(nameof(_maximumServerReplicates)); + _maximumConsumeCount = serializedObject.FindProperty(nameof(_maximumConsumeCount)); + _createLocalStates = serializedObject.FindProperty(nameof(_createLocalStates)); + _stateInterpolation = serializedObject.FindProperty(nameof(_stateInterpolation)); + _stateOrder = serializedObject.FindProperty(nameof(_stateOrder)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((PredictionManager)target), typeof(PredictionManager), false); + GUI.enabled = true; + + + EditorGUILayout.LabelField("Client", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(_createLocalStates); + + int interpolationValue = _stateInterpolation.intValue; + if (interpolationValue == 0) + EditorGUILayout.HelpBox(PredictionManager.ZERO_STATE_INTERPOLATION_MESSAGE, MessageType.Warning); + else if (_stateOrder.intValue == (int)ReplicateStateOrder.Appended && interpolationValue < PredictionManager.MINIMUM_APPENDED_INTERPOLATION_RECOMMENDATION) + EditorGUILayout.HelpBox(PredictionManager.LESS_THAN_MINIMUM_APPENDED_MESSAGE, MessageType.Warning); + else if (_stateOrder.intValue == (int)ReplicateStateOrder.Inserted && interpolationValue < PredictionManager.MINIMUM_INSERTED_INTERPOLATION_RECOMMENDATION) + EditorGUILayout.HelpBox(PredictionManager.LESS_THAN_MINIMUM_INSERTED_MESSAGE, MessageType.Warning); + EditorGUILayout.PropertyField(_stateInterpolation); + + EditorGUILayout.PropertyField(_stateOrder); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Server", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + // EditorGUILayout.PropertyField(_serverInterpolation); + EditorGUILayout.PropertyField(_dropExcessiveReplicates); + EditorGUI.indentLevel++; + if (_dropExcessiveReplicates.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_maximumServerReplicates); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + + + 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..f6fa194 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5382c1ab98f25c8439d23140d36651fe +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Prediction/Editor/PredictionManagerEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs new file mode 100644 index 0000000..cdfb905 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs @@ -0,0 +1,800 @@ +using FishNet.Connection; +using FishNet.Managing.Timing; +using FishNet.Managing.Transporting; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Performance; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; + +namespace FishNet.Managing.Predicting +{ + /// + /// Additional options for managing the observer system. + /// + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Manager/PredictionManager")] + public sealed class PredictionManager : MonoBehaviour + { + #region Types. + internal class StatePacketTick + { + public uint Client = TimeManager.UNSET_TICK; + public uint Server = TimeManager.UNSET_TICK; + + /// + /// Returns if ticks are unset. + /// Only client needs to be checked, as they both are set with non default at the same time. + /// + public bool IsUnset => Client == TimeManager.UNSET_TICK; + + public void Update(uint client, uint server) + { + Client = client; + Server = server; + } + + /// + /// Adds ticks onto each field. + /// + public void AddTick(uint quantity) + { + Client += quantity; + Server += quantity; + } + } + #endregion + + #region Public. + /// + /// Called before performing a reconcile. Contains the client and server tick the reconcile is for. + /// + public event PreReconcileDel OnPreReconcile; + + public delegate void PreReconcileDel(uint clientTick, uint serverTick); + + /// + /// Called when performing a reconcile. + /// This is used internally to reconcile objects and does not gaurantee your subscriptions to this event will process before or after internal components. + /// + public event ReconcileDel OnReconcile; + + public delegate void ReconcileDel(uint clientTick, uint serverTick); + + /// + /// Called after performing a reconcile. Contains the client and server tick the reconcile is for. + /// + public event PostReconcileDel OnPostReconcile; + + public delegate void PostReconcileDel(uint clientTick, uint serverTick); + + /// + /// Called before Physics SyncTransforms are run after a reconcile. + /// This will only invoke if physics are set to TimeManager, within the TimeManager inspector. + /// + public event PrePhysicsSyncTransformDel OnPrePhysicsTransformSync; + + public delegate void PrePhysicsSyncTransformDel(uint clientTick, uint serverTick); + + /// + /// Called after Physics SyncTransforms are run after a reconcile. + /// This will only invoke if physics are set to TimeManager, within the TimeManager inspector. + /// + public event PostPhysicsSyncTransformDel OnPostPhysicsTransformSync; + + public delegate void PostPhysicsSyncTransformDel(uint clientTick, uint serverTick); + + public event PostPhysicsSyncTransformDel OnPostReconcileSyncTransforms; + + /// + /// Called before physics is simulated when replaying a replicate method. + /// + public event PreReplicateReplayDel OnPreReplicateReplay; + + public delegate void PreReplicateReplayDel(uint clientTick, uint serverTick); + + /// + /// Called when replaying a replication. + /// This is called before physics are simulated. + /// This is used internally to replay objects and does not gaurantee your subscriptions to this event will process before or after internal components. + /// + internal event ReplicateReplayDel OnReplicateReplay; + + public delegate void ReplicateReplayDel(uint clientTick, uint serverTick); + + /// + /// Called after physics is simulated when replaying a replicate method. + /// + public event PostReplicateReplayDel OnPostReplicateReplay; + + public delegate void PostReplicateReplayDel(uint clientTick, uint serverTick); + + /// + /// True if client timing needs to be reduced. This is fine-tuning of the prediction system. + /// + internal bool ReduceClientTiming; + /// + /// True if prediction is currently reconciling. While reconciling run replicates will be replays. + /// + public bool IsReconciling { get; private set; } + /// + /// When not unset this is the current tick which local client is replaying authoraitive inputs on. + /// + public uint ClientReplayTick { get; private set; } = TimeManager.UNSET_TICK; + /// + /// When not unset this is the current tick which local client is replaying non-authoraitive inputs on. + /// + public uint ServerReplayTick { get; private set; } = TimeManager.UNSET_TICK; + /// + /// Local tick on the most recent performed reconcile. + /// + public uint ClientStateTick { get; private set; } = TimeManager.UNSET_TICK; + /// + /// Server tick on the most recent performed reconcile. + /// + public uint ServerStateTick { get; private set; } = TimeManager.UNSET_TICK; + #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; + /// + /// Sets the maximum number of replicates a server can queue per object. + /// + public void SetMaximumServerReplicates(byte value) + { + _maximumServerReplicates = (byte)Mathf.Clamp(value, MINIMUM_REPLICATE_QUEUE_SIZE, MAXIMUM_REPLICATE_QUEUE_SIZE); + } + /// + /// Maximum number of replicates a server can queue per object. Higher values will reduce the chance of dropped input when the client's connection is unstable, but will potentially add latency to the client's object both on the server and client. + /// + public byte GetMaximumServerReplicates() => _maximumServerReplicates; + [Tooltip("Maximum number of replicates a server can queue per object. Higher values will reduce the chance of dropped input when the client's connection is unstable, but will potentially add latency to the client's object both on the server and client.")] + [SerializeField] + private byte _maximumServerReplicates = 15; + + /// + /// No more than this value of replicates should be stored as a buffer. + /// + internal ushort MaximumPastReplicates => (ushort)(_networkManager.TimeManager.TickRate * 5); + + /// + /// True for the client to create local reconcile states. Enabling this feature allows reconciles to be sent less frequently and provides data to use for reconciles when packets are lost. + /// + internal bool CreateLocalStates => _createLocalStates; + [FormerlySerializedAs("_localStates")] + [Tooltip("True for the client to create local reconcile states. Enabling this feature allows reconciles to be sent less frequently and provides data to use for reconciles when packets are lost.")] + [SerializeField] + private bool _createLocalStates = true; + /// + /// How many states to try and hold in a buffer before running them. Larger values add resilience against network issues at the cost of running states later. + /// + public byte StateInterpolation => _stateInterpolation; + [Tooltip("How many states to try and hold in a buffer before running them on clients. Larger values add resilience against network issues at the cost of running states later.")] + [Range(0, MAXIMUM_PAST_INPUTS)] + [FormerlySerializedAs("_redundancyCount")] //Remove on V5. + [FormerlySerializedAs("_interpolation")] //Remove on V5. + [SerializeField] + private byte _stateInterpolation = 2; + /// + /// The order in which states are run. Future favors performance and does not depend upon reconciles, while Past favors accuracy but clients must reconcile every tick. + /// + public ReplicateStateOrder StateOrder => _stateOrder; + [Tooltip("The order in which clients run states. Future favors performance and does not depend upon reconciles, while Past favors accuracy but clients must reconcile every tick.")] + [SerializeField] + private ReplicateStateOrder _stateOrder = ReplicateStateOrder.Appended; + /// + /// True if StateOrder is set to future. + /// + internal bool IsAppendedStateOrder => (_stateOrder == ReplicateStateOrder.Appended); + + /// + /// Sets the current ReplicateStateOrder. This may be changed at runtime. + /// Changing this value only affects the client which it is changed on. + /// + /// + public void SetStateOrder(ReplicateStateOrder stateOrder) + { + //Server doesnt use state order, exit early if server. + if (_networkManager.IsServerStarted) + return; + //Same as before, do nothing. + if (stateOrder == _stateOrder) + return; + + _stateOrder = stateOrder; + /* If client is started and if new order is + * past then tell all spawned objects to + * clear future queue. */ + if (stateOrder == ReplicateStateOrder.Inserted && _networkManager.IsClientStarted) + { + foreach (NetworkObject item in _networkManager.ClientManager.Objects.Spawned.Values) + item.EmptyReplicatesQueueIntoHistory(); + } + } + + /// + /// Number of past inputs to send, which is also the number of times to resend final datas. + /// + internal byte RedundancyCount => (byte)(_stateInterpolation + 1); + ///// + ///// + ///// + //[Tooltip("How many states to try and hold in a buffer before running them on server. Larger values add resilience against network issues at the cost of running states later.")] + //[Range(0, MAXIMUM_PAST_INPUTS + 30)] + //[SerializeField] + //private byte _serverInterpolation = 1; + ///// + ///// How many states to try and hold in a buffer before running them on server. Larger values add resilience against network issues at the cost of running states later. + ///// + //internal byte ServerInterpolation => _serverInterpolation; + #endregion + + #region Private. + /// + /// Number of reconciles dropped due to high latency. + /// This is not necessarily needed but can save performance on machines struggling to keep up with simulations when combined with low frame rate. + /// + private byte _droppedReconcilesCount; + /// + /// Ticks for the last state packet to run. + /// + private StatePacketTick _lastStatePacketTick = new(); + /// + /// Current reconcile state to use. + /// + //private StatePacket _reconcileState; + private Queue _reconcileStates = new(); + /// + /// Look up to find states by their tick. + /// Key: client LocalTick on the state. + /// Value: StatePacket stored. + /// + private Dictionary _stateLookups = new(); + /// + /// Last ordered tick read for a reconcile state. + /// + private uint _lastOrderedReadReconcileTick; + /// + /// NetworkManager used with this. + /// + private NetworkManager _networkManager; + #endregion + + #region Const. + /// + /// Minimum number of past inputs which can be sent. + /// + private const byte MINIMUM_PAST_INPUTS = 1; + /// + /// Maximum number of past inputs which can be sent. + /// + internal const byte MAXIMUM_PAST_INPUTS = 5; + /// + /// Minimum amount of replicate queue size. + /// + private const byte MINIMUM_REPLICATE_QUEUE_SIZE = (MINIMUM_PAST_INPUTS + 1); + /// + /// Maxmimum amount of replicate queue size. + /// + private const byte MAXIMUM_REPLICATE_QUEUE_SIZE = byte.MaxValue; + /// + /// Recommended state interpolation value when using appended state order. + /// + internal const int MINIMUM_APPENDED_INTERPOLATION_RECOMMENDATION = 2; + /// + /// Recommended state interpolation value when using inserted state order. + /// + internal const int MINIMUM_INSERTED_INTERPOLATION_RECOMMENDATION = 1; + /// + /// Message when state interpolation is 0. + /// + internal static readonly string ZERO_STATE_INTERPOLATION_MESSAGE = $"When interpolation is 0 the chances of de-synchronizations on non-owned objects is increased drastically."; + /// + /// Message when state interpolation is less than ideal for appended state order. + /// + internal static readonly string LESS_THAN_MINIMUM_APPENDED_MESSAGE = $"When using Appended StateOrder and an interpolation less than {MINIMUM_APPENDED_INTERPOLATION_RECOMMENDATION} the chances of de-synchronizations on non-owned objects is increased."; + /// + /// Message when state interpolation is less than ideal for inserted state order. + /// + internal static readonly string LESS_THAN_MINIMUM_INSERTED_MESSAGE = $"When using Inserted StateOrder and an interpolation less than {MINIMUM_INSERTED_INTERPOLATION_RECOMMENDATION} the chances of de-synchronizations on non-owned objects is increased."; + #endregion + + internal void InitializeOnce(NetworkManager manager) + { + _networkManager = manager; + ValidateClampInterpolation(); + _networkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + } + + /// + /// Called after the local client connection state changes. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + _droppedReconcilesCount = 0; + _lastOrderedReadReconcileTick = 0; + } + + /// + /// Amount to reserve for the header of a state update. + /// + internal const int STATE_HEADER_RESERVE_LENGTH = (TransportManager.PACKETID_LENGTH + TransportManager.UNPACKED_TICK_LENGTH + TransportManager.UNPACKED_SIZE_LENGTH); + + /// + /// Clamps queued inputs to a valid value. + /// + private void ValidateClampInterpolation() + { + ushort startingValue = _stateInterpolation; + //Check for setting if dropping. + if (_dropExcessiveReplicates && _stateInterpolation > _maximumServerReplicates) + _stateInterpolation = (byte)(_maximumServerReplicates - 1); + + //If changed. + if (_stateInterpolation != startingValue) + _networkManager.Log($"Interpolation has been set to {_stateInterpolation}."); + + //Check to warn if low value. + if (_stateInterpolation == 0) + _networkManager.LogWarning(ZERO_STATE_INTERPOLATION_MESSAGE); + else if (_stateOrder == ReplicateStateOrder.Appended && _stateInterpolation < MINIMUM_APPENDED_INTERPOLATION_RECOMMENDATION) + _networkManager.LogWarning(LESS_THAN_MINIMUM_APPENDED_MESSAGE); + else if (_stateOrder == ReplicateStateOrder.Inserted && _stateInterpolation < MINIMUM_INSERTED_INTERPOLATION_RECOMMENDATION) + _networkManager.LogWarning(LESS_THAN_MINIMUM_INSERTED_MESSAGE); + } + + internal class StatePacket : IResettable + { + public struct IncomingData + { + public ArraySegment Data; + public Channel Channel; + + public IncomingData(ArraySegment data, Channel channel) + { + Data = data; + Channel = channel; + } + } + + public List Datas; + public uint ClientTick; + public uint ServerTick; + + public void Update(ArraySegment data, uint clientTick, uint serverTick, Channel channel) + { + AddData(data, channel); + ServerTick = serverTick; + ClientTick = clientTick; + } + + public void AddData(ArraySegment data, Channel channel) + { + if (data.Array != null) + Datas.Add(new(data, channel)); + } + + public void ResetState() + { + for (int i = 0; i < Datas.Count; i++) + ByteArrayPool.Store(Datas[i].Data.Array); + + CollectionCaches.StoreAndDefault(ref Datas); + } + + public void InitializeState() + { + Datas = CollectionCaches.RetrieveList(); + } + } + + /// + /// Returns client or server state tick for the current reconcile. + /// + /// True to return client state tick, false for servers. + /// + public uint GetReconcileStateTick(bool clientTick) => (clientTick) ? ClientStateTick : ServerStateTick; + + /// + /// Reconciles to received states. + /// + internal void ReconcileToStates() + { + if (!_networkManager.IsClientStarted) + return; + + //Creates a local state update if one is not available in reconcile states. + // CreateLocalStateUpdate(); + + //If there are no states then guestimate the next state. + if (_reconcileStates.Count == 0) + return; + + TimeManager tm = _networkManager.TimeManager; + uint localTick = tm.LocalTick; + uint estimatedLastRemoteTick = tm.LastPacketTick.Value(); + + /* When there is an excessive amount of states try to consume + * some.This only happens when the client gets really far behind + * and has to catch up, such as a latency increase then drop. + * Limit the number of states consumed per tick so the clients + * computer doesn't catch fire. */ + int iterations = 0; + + while (_reconcileStates.Count > 0) + { + iterations++; + /* Typically there should only be 'interpolation' amount in queue but + * there can be more if the clients network is unstable and they are + * arriving in burst. + * If there's more than interpolation (+1 for as a leniency buffer) then begin to + * consume multiple. */ + byte stateInterpolation = StateInterpolation; + int maxIterations = (_reconcileStates.Count > (stateInterpolation + 1)) ? 2 : 1; + //At most 2 iterations. + if (iterations > maxIterations) + return; + + StatePacket sp; + if (!ConditionsMet(_reconcileStates.Peek())) + return; + else + sp = _reconcileStates.Dequeue(); + + //Condition met. See if the next one matches condition, if so drop current. + //Returns if a state has it's conditions met. + bool ConditionsMet(StatePacket spChecked) + { + if (spChecked == null) + return false; + + /* varianceAllowance gives a few ticks to provide opportunity for late + * packets to arrive. This adds on varianceAllowance to replays but greatly + * increases the chances of the state being received before skipping past it + * in a replay. + * + * When using Inserted (not AppendedStateOrder) there does not need to be any + * additional allowance since there is no extra queue like appended, they rather just + * go right into the past. */ + uint varianceAllowance = (IsAppendedStateOrder) ? (uint)2 : (uint)0; + uint serverTickDifferenceRequirement = (varianceAllowance + stateInterpolation); + + bool serverPass = (spChecked.ServerTick < (estimatedLastRemoteTick - serverTickDifferenceRequirement)); + bool clientPass = spChecked.ClientTick < (localTick - stateInterpolation); + + return (serverPass && clientPass); + } + + bool dropReconcile = false; + uint clientTick = sp.ClientTick; + uint serverTick = sp.ServerTick; + + /* If client has a low frame rate + * then limit the number of reconciles to prevent further performance loss. + * Wait 2 seconds for client to achieve a 'not low framerate'.*/ + if (_networkManager.TimeManager.LowFrameRate && _networkManager.TimeManager.ClientUptime > 2f) + { + /* Limit 3 drops a second. DropValue will be roughly the same + * as every 330ms. */ + int reconcileValue = Mathf.Max(1, (_networkManager.TimeManager.TickRate / 3)); + //If cannot drop then reset dropcount. + if (_droppedReconcilesCount >= reconcileValue) + { + _droppedReconcilesCount = 0; + } + //If can drop... + else + { + dropReconcile = true; + _droppedReconcilesCount++; + } + } + //} + //No reason to believe client is struggling, allow reconcile. + else + { + _droppedReconcilesCount = 0; + } + + if (!dropReconcile) + { + IsReconciling = true; + _lastStatePacketTick.Update(clientTick, serverTick); + + ClientStateTick = clientTick; + /* This is the tick which the reconcile is for. + * Since reconciles are performed after replicate, if + * the replicate was on tick 100 then this reconcile is the state + * on tick 100, after the replicate is performed. */ + ServerStateTick = serverTick; + + //Have the reader get processed. + foreach (StatePacket.IncomingData item in sp.Datas) + { + // //If data isn't set skip it. This can be true if a locally generated state packet. + // if (item.Data.Array == null) + // continue; + + PooledReader reader = ReaderPool.Retrieve(item.Data, _networkManager, Reader.DataSource.Server); + _networkManager.ClientManager.ParseReader(reader, item.Channel); + ReaderPool.Store(reader); + } + + bool timeManagerPhysics = (tm.PhysicsMode == PhysicsMode.TimeManager); + float tickDelta = ((float)tm.TickDelta * _networkManager.TimeManager.GetPhysicsTimeScale()); + + OnPreReconcile?.Invoke(ClientStateTick, ServerStateTick); + OnReconcile?.Invoke(ClientStateTick, ServerStateTick); + + if (timeManagerPhysics) + { + OnPrePhysicsTransformSync?.Invoke(ClientStateTick, ServerStateTick); + Physics.SyncTransforms(); + Physics2D.SyncTransforms(); + OnPostPhysicsTransformSync?.Invoke(ClientStateTick, ServerStateTick); + } + + OnPostReconcileSyncTransforms?.Invoke(ClientStateTick, ServerStateTick); + /* Set first replicate to be the 1 tick + * after reconcile. This is because reconcile calcs + * should be performed after replicate has run. + * In result object will reconcile to data AFTER + * the replicate tick, and then run remaining replicates as replay. + * + * Replay up to localtick, excluding localtick. There will + * be no input for localtick since reconcile runs before + * OnTick. */ + ClientReplayTick = ClientStateTick + 1; + ServerReplayTick = ServerStateTick + 1; + + /* Only replay up to but excluding local tick. + * This prevents client from running 1 local tick into the future + * since the OnTick has not run yet. + * + * EG: if localTick is 100 replay will run up to 99, then OnTick + * will fire for 100. */ + while (ClientReplayTick < localTick) + { + OnPreReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick); + OnReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick); + if (timeManagerPhysics && tickDelta > 0f) + { + Physics.Simulate(tickDelta); + Physics2D.Simulate(tickDelta); + } + OnPostReplicateReplay?.Invoke(ClientReplayTick, ServerReplayTick); + ClientReplayTick++; + ServerReplayTick++; + } + + OnPostReconcile?.Invoke(ClientStateTick, ServerStateTick); + + // ClientStateTick = TimeManager.UNSET_TICK; + // ServerStateTick = TimeManager.UNSET_TICK; + ClientReplayTick = TimeManager.UNSET_TICK; + ServerReplayTick = TimeManager.UNSET_TICK; + IsReconciling = false; + } + + DisposeOfStatePacket(sp); + } + } + + /// + /// Gets the reconcile tick to use when generating a local reconcile. + /// + /// + internal uint GetCreateReconcileTick(bool isOwner) + { + uint localTick = _networkManager.TimeManager.LocalTick; + + //Client uses current localTick if owner. + if (isOwner) + return localTick; + + //ClientStateTick has never been set, might happen when just connecting. Cannot get tick. + if (ClientStateTick == TimeManager.UNSET_TICK) + return TimeManager.UNSET_TICK; + + long tickDifference = (long)(localTick - ClientStateTick); + + //Should not be possible given state tick is always behind. + if (tickDifference < 0) + tickDifference = 0; + + return (ServerStateTick + (uint)tickDifference); + } + + /// + /// Sends written states for clients. + /// + internal void SendStateUpdate() + { + byte stateInterpolation = StateInterpolation; + TransportManager tm = _networkManager.TransportManager; + //Must have replicated within two timing intervals. + uint recentReplicateToTicks = (_networkManager.TimeManager.TimingTickInterval * 2); + + foreach (NetworkConnection nc in _networkManager.ServerManager.Clients.Values) + { + uint lastReplicateTick; + //If client has performed a replicate recently. + if (!nc.ReplicateTick.IsUnset) + { + lastReplicateTick = nc.ReplicateTick.Value(); + } + /* If not then use what is estimated to be the clients + * current tick along with desired interpolation. + * This should be just about the same as if the client used replicate recently. */ + else + { + uint ncLocalTick = nc.LocalTick.Value(); + uint interpolationDifference = ((uint)stateInterpolation * 2); + if (ncLocalTick < interpolationDifference) + ncLocalTick = 0; + + lastReplicateTick = ncLocalTick; + } + + foreach (PooledWriter writer in nc.PredictionStateWriters) + { + /* Packet is sent as follows... + * PacketId. + * LastReplicateTick of receiver. + * Length of packet. + * Data. */ + ArraySegment segment = writer.GetArraySegment(); + writer.Position = 0; + writer.WritePacketIdUnpacked(PacketId.StateUpdate); + writer.WriteTickUnpacked(lastReplicateTick); + + /* Send the full length of the writer excluding + * the reserve count of the header. The header reserve + * count will always be the same so that can be parsed + * off immediately upon receiving. */ + int dataLength = (segment.Count - STATE_HEADER_RESERVE_LENGTH); + //Write length. + writer.WriteInt32Unpacked(dataLength); + //Channel is defaulted to unreliable. + Channel channel = Channel.Unreliable; + //If a single state exceeds MTU it must be sent on reliable. This is extremely unlikely. + _networkManager.TransportManager.CheckSetReliableChannel(segment.Count, ref channel); + tm.SendToClient((byte)channel, segment, nc, true); + } + + nc.StorePredictionStateWriters(); + } + } + + /// + /// Parses a received state update. + /// + internal void ParseStateUpdate(PooledReader reader, Channel channel) + { + uint lastRemoteTick = _networkManager.TimeManager.LastPacketTick.LastRemoteTick; + //If server or state is older than another received state. + if (_networkManager.IsServerStarted || (lastRemoteTick < _lastOrderedReadReconcileTick)) + { + /* If the server is receiving a state update it can + * simply discard the data since the server will never + * need to reset states. This can occur on the clientHost + * side. */ + reader.ReadTickUnpacked(); + int length = reader.ReadInt32Unpacked(); + reader.Skip(length); + } + else + { + _lastOrderedReadReconcileTick = lastRemoteTick; + + RemoveExcessiveStates(); + + //LocalTick of this client the state is for. + uint clientTick = reader.ReadTickUnpacked(); + //Length of packet. + int length = reader.ReadInt32Unpacked(); + //Read data into array. + byte[] arr = ByteArrayPool.Retrieve(length); + reader.ReadUInt8Array(ref arr, length); + //Make segment and store into states. + ArraySegment segment = new(arr, 0, length); + + /* See if an entry was already added for the clientTick. If so then + * add onto the datas. Otherwise add a new state packet. */ + if (_stateLookups.TryGetValue(clientTick, out StatePacket sp1)) + { + sp1.AddData(segment, channel); + } + else + { + StatePacket sp2 = ResettableObjectCaches.Retrieve(); + sp2.Update(segment, clientTick, lastRemoteTick, channel); + _stateLookups[clientTick] = sp2; + _reconcileStates.Enqueue(sp2); + } + } + } + // + // /// + // /// Creates a local statePacket with no data other than ticks. + // /// + // internal void CreateLocalStateUpdate() + // { + // //Only to be called when there are no reconcile states available. + // if (_reconcileStates.Count > 0) + // return; + // if (_networkManager.IsServerStarted) + // return; + // //Not yet received first state, cannot apply tick. + // if (_lastStatePacketTick.IsUnset) + // return; + // + // _lastStatePacketTick.AddTick(1); + // + // /* Update last read as well. If we've made it this far we won't be caring about states before this + // * even if they come in late. */ + // _lastOrderedReadReconcileTick = _lastStatePacketTick.Server; + // + // StatePacket sp = ResettableObjectCaches.Retrieve(); + // //Channel does not matter; it's only used to determine how data is parsed, data we don't have. + // sp.Update(default, _lastStatePacketTick.Client, _lastStatePacketTick.Server, Channel.Unreliable); + // _reconcileStates.Enqueue(sp); + // } + + /// + /// Removes excessively stored state packets. + /// + private void RemoveExcessiveStates() + { + /* There should never really be more than queuedInputs so set + * a limit a little beyond to prevent reconciles from building up. + * This is more of a last result if something went terribly + * wrong with the network. */ + int adjustedStateInterpolation = (StateInterpolation * 4) + 2; + /* If appending allow an additional of stateInterpolation since + * entries arent added into the past until they are run on the appended + * queue for each networkObject. */ + if (IsAppendedStateOrder) + adjustedStateInterpolation += StateInterpolation; + int maxAllowedStates = Mathf.Max(adjustedStateInterpolation, 4); + + while (_reconcileStates.Count > maxAllowedStates) + { + StatePacket oldSp = _reconcileStates.Dequeue(); + DisposeOfStatePacket(oldSp); + } + } + + /// + /// Disposes of and cleans up everything related to a StatePacket. + /// + private void DisposeOfStatePacket(StatePacket sp) + { + uint clientTick = sp.ClientTick; + _stateLookups.Remove(clientTick); + ResettableObjectCaches.Store(sp); + } + +#if UNITY_EDITOR + private void OnValidate() + { + ValidateClampInterpolation(); + } + +#endif + } +} \ 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..86e00e3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e08bb003fce297d4086cf8cba5aa459a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Prediction/PredictionManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs b/Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs new file mode 100644 index 0000000..cad37cd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs @@ -0,0 +1,19 @@ + +namespace FishNet.Managing.Predicting +{ + public enum ReplicateStateOrder + { + /// + /// On clients states are placed in the past and then run when reconciles occur. + /// This ensures that the local client is running states at the exact same time the server had, resulting in the best possible outcome for prediction accuracy. + /// To function properly however, clients must reconcile regularly to run past inputs which may cause performance loss on lower-end client devices. + /// + Inserted, + /// + /// On clients states are still placed in the past but rather than wait until a reconcile to run, they are also placed into a queue and run as they are received. + /// This causes states to initially run out of tick alignment, while correcting during reconciles. + /// However, due to states no longer depending on reconciles to be run reconciles may be sent less, and clients may run reconciles less, resulting in high performance gain especially among physics-based games. + /// + Appended, + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs.meta b/Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs.meta new file mode 100644 index 0000000..443370e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 677aa6d3e4dab3743925f2c28646da09 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Prediction/StateOrder.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs b/Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs new file mode 100644 index 0000000..d8191d0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs @@ -0,0 +1,21 @@ + +namespace FishNet.Managing +{ + + public enum RemoteTimeoutType + { + /// + /// Disable this feature. + /// + Disabled = 0, + /// + /// Only enable in release builds. + /// + Release = 1, + /// + /// Enable in all builds and editor. + /// + Development = 2, + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs.meta b/Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs.meta new file mode 100644 index 0000000..5bf91c1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b3df7f42da2f4f444952e23b5fa8aaa0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/RemoteTimeoutType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened.meta b/Assets/FishNet/Runtime/Managing/Scened.meta new file mode 100644 index 0000000..771f877 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 36c52dde2f6aec644aa226d1a864b2b6 +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..426bf8d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Broadcast.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f210b76f529cc1040968dfa087adb628 +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..28fedd2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 698a94b4f8664ac4ab108deae0ba3b7c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/Broadcast/SceneBroadcasts.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs new file mode 100644 index 0000000..9050618 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs @@ -0,0 +1,177 @@ +using System.Collections.Generic; +using UnityEngine; +using UnitySceneManager = UnityEngine.SceneManagement.SceneManager; +using UnityScene = UnityEngine.SceneManagement.Scene; +using System.Collections; +using System; + +namespace FishNet.Managing.Scened +{ + + public class DefaultSceneProcessor : SceneProcessorBase + { + #region Private. + /// + /// Currently active loading AsyncOperations. + /// + protected List LoadingAsyncOperations = new(); + /// + /// A collection of scenes used both for loading and unloading. + /// + protected List Scenes = new(); + /// + /// Current AsyncOperation being processed. + /// + protected AsyncOperation CurrentAsyncOperation; + /// + /// Last scene to load or begin loading. + /// + private UnityScene _lastLoadedScene; + #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); + + _lastLoadedScene = UnitySceneManager.GetSceneAt(UnitySceneManager.sceneCount - 1); + + 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; + } + + /// + /// Gets the scene last loaded by the processor. + /// + /// This is called after IsPercentComplete returns true. + public override UnityScene GetLastLoadedScene() => _lastLoadedScene; + + + /// + /// 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() + { + for (int i = 0; i < LoadingAsyncOperations.Count; i++) + { + try + { + LoadingAsyncOperations[i].allowSceneActivation = true; + } + catch(Exception e) + { + base.SceneManager.NetworkManager.LogError($"An error occured while activating scenes. {e.Message}"); + } + } + } + + /// + /// 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); + + } + } + + +} \ 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..6a41223 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0c6eacaa60569d947b383df03fff1ea3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/DefaultSceneProcessor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/Events.meta b/Assets/FishNet/Runtime/Managing/Scened/Events.meta new file mode 100644 index 0000000..9c42edd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dab62b7547b4c7244ac46c329804de12 +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..8735391 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fa91039d4ab1c6445af72881af122b0a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/Events/ClientPresenceChangeEventArgs.cs + uploadId: 762203 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..1f30ba1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 86278568f8087de49b0908f148501993 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/Events/LoadSceneEventArgs.cs + uploadId: 762203 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..6580c0d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs @@ -0,0 +1,56 @@ +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; + /// + /// Scenes which were successfully unloaded. + /// This collection may be populated with empty scenes depending on engine version. + /// + [Obsolete("Use UnloadedScenesV2.")] //Remove on V5. Rename UnloadedScenesV2 to UnloadedScenes. + public List UnloadedScenes; + /// + /// Scenes which were successfully unloaded. + /// This contains information of the scene unloaded but may not contain scene references as some Unity versions discard that information after a scene is unloaded. + /// + public List UnloadedScenesV2; + + internal SceneUnloadEndEventArgs(UnloadQueueData sqd, List unloadedScenes, List newUnloadedScenes) + { + QueueData = sqd; +#pragma warning disable CS0618 // Type or member is obsolete + UnloadedScenes = unloadedScenes; +#pragma warning restore CS0618 // Type or member is obsolete + UnloadedScenesV2 = newUnloadedScenes; + } + } + + +} \ 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..ab6a87e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2c24765fea85b564aa331b529f324f92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/Events/UnloadSceneEventArgs.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta new file mode 100644 index 0000000..41cdfee --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 64830aa6b410d984a94143eb0c9a057e +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..f7b333d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1614453d3786b2a4eb18b69297da7dc9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadOptions.cs + uploadId: 762203 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..473439e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d8b395f67f61b4e45830a70289a1901d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadParams.cs + uploadId: 762203 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..378e4b6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs @@ -0,0 +1,51 @@ +using FishNet.Connection; +using FishNet.Utility; +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..6a7e431 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8fb4183af628f754b800dfdbb1ba9bf0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/LoadQueueData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs new file mode 100644 index 0000000..20a3661 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs @@ -0,0 +1,36 @@ + +namespace FishNet.Managing.Scened +{ + public struct PreferredScene + { + /// + /// Preferred scene for the client. + /// + public SceneLookupData Client; + /// + /// Preferred scene for the server. + /// + public SceneLookupData Server; + + /// + /// Sets an individual preferred scene for client and server. + /// + public PreferredScene(SceneLookupData client, SceneLookupData server) + { + Client = client; + Server = server; + } + + /// + /// Sets the same preferred scene for client and server. + /// + /// + public PreferredScene(SceneLookupData sld) + { + Client = sld; + Server = sld; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs.meta b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs.meta new file mode 100644 index 0000000..215351c --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 680c29c425aaea745a28a5590e2c00f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/PreferredActiveScenes.cs + uploadId: 762203 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..b288b78 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cb8e2c0fe3b9d3344a05810936861555 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/ReplaceOption.cs + uploadId: 762203 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..47731fa --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs @@ -0,0 +1,202 @@ +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 these scenes will be set as the active scene after loading occurs. + /// + public PreferredScene PreferredActiveScene; + /// + /// 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(); + /// + /// Additional options to use for loaded scenes. + /// + public LoadOptions Options = new(); + + 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) + { + sceneLookupDatas = SceneLookupData.ValidateData(sceneLookupDatas); + 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 _, false); + 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..6e43ee6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ecd4065158ab62047a074c594f245d90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneLoadData.cs + uploadId: 762203 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..36512e6 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2be621eb04519a14eb2297a666b1bc2c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneScopeTypes.cs + uploadId: 762203 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..ec417b9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using UnityEngine.SceneManagement; + +namespace FishNet.Managing.Scened +{ + + /// + /// Data about which scenes to unload. + /// + public class SceneUnloadData + { + /// + /// When specified these scenes will be set as the active scene after loading occurs. + /// + public PreferredScene PreferredActiveScene; + /// + /// 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(); + /// + /// Additional options to use for loaded scenes. + /// + public UnloadOptions Options = new(); + + /// + /// + /// + 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 }) { } + /// + /// + /// + /// Scene to unload by SceneLookupData. + public SceneUnloadData(SceneLookupData sceneLookupData) + { + SceneLookupDatas = new SceneLookupData[] { sceneLookupData }; + } + /// + /// + /// + /// 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..3172464 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 77cbfeea232e4ab44a4315b003ce6742 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/SceneUnloadData.cs + uploadId: 762203 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..1f41fb0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs @@ -0,0 +1,35 @@ + +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. + /// + 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..991a80b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3de31d76de313bc49aefba61135fdffc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadOptions.cs + uploadId: 762203 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..f0d4e6f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3bba3fbbe6ffbae4bb18d50c8c9b3b30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadParams.cs + uploadId: 762203 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..d405928 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs @@ -0,0 +1,53 @@ +using FishNet.Connection; +using FishNet.Utility; +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..0d62bd0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ea9c20d60381ea74f974fb87a7e78297 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/LoadUnloadDatas/UnloadQueueData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs new file mode 100644 index 0000000..3742f0b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs @@ -0,0 +1,410 @@ +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +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; + } + /// + /// Returns Names from SceneLookupData. + /// + /// + /// + public static string[] GetNamesOnly(this SceneLookupData[] datas) + { + string[] names = new string[datas.Length]; + for (int i = 0; i < datas.Length; i++) + names[i] = datas[i].NameOnly; + + return names; + } + } + + /// + /// Data container for looking up, loading, or unloading a scene. + /// + public class SceneLookupData : IEquatable + { + /// + /// 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 + { + get + { + if (string.IsNullOrEmpty(Name)) + return string.Empty; + string name = System.IO.Path.GetFileName(Name); + return RemoveUnityExtension(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 $"Name {Name}, Handle {Handle}"; + //return base.ToString(); + } + #endregion + + #region CreateData. + /// + /// Returns a new SceneLookupData. + /// + /// Scene to create from. + /// + public static SceneLookupData CreateData(Scene scene) => new(scene); + /// + /// Returns a new SceneLookupData. + /// + /// Scene name to create from. + /// + public static SceneLookupData CreateData(string name) => new(name); + /// + /// Returns a new SceneLookupData. + /// + /// Scene handle to create from. + /// + public static SceneLookupData CreateData(int handle) => new(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(); + foreach (Scene item in scenes) + { + if (!item.IsValid()) + { + invalidFound = true; + continue; + } + + result.Add(CreateData(item)); + } + + if (invalidFound) + NetworkManagerExtensions.LogWarning(INVALID_SCENE); + + return result.ToArray(); + } + /// + /// Returns a SceneLookupData collection. + /// + /// Scene names to create from. + /// + public static SceneLookupData[] CreateData(string[] names) + { + SceneLookupData[] result = new SceneLookupData[names.Length]; + for (int i = 0; i < result.Length; i++) + result[i] = new(names[i]); + + return ValidateData(result); + } + + /// + /// Validates SceneLookupdatas and returns only valid entries. + /// + public static SceneLookupData[] ValidateData(SceneLookupData data) => ValidateData(new SceneLookupData[] { data }); + /// + /// Validates SceneLookupdatas and returns only valid entries. + /// + /// Datas to validate. + public static SceneLookupData[] ValidateData(SceneLookupData[] datas) + { + bool invalidFound = false; + List result = CollectionCaches.RetrieveList(); + foreach (SceneLookupData item in datas) + { + if (item.IsValid) + { + int failingIndex = -1; + //Scene name or handle is set, make sure it's not duplicated in datas. + for (int i = 0; i < result.Count; i++) + { + bool nameMatches = (result[i].Name == item.Name); + bool handleMatches = (result[i].Handle == item.Handle); + //Handle is the same (could be 0 handle). + if (handleMatches) + { + //If handle matches and not default then the same scene was added multiple times. + if (item.Handle != 0) + failingIndex = i; + } + //Name is the same. + else if (nameMatches) + { + //If handle and name matches then also fail. + if (handleMatches) + failingIndex = i; + } + } + + if (failingIndex != -1) + NetworkManagerExtensions.LogWarning($"Data {item.ToString()} matches {result[failingIndex].ToString()} and has been removed from datas."); + else + result.Add(item); + } + else + { + invalidFound = true; + } + } + + SceneLookupData[] returnedValue; + if (invalidFound) + { + NetworkManagerExtensions.LogWarning(INVALID_SCENE); + returnedValue = result.ToArray(); + } + else + { + returnedValue = datas; + } + + CollectionCaches.Store(result); + return returnedValue; + } + + /// + /// Returns a SceneLookupData collection. + /// + /// Scene handles to create from. + /// + public static SceneLookupData[] CreateData(int[] handles) + { + bool invalidFound = false; + List result = new(); + foreach (int item in handles) + { + if (item == 0) + { + invalidFound = true; + continue; + } + + result.Add(CreateData(item)); + } + + if (invalidFound) + NetworkManagerExtensions.LogWarning(INVALID_SCENE); + + return result.ToArray(); + } + #endregion + + /// + /// Removes .Unity from text. + /// + private static string RemoveUnityExtension(string text) + { + string extension = ".unity"; + int extIndex = text.ToLower().IndexOf(extension); + if (extIndex != -1 && (text.Length - extIndex) == extension.Length) + text = text.Substring(0, extIndex); + + return text; + } + + /// + /// Returns the first scene found using Handle or Name, preferring Handle. + /// + /// + /// True if scene was found by handle. Handle is always checked first. + /// True to warn if duplicates are found. + public Scene GetScene(out bool foundByHandle, bool warnIfDuplicates = true) + { + foundByHandle = false; + + if (Handle == 0 && string.IsNullOrEmpty(NameOnly)) + { + NetworkManagerExtensions.LogWarning("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, null, warnIfDuplicates); + + return result; + } + + } +} \ No newline at end of file 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..6be25dc --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: df1ac9b164e75da46bc52f4dd4fe30ba +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/SceneLookupData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs new file mode 100644 index 0000000..ec5fcba --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs @@ -0,0 +1,4843 @@ +// #if !STABLE_SCENEMANAGER_DEFINE +// 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 GameKit.Dependencies.Utilities; +// using GameKit.Dependencies.Utilities.Types; +// 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(); +// /// +// /// +// /// +// [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; +// +// /// +// /// NetworkManager for this script. +// /// +// public NetworkManager NetworkManager { get; private set; } +// #endregion +// +// #region Internal. +// /// +// /// Called after the active scene has been set, immediately after scene loads. +// /// +// internal event Action OnActiveSceneSetInternal; +// /// +// /// True if the SceneManager has items in queue. +// /// +// internal bool IteratingQueue { get; private set; } +// /// +// /// Unscaled time when the SceneManager completed it's last queue. +// /// +// internal float QueueCompleteTime { get; private set; } +// #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 spawned objects visible to the client that are within an unloading scene. This ensures the objects are despawned on the client side rather than when the scene is destroyed. +// /// +// [FormerlySerializedAs("_moveClientHostObjects")] +// [Tooltip("True to move spawned objects visible to the client 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 _moveClientObjects = true; +// /// +// /// Sets a new value for MoveClientObjects. +// /// +// public void SetMoveClientObjects(bool value) => _moveClientObjects = value; +// /// +// /// 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. +// /// +// /// 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(); +// /// +// /// Scenes to load or unload, in order. +// /// +// private List _queuedOperations = new(); +// /// +// /// Scenes which must be manually unloaded, even when emptied. +// /// +// private HashSet _manualUnloadScenes = new(); +// /// +// /// 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(); +// /// +// /// 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(); +// ///// +// ///// Cache of SceneLookupData. +// ///// +// //private SceneLookupData _sceneLookupDataCache = new SceneLookupData(); +// /// +// /// GlobalScenes currently loading on the server. +// /// +// private HashSet _serverGlobalScenesLoading = new(); +// #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(); +// _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.IsServerStarted) +// 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) +// { +// //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(); +// connection.Broadcast(msg); +// } +// else +// { +// string[] globalsNotLoading = GlobalScenesExcludingLoading(); +// //If there are globals that can be sent now. +// if (globalsNotLoading != null) +// { +// SceneLoadData sld = new(globalsNotLoading); +// sld.Params = _globalSceneLoadData.Params; +// sld.Options = _globalSceneLoadData.Options; +// sld.ReplaceScenes = _globalSceneLoadData.ReplaceScenes; +// sld.PreferredActiveScene = _globalSceneLoadData.PreferredActiveScene; +// +// List loadingScenes = CollectionCaches.RetrieveList(); +// foreach (SceneLookupData lSld in sld.SceneLookupDatas) +// loadingScenes.Add(lSld.GetScene(out _)); +// +// AddPendingLoad(connection, loadingScenes); +// CollectionCaches.StoreAndDefault(ref loadingScenes); +// +// LoadQueueData qd = new(SceneScopeType.Global, Array.Empty(), sld, _globalScenes, false); +// //Send message to load the networked scenes. +// LoadScenesBroadcast msg = new() +// { +// QueueData = qd +// }; +// +// connection.Broadcast(msg, true); +// } +// } +// } +// +// /// +// /// Received on client when the server has no start scenes. +// /// +// private void OnClientEmptyStartScenes(EmptyStartScenesBroadcast msg, Channel channel) +// { +// TryInvokeLoadedStartScenes(_clientManager.Connection, asServer: false); +// _clientManager.Broadcast(msg); +// } +// +// /// +// /// Received on server when client confirms there are no start scenes. +// /// +// private void OnServerEmptyStartScenes(NetworkConnection conn, EmptyStartScenesBroadcast msg, Channel channel) +// { +// //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 +// TryInvokeLoadedStartScenes(conn, asServer: true); +// } +// #endregion +// +// #region Player disconnect. +// /// +// /// Received when a player disconnects from the server. +// /// +// /// //finish. +// private void ClientDisconnected(NetworkConnection conn) +// { +// List connScenes = CollectionCaches.RetrieveList(); +// +// //Remove the disconnecting clients pending loads. +// if (_pendingClientSceneChanges.TryGetValueIL2CPP(conn, out HashSet pendingLoads)) +// { +// //Add pending to connsScenes. +// foreach (Scene s in pendingLoads) +// connScenes.AddUnique(s); +// +// //The pending collection is no longer needed, cache it. +// CollectionCaches.Store(pendingLoads); +// _pendingClientSceneChanges.Remove(conn); +// } +// +// //Now add scenes the connection is confirmed to be in. +// foreach (Scene s in conn.Scenes) +// { +// connScenes.AddUnique(s); +// +// /* If scene is in SceneConnections, which it should be, then +// * remove the disconnecting player from the connections in that scene. +// * If there are no connections left in that scene then cache the collection. */ +// if (SceneConnections.TryGetValueIL2CPP(s, out HashSet connsInScene)) +// { +// connsInScene.Remove(conn); +// if (connsInScene.Count == 0) +// { +// CollectionCaches.Store(connsInScene); +// SceneConnections.Remove(s); +// } +// } +// } +// +// /* Now remove occupied scenes from scenes the disconnecting +// * client was in. This will remove any scenes that are pending +// * or confirmed load for other clients. */ +// RemoveOccupiedScenes(connScenes); +// //Globals should not be unloaded either. +// RemoveGlobalScenes(connScenes); +// +// List scenesToUnload = CollectionCaches.RetrieveList(); +// //Current active scene. +// Scene activeScene = UnitySceneManager.GetActiveScene(); +// +// foreach (Scene s in connScenes) +// { +// /* Run some additional checks to make sure the scene can be +// * unloaded. If possible, add to scenes to unload. */ +// if (!_manualUnloadScenes.Contains(s) && (s != activeScene)) +// scenesToUnload.Add(s); +// } +// +// //If scenes should be unloaded. +// if (scenesToUnload.Count > 0) +// { +// SceneUnloadData sud = new(SceneLookupData.CreateData(scenesToUnload)); +// UnloadConnectionScenes(Array.Empty(), sud); +// } +// +// CollectionCaches.Store(connScenes); +// CollectionCaches.Store(scenesToUnload); +// } +// #endregion +// +// #region Server received messages. +// /// +// /// Received on server when a client loads scenes. +// /// +// /// +// /// +// private void OnClientLoadedScenes(NetworkConnection conn, ClientScenesLoadedBroadcast msg, Channel channel) +// { +// HashSet pendingLoads; +// //There's no loads or unloads pending, kick client. +// if (!_pendingClientSceneChanges.TryGetValueIL2CPP(conn, out pendingLoads) || pendingLoads.Count == 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. +// foreach (SceneLookupData item in msg.SceneLookupDatas) +// { +// Scene s = item.GetScene(out _); +// pendingLoads.Remove(s); +// } +// +// //If there are no more pending loads for the connection then cache the collections and remove client from pending. +// if (pendingLoads.Count == 0) +// { +// CollectionCaches.Store(pendingLoads); +// _pendingClientSceneChanges.Remove(conn); +// } +// +// if (!Comparers.IsDefault(msg)) +// { +// foreach (SceneLookupData item in msg.SceneLookupDatas) +// { +// Scene s = item.GetScene(out _); +// if (s.IsValid()) +// AddConnectionToScene(conn, s); +// } +// } +// } +// #endregion +// +// #region Events. +// /// +// /// Checks if OnQueueStart should invoke, and if so invokes. +// /// +// private void TryInvokeOnQueueStart() +// { +// if (_sceneQueueStartInvoked) +// return; +// +// _sceneQueueStartInvoked = true; +// IteratingQueue = true; +// OnQueueStart?.Invoke(); +// } +// +// /// +// /// Checks if OnQueueEnd should invoke, and if so invokes. +// /// +// private void TryInvokeOnQueueEnd() +// { +// if (!_sceneQueueStartInvoked) +// return; +// +// _sceneQueueStartInvoked = false; +// IteratingQueue = false; +// QueueCompleteTime = Time.unscaledTime; +// 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(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(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(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(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(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 +// +// /// +// /// Returns global scenes which are not currently being loaded by the server. +// /// +// /// +// private string[] GlobalScenesExcludingLoading() +// { +// HashSet excludedScenes = null; +// foreach (string gs in _globalScenes) +// { +// if (_serverGlobalScenesLoading.Contains(gs)) +// { +// if (excludedScenes == null) +// excludedScenes = new(); +// +// excludedScenes.Add(gs); +// } +// } +// +// //Some scenes are excluded. +// if (excludedScenes != null) +// { +// //All are excluded, quick exit to save perf. +// int remaining = (_globalScenes.Length - excludedScenes.Count); +// if (remaining <= 0) +// return null; +// //Some are excluded. +// List results = new(); +// foreach (string globalScene in _globalScenes) +// { +// if (!excludedScenes.Contains(globalScene)) +// results.Add(globalScene); +// } +// +// return results.ToArray(); +// } +// //No scenes are excluded. +// else +// { +// return _globalScenes; +// } +// } +// +// //#region IsQueuedScene. +// ///// +// ///// Returns if this SceneManager has a scene load or unload in queue for server or client. +// ///// +// ///// True to check loading scenes, false to check unloading. +// ///// True to check if data in queue is for server, false if for client. +// ///// +// //public bool IsQueuedScene(string sceneName, bool loading, bool asServer) +// //{ +// // _sceneLookupDataCache.Update(sceneName, 0); +// // return IsQueuedScene(_sceneLookupDataCache, loading, asServer); +// //} +// ///// +// ///// Returns if this SceneManager has a scene load or unload in queue for server or client. +// ///// +// ///// True to check loading scenes, false to check unloading. +// ///// True to check if data in queue is for server, false if for client. +// ///// +// //public bool IsQueuedScene(int handle, bool loading, bool asServer) +// //{ +// // _sceneLookupDataCache.Update(string.Empty, handle); +// // return IsQueuedScene(_sceneLookupDataCache, loading, asServer); +// //} +// ///// +// ///// Returns if this SceneManager has a scene load or unload in queue for server or client. +// ///// +// ///// True to check loading scenes, false to check unloading. +// ///// True to check if data in queue is for server, false if for client. +// ///// +// //public bool IsQueuedScene(Scene scene, bool loading, bool asServer) +// //{ +// // _sceneLookupDataCache.Update(scene.name, scene.handle); +// // return IsQueuedScene(_sceneLookupDataCache, loading, asServer); +// //} +// ///// +// ///// Returns if this SceneManager has a scene load or unload in queue for server or client. +// ///// +// ///// True to check loading scenes, false to check unloading. +// ///// True to check if data in queue is for server, false if for client. +// ///// +// //public bool IsQueuedScene(SceneLookupData sld, bool loading, bool asServer) +// //{ +// // foreach (object item in _queuedOperations) +// // { +// // SceneLookupData[] lookupDatas = null; +// // //Loading check. +// // if (loading && item is SceneLoadData loadData) +// // lookupDatas = loadData.SceneLookupDatas; +// // else if (!loading && item is SceneUnloadData unloadData) +// // lookupDatas = unloadData.SceneLookupDatas; +// +// // if (lookupDatas != null) +// // { +// // foreach (SceneLookupData operationSld in lookupDatas) +// // { +// // if (operationSld == sld) +// // return true; +// // } +// // } +// // } +// +// // //Fall through, not found in any queue operations. +// // return false; +// //} +// //#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); +// } +// +// 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(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) +// { +// //This cannot use cache because the array will persist for many frames after this method completion. +// 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); +// } +// +// 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(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.GetIsNetworked()) +// 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 == DDOL.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() +// { +// try +// { +// 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.IsServerStarted); +// +// //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(); +// //Add to server global scenes which are currently loading. +// foreach (string item in names) +// _serverGlobalScenesLoading.Add(item); +// //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); +// } +// CheckForDuplicateGlobalSceneNames(); +// 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 requestedLoadSceneHandles = new(); +// +// /* 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(); +// 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(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(); +// //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(); +// //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. */ +// InvokeOnSceneLoadStart(data); +// if (unloadableScenes.Count > 0 || loadableScenes.Count > 0) +// _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(); +// /* 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() +// { +// 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); +// } +// +// Scene lastLoadedScene = _sceneProcessor.GetLastLoadedScene(); +// /* If the lastLoadedScene returns default +// * then the user is overriding the sceneprocessor +// * and has not setup use for this particular API. */ +// if (lastLoadedScene == default) +// lastLoadedScene = UnitySceneManager.GetSceneAt(UnitySceneManager.sceneCount - 1); +// +// loadedScenes.Add(lastLoadedScene); +// _sceneProcessor.AddLoadedScene(lastLoadedScene); +// } + +// //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; +// do +// { +// //Reset state for iteration https://github.com/FirstGearGames/FishNet/issues/322 +// allScenesLoaded = true; +// 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, asServer, out byUser); +// //If preferred still is not set then try to figure it out. +// if (!preferredActiveScene.IsValid()) +// { +// bool setToFirstLookup = false; +// //If any scenes are being replaced see if active needs to be updated. +// if (sceneLoadData.ReplaceScenes != ReplaceOption.None) +// { +// //If load is for a connection and server isnt started. +// setToFirstLookup |= (data.ScopeType == SceneScopeType.Connections && !NetworkManager.IsServerStarted); +// /* If current active is the movedObjectsHolder, such as moved objects. +// * This can happen when replacing a scene that was active and the next in line is +// * set by unity as one of the temp scenes. */ +// Scene activeScene = UnitySceneManager.GetActiveScene(); +// setToFirstLookup |= (activeScene == GetMovedObjectsScene()); +// } +// +// if (setToFirstLookup) +// 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(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.IsServerStarted) +// { +// //Tell clients to load same scenes. +// LoadScenesBroadcast msg = new() +// { +// QueueData = data +// }; +// +// //Replace scene lookup datas with ones intended to broadcast to client. +// msg.QueueData.SceneLoadData.SceneLookupDatas = broadcastLookupDatas; +// +// /* Build scenes the client will be loading. This is used to +// * add the client to pending loading for these scenes.*/ +// List scenes = CollectionCaches.RetrieveList(); +// foreach (SceneLookupData sud in msg.QueueData.SceneLoadData.SceneLookupDatas) +// scenes.Add(sud.GetScene(out _)); +// +// //If networked scope then send to all. +// if (data.ScopeType == SceneScopeType.Global) +// { +// NetworkConnection[] conns = _serverManager.Clients.Values.ToArray(); +// AddPendingLoad(conns, scenes); +// _serverManager.Broadcast(msg, true); +// } +// //If connections scope then only send to connections. +// else if (data.ScopeType == SceneScopeType.Connections) +// { +// AddPendingLoad(data.Connections, scenes); +// for (int i = 0; i < data.Connections.Length; i++) +// { +// NetworkConnection c = data.Connections[i]; +// if (c.IsValid() && c.IsAuthenticated) +// data.Connections[i].Broadcast(msg, true); +// } +// } +// +// CollectionCaches.Store(scenes); +// } +// /* 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.IsClientStarted) +// { +// //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); +// +// TryInvokeLoadedStartScenes(_clientManager.Connection, asServer: false); +// +// ClientScenesLoadedBroadcast msg = new() +// { +// SceneLookupDatas = sceneLoadData.SceneLookupDatas +// }; +// _clientManager.Broadcast(msg); +// } +// +// InvokeOnSceneLoadEnd(data, requestedLoadSceneNames, loadedScenes, unloadedNames); +// } +// finally +// { +// _serverGlobalScenesLoading.Clear(); +// } +// } +// +// /// +// /// Received on client when connection scenes must be loaded. +// /// +// /// +// /// +// private void OnLoadScenes(LoadScenesBroadcast msg, Channel channel) +// { +// //Null data is sent by the server when there are no start scenes to load. +// if (msg.QueueData == null) +// { +// TryInvokeLoadedStartScenes(_clientManager.Connection, false); +// } +// else +// { +// 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(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) +// { +// //This cannot use cache because the array will persist for many frames after this method completion. +// 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); +// } +// +// 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(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.IsServerStarted); +// ///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($"Scene lookup data of length {sceneUnloadData.SceneLookupDatas.Length} could not find any scenes to unload. This may occur when trying to unload a scene only by handle. Consider using the scene reference or handle and name while creating SceneLookupData."); +// 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(); +// /* 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(); +// //If clientOnly then force mode to unloadUnused. +// else if (!asServer && !asClientHost) +// sceneUnloadData.Options.Mode = UnloadOptions.ServerUnloadMode.UnloadUnused; +// /* 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) +// { +// if (!s.IsValid()) +// { +// NetworkManager.LogWarning($"A scene was expected to be unloaded but could not due to it's referening going missing. This usually occurs when the same scene has been queued for unloading multiple times."); +// continue; +// } +// +// unloadedScenes.Add(new(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, asServer, 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() +// { +// 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++) +// { +// NetworkConnection conn = data.Connections[i]; +// //Would not be null from internals, but users might incorrectly pass null in. +// if (conn.IsValid()) +// 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, Channel channel) +// { +// 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 (!_moveClientObjects) +// 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.IsClientStarted) +// 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. */ +// +// List movingNobs = CollectionCaches.RetrieveList(); +// /* 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.Add(nob); +// } +// +// int count = movingNobs.Count; +// if (count > 0) +// { +// Scene moveScene = GetDelayedDestroyScene(); +// for (int i = 0; i < count; i++) +// { +// NetworkObject nob = movingNobs[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); +// } +// } +// CollectionCaches.Store(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.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) +// { +// if (!conn.IsValid()) +// return; +// if (!scene.isLoaded || !scene.IsValid()) +// { +// NetworkManager.LogError($"Only valid, loaded scenes may be used."); +// return; +// } +// +// HashSet hs; +// //Scene doesn't have any connections yet. +// bool inSceneConnections = SceneConnections.TryGetValueIL2CPP(scene, out hs); +// if (!inSceneConnections) +// hs = new(); +// +// 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); +// 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(); +// +// 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(); +// //Remove every connection from the scene. +// foreach (NetworkConnection c in conns) +// { +// if (!c.IsValid()) +// continue; +// +// 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) +// { +// InvokeClientPresenceChange(scene, connectionsRemoved, false, true); +// RebuildObservers(connectionsRemoved); +// InvokeClientPresenceChange(scene, connectionsRemoved, 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(); +// //Remove every connection from the scene. +// foreach (NetworkConnection c in conns) +// { +// if (!c.IsValid()) +// continue; +// +// 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(IList networkObjects) +// { +// NetworkObject nob; +// int count = networkObjects.Count; +// for (int i = 0; i < count; i++) +// { +// nob = networkObjects[i]; +// if (nob != null && nob.IsSpawned) +// _serverManager.Objects.RebuildObservers(nob); +// } +// } +// +// /// +// /// Rebuilds all NetworkObjects for connection. +// /// +// internal void RebuildObservers(NetworkConnection connection) +// { +// List connCache = CollectionCaches.RetrieveList(connection); +// RebuildObservers(connCache); +// CollectionCaches.Store(connCache); +// } +// +// /// +// /// Rebuilds all NetworkObjects for connections. +// /// +// internal void RebuildObservers(IList connections) +// { +// int count = connections.Count; +// for (int i = 0; i < count; i++) +// _serverManager.Objects.RebuildObservers(connections[i]); +// } +// +// /// +// /// Invokes OnClientPresenceChange start or end. +// /// +// private void InvokeClientPresenceChange(Scene scene, IList conns, bool added, bool start) +// { +// NetworkConnection c; +// int count = conns.Count; +// for (int i = 0; i < count; i++) +// { +// c = conns[i]; +// ClientPresenceChangeEventArgs cpc = new(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(); +// 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. +// /// +// /// Name of scene to retrieve. +// /// NetworkManager to use for debug print. This value may be left null. +// /// True to warn if scene name is found loaded multiple times. +// /// +// public static Scene GetScene(string sceneName, NetworkManager nm = null, bool warnIfDuplicates = true) +// { +// Scene result = default; +// sceneName = sceneName.ToLower(); +// +// int count = UnitySceneManager.sceneCount; +// for (int i = 0; i < count; i++) +// { +// Scene s = UnitySceneManager.GetSceneAt(i); +// //Matches. +// if (s.name.ToLower() == sceneName) +// { +// //If result is already set. +// if (result.IsValid()) +// { +// if (warnIfDuplicates) +// { +// string msg = $"Scene name {s.name} is loaded multiple times. The first scene found will be returned. If you wish to unload multiple instances of a scene with the same name create {nameof(SceneLookupData)} using scene handles instead of name."; +// nm.LogWarning(msg); +// //No need to spam the message, break on first duplicate. +// break; +// } +// } +// else +// { +// result = s; +// } +// } +// } +// +// return result; +// } +// +// /// +// /// 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(); +// } +// #endregion +// +// /// +// /// Returns if GlobalScenes contains scene. +// /// +// /// +// /// +// private bool IsGlobalScene(Scene scene) +// { +// foreach (string item in _globalScenes) +// { +// string nameOnly = System.IO.Path.GetFileNameWithoutExtension(item); +// if (item == scene.name || nameOnly == scene.name) +// return true; +// } +// return false; +// } +// +// /// +// /// Warns if any scene names in GlobalScenes are unsupported. +// /// This only applies to FishNet version 3. +// /// +// private void CheckForDuplicateGlobalSceneNames() +// { +// /* This is being removed between version 4.0.0 to 4.1.0 */ +// HashSet namesOnly = CollectionCaches.RetrieveHashSet(); +// foreach (string item in _globalScenes) +// { +// string name = System.IO.Path.GetFileNameWithoutExtension(item); +// if (namesOnly.Contains(name)) +// { +// NetworkManager.LogWarning($"There are multiple global scenes loaded with the same NameOnly. This occurs when a global scene has the same name as another but resides in a different folder path. Each global scene name must be unique."); +// break; +// } +// else +// { +// namesOnly.Add(name); +// } +// } +// } +// +// /// +// /// 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++) +// { +// Scene currentScene = scenes[i]; +// /* If any connections are in the scene being checked +// * then remove from scenes. */ +// if (SceneConnections.TryGetValueIL2CPP(currentScene, out _)) +// { +// RemoveAndSubtractIndex(ref i); +// } +// /* Not in scene connections, see if any connection has +// * the scene pending load. */ +// else +// { +// foreach (HashSet pendingScenes in _pendingClientSceneChanges.Values) +// { +// if (pendingScenes.Contains(currentScene)) +// { +// RemoveAndSubtractIndex(ref i); +// /* No need to keep checking pending for currentScene +// * since it was removed from scenes. */ +// break; +// } +// } +// } +// } +// +// void RemoveAndSubtractIndex(ref int index) +// { +// scenes.RemoveAt(index--); +// } +// } +// +// /// +// /// Adds a pending load for a connection. +// /// +// private void AddPendingLoad(NetworkConnection conn, List scenes) +// { +// NetworkConnection[] conns = CollectionCaches.RetrieveArray(); +// if (conns.Length == 0) +// conns = new NetworkConnection[1]; +// conns[0] = conn; +// +// AddPendingLoad(conns, scenes); +// CollectionCaches.Store(conns, 1); +// } +// +// /// +// /// Adds a pending load for a connection. +// /// +// private void AddPendingLoad(NetworkConnection[] conns, List scenes) +// { +// 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.IsAuthenticated) +// continue; +// +// HashSet pendingScenes; +// if (!_pendingClientSceneChanges.TryGetValueIL2CPP(c, out pendingScenes)) +// { +// pendingScenes = CollectionCaches.RetrieveHashSet(); +// _pendingClientSceneChanges[c] = pendingScenes; +// } +// +// foreach (Scene scene in scenes) +// pendingScenes.Add(scene); +// } +// } +// +// /// +// /// 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); +// } +// //Setting active scene is not used. +// else if (!_setActiveScene) +// { +// //Still invoke event with current scene. +// Scene s = UnitySceneManager.GetActiveScene(); +// CompleteSetActive(s); +// return; +// } +// //Need to figure out which scene to use. +// else +// { +// Scene s = default; +// +// if (_globalScenes.Length > 0) +// s = GetScene(_globalScenes[0], NetworkManager, false); +// 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() => _sceneProcessor.GetFallbackActiveScene(); +// +// /// +// /// Returns the MovedObjectsScene. +// /// +// /// +// private Scene GetMovedObjectsScene() => _sceneProcessor.GetMovedObjectsScene(); +// +// /// +// /// Returns the DelayedDestroyScene. +// /// +// /// +// private Scene GetDelayedDestroyScene() => _sceneProcessor.GetDelayedDestroyScene(); +// +// /// +// /// Returns a preferred active scene to use. +// /// +// private Scene GetUserPreferredActiveScene(PreferredScene ps, bool asServer, out bool byUser) +// { +// byUser = false; +// SceneLookupData sld = (asServer) ? ps.Server : ps.Client; +// //Not specified. +// if (sld == null) +// return default; +// +// Scene s = sld.GetScene(out _); +// if (s.IsValid()) +// byUser = true; +// return s; +// } +// +// #region Sanity checks. +// /// +// /// Returns if iterating queue. +// /// True will be returned even if not iterating queue if the iteration had completed with the time requirement. +// internal bool IsIteratingQueue(float completionTimeRequirement = 0f) +// { +// return (IteratingQueue || (Time.unscaledTime - QueueCompleteTime) < completionTimeRequirement); +// } +// +// /// +// /// 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.IsServerStarted : NetworkManager.IsClientStarted; +// } +// +// /// +// /// Returns if a method can execute. +// /// +// /// +// /// +// /// +// private bool CanExecute(bool asServer, bool warn) +// { +// bool result; +// if (asServer) +// { +// result = NetworkManager.IsServerStarted; +// if (!result && warn) +// NetworkManager.LogWarning($"Method cannot be called as the server is not active."); +// } +// else +// { +// result = NetworkManager.IsClientStarted; +// if (!result && warn) +// NetworkManager.LogWarning($"Method cannot be called as the client is not active."); +// } +// +// return result; +// } +// #endregion +// } +// } +// +// #else + +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 GameKit.Dependencies.Utilities; +using GameKit.Dependencies.Utilities.Types; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityEngine.Serialization; +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(); + /// + /// + /// + [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; + + /// + /// NetworkManager for this script. + /// + public NetworkManager NetworkManager { get; private set; } + #endregion + + #region Internal. + /// + /// Called after the active scene has been set, immediately after scene loads. + /// + internal event Action OnActiveSceneSetInternal; + /// + /// True if the SceneManager has items in queue. + /// + internal bool IteratingQueue { get; private set; } + /// + /// Unscaled time when the SceneManager completed it's last queue. + /// + internal float QueueCompleteTime { get; private set; } + #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 spawned objects visible to the client that are within an unloading scene. This ensures the objects are despawned on the client side rather than when the scene is destroyed. + /// + [FormerlySerializedAs("_moveClientHostObjects")] + [Tooltip("True to move spawned objects visible to the client 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 _moveClientObjects = true; + + /// + /// Sets a new value for MoveClientObjects. + /// + public void SetMoveClientObjects(bool value) => _moveClientObjects = value; + + /// + /// 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. + /// + /// 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(); + /// + /// Scenes to load or unload, in order. + /// + private List _queuedOperations = new(); + /// + /// Scenes which must be manually unloaded, even when emptied. + /// + private HashSet _manualUnloadScenes = new(); + /// + /// 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(); + /// + /// 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(); + ///// + ///// Cache of SceneLookupData. + ///// + //private SceneLookupData _sceneLookupDataCache = new SceneLookupData(); + /// + /// GlobalScenes currently loading on the server. + /// + private HashSet _serverGlobalScenesLoading = new(); + #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 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.IsAnyServerStarted()) + ResetValues(); + } + + /// + /// Resets as if first use. + /// + private void ResetValues() + { + SceneConnections.Clear(); + _globalScenes = new string[0]; + _globalSceneLoadData = new(); + _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; + //No need to unregister since managers are on the same object. + NetworkManager.ServerManager.OnRemoteConnectionState += ServerManager_OnRemoteConnectionState; + NetworkManager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + _clientManager.RegisterBroadcast(OnServerLoadedScenes); + _clientManager.RegisterBroadcast(OnServerUnloadedScenes); + _serverManager.RegisterBroadcast(OnClientLoadedScenes); + _serverManager.RegisterBroadcast(OnClientSentEmptyStartScenes); + _clientManager.RegisterBroadcast(OnServerSentEmptyStartScenes); + } + + /// + /// Received when a scene is unloaded. + /// + /// + private void SceneManager_SceneUnloaded(Scene scene) + { + if (!NetworkManager.IsServerStarted) + 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(); + connection.Broadcast(msg); + } + else + { + string[] globalsNotLoading = GlobalScenesExcludingLoading(); + //If there are globals that can be sent now. + if (globalsNotLoading != null) + { + SceneLoadData sld = new(globalsNotLoading); + sld.Params = _globalSceneLoadData.Params; + sld.Options = _globalSceneLoadData.Options; + sld.ReplaceScenes = _globalSceneLoadData.ReplaceScenes; + sld.PreferredActiveScene = _globalSceneLoadData.PreferredActiveScene; + + LoadQueueData qd = new(SceneScopeType.Global, Array.Empty(), sld, _globalScenes, false); + //Send message to load the networked scenes. + LoadScenesBroadcast msg = new() + { + QueueData = qd + }; + + connection.Broadcast(msg, true); + } + } + } + + /// + /// Received on client when the server has no start scenes. + /// + private void OnServerSentEmptyStartScenes(EmptyStartScenesBroadcast msg, Channel channel) + { + TryInvokeLoadedStartScenes(_clientManager.Connection, false); + _clientManager.Broadcast(msg); + } + + /// + /// Received on server when client confirms there are no start scenes. + /// + private void OnClientSentEmptyStartScenes(NetworkConnection conn, EmptyStartScenesBroadcast msg, Channel channel) + { + if (!conn.IsActive) + return; + + //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(), channel); + } + #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(); + //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(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, Channel channel) + { + if (!conn.IsActive) + return; + + 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; + IteratingQueue = true; + OnQueueStart?.Invoke(); + } + + /// + /// Checks if OnQueueEnd should invoke, and if so invokes. + /// + private void TryInvokeOnQueueEnd() + { + if (!_sceneQueueStartInvoked) + return; + + _sceneQueueStartInvoked = false; + IteratingQueue = false; + QueueCompleteTime = Time.unscaledTime; + 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(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(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(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(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(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 + + /// + /// Returns global scenes which are not currently being loaded by the server. + /// + /// + private string[] GlobalScenesExcludingLoading() + { + HashSet excludedScenes = null; + foreach (string gs in _globalScenes) + { + if (_serverGlobalScenesLoading.Contains(gs)) + { + if (excludedScenes == null) + excludedScenes = new(); + + excludedScenes.Add(gs); + } + } + + //Some scenes are excluded. + if (excludedScenes != null) + { + //All are excluded, quick exit to save perf. + int remaining = (_globalScenes.Length - excludedScenes.Count); + if (remaining <= 0) + return null; + //Some are excluded. + List results = new(); + foreach (string globalScene in _globalScenes) + { + if (!excludedScenes.Contains(globalScene)) + results.Add(globalScene); + } + + return results.ToArray(); + } + //No scenes are excluded. + else + { + return _globalScenes; + } + } + + //#region IsQueuedScene. + ///// + ///// Returns if this SceneManager has a scene load or unload in queue for server or client. + ///// + ///// True to check loading scenes, false to check unloading. + ///// True to check if data in queue is for server, false if for client. + ///// + //public bool IsQueuedScene(string sceneName, bool loading, bool asServer) + //{ + // _sceneLookupDataCache.Update(sceneName, 0); + // return IsQueuedScene(_sceneLookupDataCache, loading, asServer); + //} + ///// + ///// Returns if this SceneManager has a scene load or unload in queue for server or client. + ///// + ///// True to check loading scenes, false to check unloading. + ///// True to check if data in queue is for server, false if for client. + ///// + //public bool IsQueuedScene(int handle, bool loading, bool asServer) + //{ + // _sceneLookupDataCache.Update(string.Empty, handle); + // return IsQueuedScene(_sceneLookupDataCache, loading, asServer); + //} + ///// + ///// Returns if this SceneManager has a scene load or unload in queue for server or client. + ///// + ///// True to check loading scenes, false to check unloading. + ///// True to check if data in queue is for server, false if for client. + ///// + //public bool IsQueuedScene(Scene scene, bool loading, bool asServer) + //{ + // _sceneLookupDataCache.Update(scene.name, scene.handle); + // return IsQueuedScene(_sceneLookupDataCache, loading, asServer); + //} + ///// + ///// Returns if this SceneManager has a scene load or unload in queue for server or client. + ///// + ///// True to check loading scenes, false to check unloading. + ///// True to check if data in queue is for server, false if for client. + ///// + //public bool IsQueuedScene(SceneLookupData sld, bool loading, bool asServer) + //{ + // foreach (object item in _queuedOperations) + // { + // SceneLookupData[] lookupDatas = null; + // //Loading check. + // if (loading && item is SceneLoadData loadData) + // lookupDatas = loadData.SceneLookupDatas; + // else if (!loading && item is SceneUnloadData unloadData) + // lookupDatas = unloadData.SceneLookupDatas; + + // if (lookupDatas != null) + // { + // foreach (SceneLookupData operationSld in lookupDatas) + // { + // if (operationSld == sld) + // return true; + // } + // } + // } + + // //Fall through, not found in any queue operations. + // return false; + //} + //#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); + } + + 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(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) + { + //This cannot use cache because the array will persist for many frames after this method completion. + 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); + } + + 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(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.GetIsNetworked()) + 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 == DDOL.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() + { + try + { + 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.IsServerStarted); + + //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(); + //Add to server global scenes which are currently loading. + foreach (string item in names) + _serverGlobalScenesLoading.Add(item); + //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); + } + CheckForDuplicateGlobalSceneNames(); + 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 requestedLoadSceneHandles = new(); + + /* 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(); + 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(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) + { + Scene moveScene = GetMovedObjectsScene(); + 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, moveScene); + } + } + + //Connection scenes handles prior to ConnectionScenes being modified. + List connectionScenesHandlesCached = new(); + //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(); + //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. */ + InvokeOnSceneLoadStart(data); + if (unloadableScenes.Count > 0 || loadableScenes.Count > 0) + _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)) + { + //Only proceed if moving is disabled. This statement is nested for readability. + if (_moveClientObjects) + { + 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(); + /* 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() + { + 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); + } + + Scene lastLoadedScene = _sceneProcessor.GetLastLoadedScene(); + /* If the lastLoadedScene returns default + * then the user is overriding the sceneprocessor + * and has not setup use for this particular API. */ + if (lastLoadedScene == default) + lastLoadedScene = UnitySceneManager.GetSceneAt(UnitySceneManager.sceneCount - 1); + + loadedScenes.Add(lastLoadedScene); + _sceneProcessor.AddLoadedScene(lastLoadedScene); + } + //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; + do + { + //Reset state for iteration https://github.com/FirstGearGames/FishNet/issues/322 + allScenesLoaded = true; + 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, asServer, out byUser); + //If preferred still is not set then try to figure it out. + if (!preferredActiveScene.IsValid()) + { + bool setToFirstLookup = false; + //If any scenes are being replaced see if active needs to be updated. + if (sceneLoadData.ReplaceScenes != ReplaceOption.None) + { + //If load is for a connection and server isnt started. + setToFirstLookup |= (data.ScopeType == SceneScopeType.Connections && !NetworkManager.IsServerStarted); + /* If current active is the movedObjectsHolder, such as moved objects. + * This can happen when replacing a scene that was active and the next in line is + * set by unity as one of the temp scenes. */ + Scene activeScene = UnitySceneManager.GetActiveScene(); + setToFirstLookup |= (activeScene == GetMovedObjectsScene()); + } + + if (setToFirstLookup) + preferredActiveScene = sceneLoadData.GetFirstLookupScene(); + } + + SetActiveScene(preferredActiveScene, asServer, 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(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.IsServerStarted) + { + //Tell clients to load same scenes. + LoadScenesBroadcast msg = new() + { + 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, conns.Length); + _serverManager.Broadcast(msg, true); + } + //If connections scope then only send to connections. + else if (data.ScopeType == SceneScopeType.Connections) + { + AddPendingLoad(data.Connections, data.Connections.Length); + for (int i = 0; i < data.Connections.Length; i++) + { + NetworkConnection c = data.Connections[i]; + if (c.IsValid() && c.IsAuthenticated) + 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.IsClientStarted) + { + //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); + + TryInvokeLoadedStartScenes(_clientManager.Connection, false); + + ClientScenesLoadedBroadcast msg = new() + { + SceneLookupDatas = sceneLoadData.SceneLookupDatas + }; + _clientManager.Broadcast(msg); + } + + InvokeOnSceneLoadEnd(data, requestedLoadSceneNames, loadedScenes, unloadedNames); + } + finally + { + _serverGlobalScenesLoading.Clear(); + } + } + + /// + /// Received on client when connection scenes must be loaded. + /// + /// + /// + private void OnServerLoadedScenes(LoadScenesBroadcast msg, Channel channel) + { + //Null data is sent by the server when there are no start scenes to load. + if (msg.QueueData == null) + { + TryInvokeLoadedStartScenes(_clientManager.Connection, false); + } + else + { + 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(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) + { + //This cannot use cache because the array will persist for many frames after this method completion. + 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); + } + + 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(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.IsServerStarted); + ///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($"Scene lookup data of length {sceneUnloadData.SceneLookupDatas.Length} could not find any scenes to unload. This may occur when trying to unload a scene only by handle. Consider using the scene reference or handle and name while creating SceneLookupData."); + 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(); + /* 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(); + //If clientOnly then force mode to unloadUnused. + else if (!asServer && !asClientHost) + sceneUnloadData.Options.Mode = UnloadOptions.ServerUnloadMode.UnloadUnused; + /* 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) + { + if (!s.IsValid()) + { + NetworkManager.LogWarning($"A scene was expected to be unloaded but could not due to it's referening going missing. This usually occurs when the same scene has been queued for unloading multiple times."); + continue; + } + + unloadedScenes.Add(new(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, asServer, out byUser); + SetActiveScene(preferredActiveScene, asServer, 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() + { + 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++) + { + NetworkConnection conn = data.Connections[i]; + //Would not be null from internals, but users might incorrectly pass null in. + if (conn.IsValid()) + 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 OnServerUnloadedScenes(UnloadScenesBroadcast msg, Channel channel) + { + 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 (!_moveClientObjects) + 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.IsClientStarted) + 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. */ + + List movingNobs = CollectionCaches.RetrieveList(); + /* 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.Add(nob); + } + + int count = movingNobs.Count; + if (count > 0) + { + Scene moveScene = GetDelayedDestroyScene(); + for (int i = 0; i < count; i++) + { + NetworkObject nob = movingNobs[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); + } + } + CollectionCaches.Store(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.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) + { + if (!conn.IsValid()) + return; + if (!scene.isLoaded || !scene.IsValid()) + { + NetworkManager.LogError($"Only valid, loaded scenes may be used."); + return; + } + + HashSet hs; + //Scene doesn't have any connections yet. + bool inSceneConnections = SceneConnections.TryGetValueIL2CPP(scene, out hs); + if (!inSceneConnections) + hs = new(); + + 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); + 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(); + + 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(); + //Remove every connection from the scene. + foreach (NetworkConnection c in conns) + { + if (!c.IsValid()) + continue; + + 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) + { + InvokeClientPresenceChange(scene, connectionsRemoved, false, true); + RebuildObservers(connectionsRemoved); + InvokeClientPresenceChange(scene, connectionsRemoved, 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(); + //Remove every connection from the scene. + foreach (NetworkConnection c in conns) + { + if (!c.IsValid()) + continue; + + 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(IList networkObjects) + { + NetworkObject nob; + int count = networkObjects.Count; + for (int i = 0; i < count; i++) + { + nob = networkObjects[i]; + if (nob != null && nob.IsSpawned) + _serverManager.Objects.RebuildObservers(nob); + } + } + + /// + /// Rebuilds all NetworkObjects for connection. + /// + internal void RebuildObservers(NetworkConnection connection) + { + List connCache = CollectionCaches.RetrieveList(connection); + RebuildObservers(connCache); + CollectionCaches.Store(connCache); + } + + /// + /// Rebuilds all NetworkObjects for connections. + /// + internal void RebuildObservers(IList connections) + { + int count = connections.Count; + for (int i = 0; i < count; i++) + _serverManager.Objects.RebuildObservers(connections[i]); + } + + /// + /// Invokes OnClientPresenceChange start or end. + /// + private void InvokeClientPresenceChange(Scene scene, IList conns, bool added, bool start) + { + NetworkConnection c; + int count = conns.Count; + for (int i = 0; i < count; i++) + { + c = conns[i]; + ClientPresenceChangeEventArgs cpc = new(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(); + 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. + /// + /// Name of scene to retrieve. + /// NetworkManager to use for debug print. This value may be left null. + /// True to warn if scene name is found loaded multiple times. + /// + public static Scene GetScene(string sceneName, NetworkManager nm = null, bool warnIfDuplicates = true) + { + Scene result = default; + sceneName = sceneName.ToLower(); + + int count = UnitySceneManager.sceneCount; + for (int i = 0; i < count; i++) + { + Scene s = UnitySceneManager.GetSceneAt(i); + //Matches. + if (s.name.ToLower() == sceneName) + { + //If result is already set. + if (result.IsValid()) + { + if (warnIfDuplicates) + { + string msg = $"Scene name {s.name} is loaded multiple times. The first scene found will be returned. If you wish to unload multiple instances of a scene with the same name create {nameof(SceneLookupData)} using scene handles instead of name."; + nm.LogWarning(msg); + //No need to spam the message, break on first duplicate. + break; + } + } + else + { + result = s; + } + } + } + + return result; + } + + /// + /// 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(); + } + #endregion + + /// + /// Returns if GlobalScenes contains scene. + /// + /// + /// + private bool IsGlobalScene(Scene scene) + { + foreach (string item in _globalScenes) + { + string nameOnly = System.IO.Path.GetFileNameWithoutExtension(item); + if (item == scene.name || nameOnly == scene.name) + return true; + } + return false; + } + + /// + /// Warns if any scene names in GlobalScenes are unsupported. + /// This only applies to FishNet version 3. + /// + private void CheckForDuplicateGlobalSceneNames() + { + /* This is being removed between version 4.0.0 to 4.1.0 */ + HashSet namesOnly = CollectionCaches.RetrieveHashSet(); + foreach (string item in _globalScenes) + { + string name = System.IO.Path.GetFileNameWithoutExtension(item); + if (namesOnly.Contains(name)) + { + NetworkManager.LogWarning($"There are multiple global scenes loaded with the same NameOnly. This occurs when a global scene has the same name as another but resides in a different folder path. Each global scene name must be unique."); + break; + } + else + { + namesOnly.Add(name); + } + } + } + + /// + /// 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) + { + NetworkConnection[] conns = CollectionCaches.RetrieveArray(); + if (conns.Length == 0) + conns = new NetworkConnection[1]; + conns[0] = conn; + + AddPendingLoad(conns, 1); + CollectionCaches.Store(conns, 1); + } + + /// + /// Adds a pending load for a connection. + /// + private void AddPendingLoad(NetworkConnection[] conns, int count) + { + 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.IsAuthenticated) + 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, bool asServer, bool byUser) + { + //If user specified then skip figuring it out checks. + if (byUser && preferredScene.IsValid()) + { + CompleteSetActive(preferredScene); + } + //Setting active scene is not used. + else if (!_setActiveScene) + { + //Still invoke event with current scene. + Scene s = UnitySceneManager.GetActiveScene(); + CompleteSetActive(s); + } + //Need to figure out which scene to use. + else + { + Scene s = default; + + if (_globalScenes.Length > 0) + s = GetScene(_globalScenes[0], NetworkManager, false); + 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(asServer); + + 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() => _sceneProcessor.GetFallbackActiveScene(); + + /// + /// Returns the MovedObjectsScene. + /// + /// + private Scene GetMovedObjectsScene() => _sceneProcessor.GetMovedObjectsScene(); + + /// + /// Returns the DelayedDestroyScene. + /// + /// + private Scene GetDelayedDestroyScene() => _sceneProcessor.GetDelayedDestroyScene(); + + /// + /// Returns a preferred active scene to use. + /// + private Scene GetUserPreferredActiveScene(PreferredScene ps, bool asServer, out bool byUser) + { + byUser = false; + SceneLookupData sld = (asServer) ? ps.Server : ps.Client; + //Not specified. + if (sld == null) + return default; + + Scene s = sld.GetScene(out _); + if (s.IsValid()) + byUser = true; + return s; + } + + #region Sanity checks. + /// + /// Returns if iterating queue. + /// True will be returned even if not iterating queue if the iteration had completed with the time requirement. + internal bool IsIteratingQueue(float completionTimeRequirement = 0f) + { + return (IteratingQueue || (Time.unscaledTime - QueueCompleteTime) < completionTimeRequirement); + } + + /// + /// 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.IsServerStarted : NetworkManager.IsClientStarted; + } + + /// + /// Returns if a method can execute. + /// + /// + /// + /// + private bool CanExecute(bool asServer, bool warn) + { + bool result; + if (asServer) + { + result = NetworkManager.IsServerStarted; + if (!result && warn) + NetworkManager.LogWarning($"Method cannot be called as the server is not active."); + } + else + { + result = NetworkManager.IsClientStarted; + if (!result && warn) + NetworkManager.LogWarning($"Method cannot be called as the client is not active."); + } + + return result; + } + #endregion + } +} + +//#endif \ 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..ee45157 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 15895a51081447d46bda466e7e830c08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/SceneManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs new file mode 100644 index 0000000..98fc438 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs @@ -0,0 +1,163 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; +using UnityScene = UnityEngine.SceneManagement.Scene; +using UnitySceneManager = UnityEngine.SceneManagement.SceneManager; + +namespace FishNet.Managing.Scened +{ + + public abstract class SceneProcessorBase : MonoBehaviour + { + #region Protected. + /// + /// SceneManager for this processor. + /// + protected SceneManager SceneManager; + /// + /// Scene used to store objects while they are being moved from one scene to another. + /// + protected Scene MovedObjectsScene; + /// + /// Scene used to store objects queued for destruction but cannot be destroyed until the clientHost gets the despawn packet. + /// + protected Scene DelayedDestroyScene; + /// + /// Scene used as the active scene when the user does not specify which scene to set active and the scenemanager cannot determine one without error. + /// This is primarily used so scenes with incorrect or unexpected lighting are not set as the active scene given this may disrupt visuals. + /// + protected Scene FallbackActiveScene; + #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(); + /// + /// Gets the scene last loaded by the processor. + /// + /// This is called after IsPercentComplete returns true. + public virtual Scene GetLastLoadedScene() => default; + /// + /// 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(); + + /// + /// Returns the MovedObjectsScene. + /// + /// + public virtual Scene GetMovedObjectsScene() + { + //Create moved objects scene. It will probably be used eventually. If not, no harm either way. + if (string.IsNullOrEmpty(MovedObjectsScene.name)) + MovedObjectsScene = FindOrCreateScene("MovedObjectsHolder"); + + return MovedObjectsScene; + } + + /// + /// Returns the DelayedDestroyScene. + /// + /// + public virtual Scene GetDelayedDestroyScene() + { + //Create moved objects scene. It will probably be used eventually. If not, no harm either way. + if (string.IsNullOrEmpty(DelayedDestroyScene.name)) + DelayedDestroyScene = FindOrCreateScene("DelayedDestroy"); + + return DelayedDestroyScene; + } + + /// + /// Returns the FallbackActiveScene. + /// + /// + public virtual Scene GetFallbackActiveScene() + { + if (string.IsNullOrEmpty(FallbackActiveScene.name)) + FallbackActiveScene = FindOrCreateScene("FallbackActiveScene"); + + return FallbackActiveScene; + } + + /// + /// Tries to find a scene by name and if it does not exist creates an empty scene of name. + /// + /// Name of the scene to find or create. + /// + public virtual Scene FindOrCreateScene(string name) + { + Scene result = UnitySceneManager.GetSceneByName(name); + if (!result.IsValid()) + result = UnitySceneManager.CreateScene(name); + + return result; + } + } + + +} \ 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..1f9fae3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: de3f29952a63dc341a7542a1f898cb12 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/SceneProcessorBase.cs + uploadId: 762203 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..ec878df --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 405b031a6ef64b346ae8c5ccbf07d8e1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/SceneSpawner.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs new file mode 100644 index 0000000..67b4c70 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs @@ -0,0 +1,39 @@ +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; + } + + /// + /// Returns a scene based on handle. + /// Result may not be valid as some Unity versions discard of the scene information after unloading. + /// + /// + public Scene GetScene() + { + int loadedScenes = UnityEngine.SceneManagement.SceneManager.sceneCount; + for (int i = 0; i < loadedScenes; i++) + { + Scene s = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i); + if (s.IsValid() && s.handle == Handle) + return s; + } + + return default; + } + } +} 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..6c746d1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a51fea764b1081c4ab6308101f160f10 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Scened/UnloadedScene.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Server.meta b/Assets/FishNet/Runtime/Managing/Server.meta new file mode 100644 index 0000000..43c76e4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 263d37a9ed8fb8a4dbff1095a7b20695 +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..33f2823 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs @@ -0,0 +1,42 @@ +using FishNet.Broadcast; +using FishNet.CodeGenerating; +using FishNet.Serializing; +using FishNet.Utility.Performance; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; + +namespace FishNet.Managing.Server +{ + public struct ClientConnectionChangeBroadcast : IBroadcast + { + public bool Connected; + public int Id; + } + + [UseGlobalCustomSerializer] + public struct ConnectedClientsBroadcast : IBroadcast + { + public List Values; + } + + + internal static class ConnectedClientsBroadcastSerializers + { + public static void WriteConnectedClientsBroadcast(this Writer writer, ConnectedClientsBroadcast value) + { + writer.WriteList(value.Values); + } + + public static ConnectedClientsBroadcast ReadConnectedClientsBroadcast(this Reader reader) + { + List cache = CollectionCaches.RetrieveList(); + reader.ReadList(ref cache); + return new() + { + Values = cache + }; + } + + } +} + 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..d00bedc --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a16b83a545be8f8488795783a0fc8648 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/ClientConnectionBroadcast.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Server/Editor.meta b/Assets/FishNet/Runtime/Managing/Server/Editor.meta new file mode 100644 index 0000000..354c46a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c150c74713eeaeb47a387a0f34529ad4 +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..5d93e03 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs @@ -0,0 +1,96 @@ +#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 _remoteClientTimeout; + private SerializedProperty _remoteClientTimeoutDuration; + private SerializedProperty _syncTypeRate; + private SerializedProperty SpawnPacking; + private SerializedProperty _changeFrameRate; + private SerializedProperty _frameRate; + private SerializedProperty _shareIds; + private SerializedProperty _startOnHeadless; + private SerializedProperty _allowPredictedSpawning; + private SerializedProperty _reservedObjectIds; + + protected virtual void OnEnable() + { + _authenticator = serializedObject.FindProperty(nameof(_authenticator)); + _remoteClientTimeout = serializedObject.FindProperty(nameof(_remoteClientTimeout)); + _remoteClientTimeoutDuration = serializedObject.FindProperty(nameof(_remoteClientTimeoutDuration)); + _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)); + _allowPredictedSpawning = serializedObject.FindProperty(nameof(_allowPredictedSpawning)); + _reservedObjectIds = serializedObject.FindProperty(nameof(_reservedObjectIds)); + + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour((ServerManager)target), typeof(ServerManager), false); + GUI.enabled = true; + + EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_syncTypeRate); + EditorGUILayout.PropertyField(SpawnPacking); + EditorGUILayout.PropertyField(_changeFrameRate); + if (_changeFrameRate.boolValue) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_frameRate); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_startOnHeadless); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Connections", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_remoteClientTimeout); + if ((RemoteTimeoutType)_remoteClientTimeout.intValue != RemoteTimeoutType.Disabled) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_remoteClientTimeoutDuration,new GUIContent("Timeout")); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_shareIds); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Security", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_authenticator); + + EditorGUILayout.PropertyField(_allowPredictedSpawning); + if (_allowPredictedSpawning.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_reservedObjectIds); + EditorGUI.indentLevel--; + } + EditorGUI.indentLevel--; + 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..6136c1f --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: da6ea97e6b868974e8ac139fe545e986 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/Editor/ServerManagerEditor.cs + uploadId: 762203 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..a2fc25a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/KickReasons.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9289a823878c5d1408e7106e6ed5d866 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/KickReasons.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Server/Object.meta b/Assets/FishNet/Runtime/Managing/Server/Object.meta new file mode 100644 index 0000000..bed20c4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 624750768480fa840b40449a42006488 +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..99f7cb9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs @@ -0,0 +1,460 @@ +using FishNet.Connection; +using FishNet.Managing.Object; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Observing; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +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(100); + /// + /// NetworkObservers which require regularly iteration. + /// + private List _timedNetworkObservers = new(); + /// + /// Index in TimedNetworkObservers to start on next cycle. + /// + private int _nextTimedObserversIndex; + /// + /// Used to write spawns for everyone. This writer will exclude owner only information. + /// + private PooledWriter _writer = new(); + /// + /// Indexes within TimedNetworkObservers which are unset. + /// + private Queue _emptiedTimedIndexes = new(); + #endregion + + /// + /// Called when MonoBehaviours call Update. + /// + private void Observers_OnUpdate() + { + UpdateTimedObservers(); + } + + /// + /// Progressively updates NetworkObservers with timed conditions. + /// + private void UpdateTimedObservers() + { + if (!base.NetworkManager.IsServerStarted) + return; + //No point in updating if the timemanager isn't going to tick this frame. + if (!base.NetworkManager.TimeManager.FrameTicked) + return; + int networkObserversCount = _timedNetworkObservers.Count; + if (networkObserversCount == 0) + return; + + /* Try to iterate all timed observers every half a second. + * This value will increase as there's more observers or timed conditions. */ + float timeMultiplier = 1f + ((base.NetworkManager.ServerManager.Clients.Count * 0.005f) + (_timedNetworkObservers.Count * 0.0005f)); + //Check cap this way for readability. + float completionTime = Mathf.Min((0.5f * timeMultiplier), base.NetworkManager.ObserverManager.MaximumTimedObserversDuration); + uint completionTicks = base.NetworkManager.TimeManager.TimeToTicks(completionTime, TickRounding.RoundUp); + /* Iterations will be the number of objects + * to iterate to be have completed all objects by + * the end of completionTicks. */ + int iterations = Mathf.CeilToInt((float)networkObserversCount / completionTicks); + if (iterations > _timedNetworkObservers.Count) + iterations = _timedNetworkObservers.Count; + + List connCache = RetrieveAuthenticatedConnections(); + //Build nob cache. + List nobCache = CollectionCaches.RetrieveList(); + for (int i = 0; i < iterations; i++) + { + if (_nextTimedObserversIndex >= _timedNetworkObservers.Count) + _nextTimedObserversIndex = 0; + + NetworkObject nob = _timedNetworkObservers[_nextTimedObserversIndex++]; + if (nob != null) + nobCache.Add(nob); + } + + RebuildObservers(nobCache, connCache, true); + + CollectionCaches.Store(connCache); + CollectionCaches.Store(nobCache); + } + + /// + /// Indicates that a networkObserver component should be updated regularly. This is done automatically. + /// + /// NetworkObject to be updated. + public void AddTimedNetworkObserver(NetworkObject networkObject) + { + if (_emptiedTimedIndexes.TryDequeue(out int index)) + _timedNetworkObservers[index] = networkObject; + else + _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) + { + int index = _timedNetworkObservers.IndexOf(networkObject); + if (index == -1) + return; + + _emptiedTimedIndexes.Enqueue(index); + _timedNetworkObservers[index] = null; + + //If there's a decent amount missing then rebuild the collection. + if (_emptiedTimedIndexes.Count > 20) + { + List newLst = CollectionCaches.RetrieveList(); + foreach (NetworkObject nob in _timedNetworkObservers) + { + if (nob == null) + continue; + + newLst.Add(nob); + } + + CollectionCaches.Store(_timedNetworkObservers); + _timedNetworkObservers = newLst; + _emptiedTimedIndexes.Clear(); + } + } + + /// + /// Gets all NetworkConnections which are authenticated. + /// + /// + private List RetrieveAuthenticatedConnections() + { + List cache = CollectionCaches.RetrieveList(); + foreach (NetworkConnection item in NetworkManager.ServerManager.Clients.Values) + { + if (item.IsAuthenticated) + cache.Add(item); + } + + return cache; + } + + /// + /// Gets all spawned objects with root objects first. + /// + /// + private List RetrieveOrderedSpawnedObjects() + { + List spawnedCache = GetSpawnedNetworkObjects(); + + List sortedCache = SortRootAndNestedByInitializeOrder(spawnedCache); + + CollectionCaches.Store(spawnedCache); + + return sortedCache; + } + + /// + /// Returns spawned NetworkObjects as a list. + /// Collection returned is a new cache and should be disposed of properly. + /// + /// + private List GetSpawnedNetworkObjects() + { + List cache = CollectionCaches.RetrieveList(); + Spawned.ValuesToList(ref cache); + + return cache; + } + + /// + /// Sorts a collection of NetworkObjects root and nested by initialize order. + /// Collection returned is a new cache and should be disposed of properly. + /// + internal List SortRootAndNestedByInitializeOrder(List nobs) + { + List sortedRootCache = CollectionCaches.RetrieveList(); + + //First order root objects. + foreach (NetworkObject item in nobs) + { + if (item.IsNested) + continue; + + sortedRootCache.AddOrdered(item); + } + + /* After all root are ordered check + * their nested. Order nested in segments + * of each root then insert after the root. + * This must be performed after all roots are ordered. */ + + //This holds the results of all values. + List sortedRootAndNestedCache = CollectionCaches.RetrieveList(); + + //Cache for sorting nested. + List sortedNestedCache = CollectionCaches.RetrieveList(); + + foreach (NetworkObject item in sortedRootCache) + { + /* Remove recursive and only check Initialized and Runtime. Once iterated + * check each added entry again using Initialized and Recursive. */ + List nested = item.GetNetworkObjects(GetNetworkObjectOption.AllNestedRecursive); + foreach (NetworkObject nestedItem in nested) + { + if (sortedNestedCache.Contains(nestedItem)) + Debug.LogError("Already contains " + nestedItem.name); + else + sortedNestedCache.AddOrdered(nestedItem); + } + + CollectionCaches.Store(nested); + + /* Once all nested are sorted then can be added to the + * sorted root and nested cache. */ + sortedRootAndNestedCache.Add(item); + sortedRootAndNestedCache.AddRange(sortedNestedCache); + + //Reset cache. + sortedNestedCache.Clear(); + } + + //Store temp caches. + CollectionCaches.Store(sortedRootCache); + CollectionCaches.Store(sortedNestedCache); + + return sortedRootAndNestedCache; + } + + /// + /// Removes a connection from observers without synchronizing changes. + /// + /// + private void RemoveFromObserversWithoutSynchronization(NetworkConnection connection) + { + List observerChangedObjectsCache = _observerChangedObjectsCache; + foreach (NetworkObject nob in Spawned.Values) + { + if (nob.RemoveObserver(connection)) + observerChangedObjectsCache.Add(nob); + } + + //Invoke despawn callbacks on nobs. + for (int i = 0; i < observerChangedObjectsCache.Count; i++) + observerChangedObjectsCache[i].InvokeOnServerDespawn(connection); + observerChangedObjectsCache.Clear(); + } + + /// + /// Rebuilds observers on all NetworkObjects for all connections. + /// + public void RebuildObservers(bool timedOnly = false) + { + List nobCache = RetrieveOrderedSpawnedObjects(); + List connCache = RetrieveAuthenticatedConnections(); + + RebuildObservers(nobCache, connCache, timedOnly); + + CollectionCaches.Store(nobCache); + CollectionCaches.Store(connCache); + } + + /// + /// Rebuilds observers for all connections for a NetworkObject. + /// + public void RebuildObservers(NetworkObject nob, bool timedOnly = false) + { + List nobCache = CollectionCaches.RetrieveList(nob); + List connCache = RetrieveAuthenticatedConnections(); + + RebuildObservers(nobCache, connCache, timedOnly); + + CollectionCaches.Store(nobCache); + CollectionCaches.Store(connCache); + } + + /// + /// Rebuilds observers on all NetworkObjects for a connection. + /// + public void RebuildObservers(NetworkConnection connection, bool timedOnly = false) + { + List nobCache = RetrieveOrderedSpawnedObjects(); + List connCache = CollectionCaches.RetrieveList(connection); + + RebuildObservers(nobCache, connCache, timedOnly); + + CollectionCaches.Store(nobCache); + CollectionCaches.Store(connCache); + } + + /// + /// Rebuilds observers on NetworkObjects. + /// + public void RebuildObservers(IList nobs, bool timedOnly = false) + { + List conns = RetrieveAuthenticatedConnections(); + + RebuildObservers(nobs, conns, timedOnly); + + CollectionCaches.Store(conns); + } + + /// + /// Rebuilds observers on all objects for connections. + /// + public void RebuildObservers(IList connections, bool timedOnly = false) + { + List nobCache = RetrieveOrderedSpawnedObjects(); + + RebuildObservers(nobCache, connections, timedOnly); + + CollectionCaches.Store(nobCache); + } + + /// + /// Rebuilds observers on NetworkObjects for connections. + /// + public void RebuildObservers(IList nobs, NetworkConnection conn, bool timedOnly = false) + { + List connCache = CollectionCaches.RetrieveList(conn); + + RebuildObservers(nobs, connCache, timedOnly); + + CollectionCaches.Store(connCache); + } + + /// + /// Rebuilds observers for connections on NetworkObject. + /// + public void RebuildObservers(NetworkObject networkObject, IList connections, bool timedOnly = false) + { + List nobCache = CollectionCaches.RetrieveList(networkObject); + + RebuildObservers(nobCache, connections, timedOnly); + + CollectionCaches.Store(nobCache); + } + + /// + /// Rebuilds observers on NetworkObjects for connections. + /// + public void RebuildObservers(IList nobs, IList conns, bool timedOnly = false) + { + List nobCache = CollectionCaches.RetrieveList(); + NetworkConnection nc; + + int connsCount = conns.Count; + for (int i = 0; i < connsCount; i++) + { + nobCache.Clear(); + + nc = conns[i]; + int nobsCount = nobs.Count; + for (int z = 0; z < nobsCount; z++) + RebuildObservers(nobs[z], nc, nobCache, timedOnly); + + //Send if change. + if (_writer.Length > 0) + { + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, _writer.GetArraySegment(), nc); + _writer.Clear(); + + foreach (NetworkObject n in nobCache) + n.OnSpawnServer(nc); + } + } + + CollectionCaches.Store(nobCache); + } + + /// + /// Rebuilds observers for a connection on NetworkObject. + /// + public void RebuildObservers(NetworkObject nob, NetworkConnection conn, bool timedOnly = false) + { + if (ApplicationState.IsQuitting()) + return; + _writer.Clear(); + + conn.UpdateHashGridPositions(!timedOnly); + //If observer state changed then write changes. + ObserverStateChange osc = nob.RebuildObservers(conn, timedOnly); + if (osc == ObserverStateChange.Added) + { + WriteSpawn(nob, _writer, conn); + } + else if (osc == ObserverStateChange.Removed) + { + nob.InvokeOnServerDespawn(conn); + WriteDespawn(nob, nob.GetDefaultDespawnType(), _writer); + } + else + { + return; + } + + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, _writer.GetArraySegment(), conn); + + /* If spawning then also invoke server + * start events, such as buffer last + * and onspawnserver. */ + if (osc == ObserverStateChange.Added) + nob.OnSpawnServer(conn); + + _writer.Clear(); + + /* If there is change then also rebuild recursive networkObjects. */ + foreach (NetworkBehaviour item in nob.RuntimeChildNetworkBehaviours) + RebuildObservers(item.NetworkObject, conn, timedOnly); + } + + /// + /// Rebuilds observers for a connection on NetworkObject. + /// + internal void RebuildObservers(NetworkObject nob, NetworkConnection conn, List addedNobs, bool timedOnly = false) + { + if (ApplicationState.IsQuitting()) + return; + + /* When not using a timed rebuild such as this connections must have + * hashgrid data rebuilt immediately. */ + conn.UpdateHashGridPositions(!timedOnly); + + //If observer state changed then write changes. + ObserverStateChange osc = nob.RebuildObservers(conn, timedOnly); + if (osc == ObserverStateChange.Added) + { + WriteSpawn(nob, _writer, conn); + addedNobs.Add(nob); + } + else if (osc == ObserverStateChange.Removed) + { + nob.InvokeOnServerDespawn(conn); + WriteDespawn(nob, nob.GetDefaultDespawnType(), _writer); + } + else + { + return; + } + + /* If there is change then also rebuild on any runtime children. + * This is to ensure runtime children have visibility updated + * in relation to parent. + * + * If here there is change. */ + foreach (NetworkBehaviour item in nob.RuntimeChildNetworkBehaviours) + RebuildObservers(item.NetworkObject, conn, addedNobs, timedOnly); + } + } +} \ 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..5392a53 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 02d93fa4a653dd64da0bb338b82f4740 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Observers.cs + uploadId: 762203 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..dbceaac --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs @@ -0,0 +1,42 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif + +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 ServerRpc. + /// + + internal void ParseServerRpc(PooledReader reader, NetworkConnection conn, Channel channel) + { +#if DEVELOPMENT + NetworkBehaviour.ReadDebugForValidatedRpc(base.NetworkManager, reader, out int startReaderRemaining, out string rpcInformation, out uint expectedReadAmount); +#endif + + NetworkBehaviour nb = reader.ReadNetworkBehaviour(); + int dataLength = Packets.GetPacketLength((ushort)PacketId.ServerRpc, reader, channel); + + if (nb != null) + nb.ReadServerRpc(fromRpcLink: false, methodHash: 0, reader, conn, channel); + else + SkipDataLength((ushort)PacketId.ServerRpc, reader, dataLength); + +#if DEVELOPMENT + NetworkBehaviour.TryPrintDebugForValidatedRpc(fromRpcLink: false, base.NetworkManager, reader, startReaderRemaining, rpcInformation, expectedReadAmount, channel); +#endif + } + } + +} \ 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..1e0b4f9 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3cb6cef520a4ff44bb8c4814e566c5ff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.Parsing.cs + uploadId: 762203 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..9da484a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs @@ -0,0 +1,1162 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Connection; +using FishNet.Managing.Object; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using GameKit.Dependencies.Utilities.Types; +using System; +using System.Collections.Generic; +using FishNet.Managing.Logging; +using FishNet.Object.Synchronizing; +using FishNet.Serializing.Helping; +using FishNet.Utility.Performance; +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(); + #endregion + + #region Private. + /// + /// Cached ObjectIds which may be used when exceeding available ObjectIds. + /// + private Queue _objectIdCache = new(); + + /// + /// Returns the ObjectId cache. + /// + /// + internal Queue GetObjectIdCache() => _objectIdCache; + + /// + /// NetworkBehaviours which have dirty SyncObjects. + /// + private List _dirtySyncTypeBehaviours = new(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 HashSet _pendingDestroy = new(); + /// + /// NetworkObjects in a recently loaded scene. + /// + private List<(int, List)> _loadedSceneNetworkObjects = new List<(int frame, List nobs)>(); + /// + /// Cache of spawning objects, used for recursively spawning nested NetworkObjects. + /// + private List _spawnCache = new(); + /// + /// 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.IsServerStarted) + { + _scenesLoading = false; + ClearSceneLoadedNetworkObjects(); + return; + } + + CleanRecentlyDespawned(); + + if (!_scenesLoading) + IterateLoadedScenes(false); + + Observers_OnUpdate(); + } + + /// + /// Clears NetworkObjects pending initialization from a recently loaded scene. + /// + private void ClearSceneLoadedNetworkObjects() + { + for (int i = 0; i < _loadedSceneNetworkObjects.Count; i++) + { + (int frame, List nobs) value = _loadedSceneNetworkObjects[i]; + CollectionCaches.Store(value.nobs); + } + + _loadedSceneNetworkObjects.Clear(); + } + + #region Checking dirty SyncTypes. + /// + /// Iterates NetworkBehaviours with dirty SyncTypes. + /// + internal void WriteDirtySyncTypes() + { + List collection = _dirtySyncTypeBehaviours; + int colStart = collection.Count; + if (colStart == 0) + return; + + /* Tells networkbehaviours to check their + * dirty synctypes. */ + for (int i = 0; i < collection.Count; i++) + { + bool dirtyCleared = collection[i].WriteDirtySyncTypes(SyncTypeWriteFlag.Unset); + if (dirtyCleared) + { + collection.RemoveAt(i); + i--; + } + } + } + + /// + /// Sets that a NetworkBehaviour has a dirty syncVars. + /// + internal void SetDirtySyncType(NetworkBehaviour nb) + { + _dirtySyncTypeBehaviours.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.IsOnlyOneServerStarted()) + { + BuildObjectIdCache(); + SetupSceneObjects(); + } + } + //Server in anything but started state. + else + { + //If no servers are started then reset. + if (!base.NetworkManager.ServerManager.IsAnyServerStarted()) + { + base.DespawnWithoutSynchronization(recursive: true, asServer: true); + base.SceneObjects_Internal.Clear(); + _objectIdCache.Clear(); + base.NetworkManager.ClearClientsCollection(base.NetworkManager.ServerManager.Clients); + } + //If at least one server is started then only clear for disconnecting server. + else + { + int transportIndex = args.TransportIndex; + //Remove connection from all NetworkObjects to ensure they are not stuck in observers. + foreach (NetworkConnection c in base.NetworkManager.ServerManager.Clients.Values) + { + if (c.TransportIndex == transportIndex) + RemoveFromObserversWithoutSynchronization(c); + } + + //Remove connections only for transportIndex. + base.NetworkManager.ClearClientsCollection(base.NetworkManager.ServerManager.Clients, transportIndex); + } + } + } + + /// + /// Called when a client disconnects. + /// + /// + internal void ClientDisconnected(NetworkConnection connection) + { + RemoveFromObserversWithoutSynchronization(connection); + + if (OnPreDestroyClientObjects != null) + OnPreDestroyClientObjects.Invoke(connection); + + /* A cache is made because the Objects + * collection would end up modified during + * iteration from removing ownership and despawning. */ + List nobs = CollectionCaches.RetrieveList(); + foreach (NetworkObject nob in connection.Objects) + nobs.Add(nob); + + int nobsCount = nobs.Count; + for (int i = 0; i < nobsCount; i++) + { + NetworkObject n = nobs[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 (!n.IsDeinitializing && !n.PreventDespawnOnDisconnect) + base.NetworkManager.ServerManager.Despawn(nobs[i]); + } + + CollectionCaches.Store(nobs); + } + #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(); + //Ignore ushort.maxvalue as that indicates null. + for (int i = 0; i < (ushort.MaxValue - 1); i++) + //for (int i = 0; i < (2200); i++) //QUICK-TEST Uncomment this, and comment the line above. + shuffledCache.Add(i); + /* Only shuffle when NOT in editor and not + * development build. + * Debugging could be easier when Ids are ordered. */ +#if !DEVELOPMENT + 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. + /// + 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) + { + if (!_objectIdCache.Contains(id)) + _objectIdCache.Enqueue(id); + else + NetworkManager.LogError($"Object Id [{id}] already exists within ObjectId Cache. Please report this error."); + } + + /// + /// Gets the next ObjectId to use for NetworkObjects. + /// + /// + protected internal override bool GetNextNetworkObjectId(out int nextNetworkObjectId) + { + if (!_objectIdCache.TryDequeue(out nextNetworkObjectId)) + { + nextNetworkObjectId = NetworkObject.UNSET_OBJECTID_VALUE; + base.NetworkManager.LogError($"No more available ObjectIds. How the heck did you manage to have {ushort.MaxValue} objects spawned at once?"); + } + + return (nextNetworkObjectId != NetworkObject.UNSET_OBJECTID_VALUE); + } + #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(bool asServer) + { + _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) + { + ClearSceneLoadedNetworkObjects(); + return; + } + + for (int i = 0; i < _loadedSceneNetworkObjects.Count; i++) + { + (int frame, List networkObjects) value = _loadedSceneNetworkObjects[i]; + if (ignoreFrameRestriction || (Time.frameCount > value.frame)) + { + SetupSceneObjects(value.networkObjects); + CollectionCaches.Store(value.networkObjects); + _loadedSceneNetworkObjects.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; + + List sceneNobs = CollectionCaches.RetrieveList(); + Scenes.GetSceneNetworkObjects(s, firstOnly: false, errorOnDuplicates: true, ignoreUnsetSceneIds: true, ref sceneNobs); + _loadedSceneNetworkObjects.Add((Time.frameCount, sceneNobs)); + + InitializeRootNetworkObjects(sceneNobs); + } + + /// + /// Sets initial values for NetworkObjects. + /// + /// + private void InitializeRootNetworkObjects(List nobs) + { + /* First update the nested status on all nobs, as well + * set them as not initialized. This is done as some scene objets might be prefabs + * that were changed in scene but have not had the prefab settings updated to those + * changes. */ + foreach (NetworkObject nob in nobs) + { + nob.SetIsNestedThroughTraversal(); + nob.UnsetInitializedValuesSet(); + } + + //Initialize sceneNobs cache, but do not invoke callbacks till next frame. + foreach (NetworkObject nob in nobs) + { + if (nob.IsSceneObject && !nob.IsNested) + nob.SetInitializedValues(parentNob: null, force: false); + } + } + + /// + /// Setup all NetworkObjects in scenes. Should only be called when server is active. + /// + protected internal void SetupSceneObjects() + { + Scene ddolScene = DDOL.GetDDOL().gameObject.scene; + bool ddolLoaded = ddolScene.isLoaded; + + /* Becomes false if setup in GetSceenAt. + * This is a safety check for if Unity ever changes + * the behavior where DDOL scenes appear in the sceneCount. */ + bool trySetupDdol = true; + + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene s = SceneManager.GetSceneAt(i); + if (ddolLoaded && s.handle == ddolScene.handle) + trySetupDdol = false; + + SetupSceneObjects(s); + } + + if (trySetupDdol) + SetupSceneObjects(ddolScene); + } + + /// + /// Setup NetworkObjects in a scene. Should only be called when server is active. + /// + /// + private void SetupSceneObjects(Scene s) + { + if (!s.IsValid()) + return; + + List sceneNobs = CollectionCaches.RetrieveList(); + Scenes.GetSceneNetworkObjects(s, firstOnly: false, errorOnDuplicates: true, ignoreUnsetSceneIds: true, ref sceneNobs); + + SetupSceneObjects(sceneNobs); + + CollectionCaches.Store(sceneNobs); + } + + /// + /// Setup NetworkObjects in a scene. Should only be called when server is active. + /// + /// + private void SetupSceneObjects(List sceneNobs) + { + InitializeRootNetworkObjects(sceneNobs); + + List cache = SortRootAndNestedByInitializeOrder(sceneNobs); + + bool isHost = base.NetworkManager.IsHostStarted; + int nobsCount = cache.Count; + for (int i = 0; i < nobsCount; i++) + { + NetworkObject nob = cache[i]; + //Only setup if a scene object and not initialzied. + if (nob.GetIsNetworked() && nob.IsSceneObject && nob.IsDeinitializing) + { + if (!nob.WasActiveDuringEdit_Set1) + { + NetworkManager.LogError($"NetworkObject {nob.name} in scene {nob.gameObject.scene.name} needs to be reserialized. Please use the Fish-Networking menu -> Utility -> Reserialize NetworkObjects."); + continue; + } + + 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.WasActiveDuringEdit || 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); + } + } + } + + CollectionCaches.Store(cache); + } + + /// + /// Performs setup on a NetworkObject without synchronizing the actions to clients. + /// + /// Override ObjectId to use. + private bool SetupWithoutSynchronization(NetworkObject nob, NetworkConnection ownerConnection = null, int? objectId = null, bool initializeEarly = true) + { + if (nob.GetIsNetworked()) + { + int objectIdValue; + + if (objectId != null) + { + objectIdValue = objectId.Value; + } + else + { + if (!GetNextNetworkObjectId(out objectIdValue)) + return false; + } + + if (initializeEarly) + nob.InitializeEarly(NetworkManager, objectIdValue, ownerConnection, true); + + base.AddToSpawned(nob, true); + nob.gameObject.SetActive(true); + nob.Initialize(true, true); + + return true; + } + + return false; + } + #endregion + + #region Spawning. + /// + /// Spawns an object over the network. + /// + internal void Spawn(NetworkObject networkObject, NetworkConnection ownerConnection = null, UnityEngine.SceneManagement.Scene scene = default) + { + //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.ServerManager.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, false)) + return; + + //Since server is not started then run TrySpawn for client, given this is a client trying to predicted spawn. + if (!networkObject.PredictedSpawn.OnTrySpawnClient()) + 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; + } + + NetworkBehaviour networkBehaviourParent = networkObject.CurrentParentNetworkBehaviour; + if (networkBehaviourParent != null && !networkBehaviourParent.IsSpawned) + { + base.NetworkManager.LogError($"{networkObject.name} cannot be spawned because it has a parent NetworkObject {networkBehaviourParent} which is not spawned."); + return; + } + + /* If scene is specified make sure the object is root, + * and if not move it before network spawning. */ + if (scene.IsValid()) + { + if (networkObject.transform.parent != null) + { + base.NetworkManager.LogError($"{networkObject.name} cannot be moved to scene name {scene.name}, handle {scene.handle} because {networkObject.name} is not root and only root objects may be moved."); + return; + } + else + { + UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(networkObject.gameObject, scene); + } + } + + if (predictedSpawn) + base.NetworkManager.ClientManager.Objects.PredictedSpawn(networkObject, ownerConnection); + else + SpawnWithoutChecks(networkObject, recursiveSpawnCache: null, ownerConnection); + } + + /// + /// Spawns networkObject without any checks. + /// + private void SpawnWithoutChecks(NetworkObject networkObject, List recursiveSpawnCache = null, NetworkConnection ownerConnection = null, int? objectId = null, bool rebuildObservers = true, bool initializeEarly = true, bool isRecursiveIteration = false) + { + /* Setup locally without sending to clients. + * When observers are built for the network object + * during initialization spawn messages will + * be sent. */ + networkObject.SetIsNetworked(true); + + /* Grab the nested before spawning the networkObject. This prevents double initialization + * if one of the OnStart callbacks adds nested to networkObject. + * + * EG: called on NetworkObject during 'SetupWithoutSynchronization' + * private void OnStartNetwork() + * { + * NetworkObject n = Instantiate(xyz); + * n.SetParent(this); //this being NetworkObject + * base.Spawn(n); + * } + * + * If nested was fetched after SetupWithout Synchronize just below then it would + * include the newly nested object, and try to initialize it twice. + */ + List nestedNetworkObjects = (isRecursiveIteration) ? null : networkObject.GetNetworkObjects(GetNetworkObjectOption.AllNestedRecursive); + + if (SetupWithoutSynchronization(networkObject, ownerConnection, objectId, initializeEarly)) + _spawnCache.Add(networkObject); + + if (nestedNetworkObjects != null) + { + foreach (NetworkObject item in nestedNetworkObjects) + { + /* Only spawn recursively if the nob state is unset. + * Unset indicates that the nob has not been manually spawned or despawned. */ + if (item.gameObject.activeInHierarchy || item.State == NetworkObjectState.Spawned) + SpawnWithoutChecks(item, recursiveSpawnCache: null, ownerConnection, isRecursiveIteration: true); + } + } + + /* 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. */ + bool recursiveCacheWasNull = (recursiveSpawnCache == null); + if (recursiveCacheWasNull) + recursiveSpawnCache = CollectionCaches.RetrieveList(); + recursiveSpawnCache.AddRange(_spawnCache); + _spawnCache.Clear(); + + //Also rebuild observers for the object so it spawns for others. + if (rebuildObservers) + RebuildObservers(recursiveSpawnCache); + + int spawnCacheCopyCount = recursiveSpawnCache.Count; + /* 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.IsClientStarted) + { + int count = spawnCacheCopyCount; + NetworkConnection localConnection = NetworkManager.ClientManager.Connection; + for (int i = 0; i < count; i++) + { + NetworkObject nob = recursiveSpawnCache[i]; + nob.SetRenderersVisible(nob.Observers.Contains(localConnection)); + } + } + + CollectionCaches.StoreAndDefault(ref nestedNetworkObjects); + + /* If collection was null then store the one retrieved. + * Otherwise, let the calling method handle the provided + * cache. */ + if (recursiveCacheWasNull) + CollectionCaches.Store(recursiveSpawnCache); + } + + /// + /// Reads a predicted spawn. + /// + internal void ReadSpawn(PooledReader reader, NetworkConnection conn) + { + ushort spawnLength = reader.ReadUInt16Unpacked(); + + int readStartPosition = reader.Position; + + SpawnType st = (SpawnType)reader.ReadUInt8Unpacked(); + bool sceneObject = st.FastContains(SpawnType.Scene); + bool isGlobal = st.FastContains(SpawnType.InstantiatedGlobal); + + ReadNestedSpawnIds(reader, st, out byte? nobComponentId, out int? parentObjectId, out byte? parentComponentId, readSpawningObjects: null); + + int objectId = reader.ReadNetworkObjectForSpawn(out _, out ushort collectionId); + + //No predicted spawn ids left. Should not be possible as client would have stopped this locally. + if (conn.PredictedObjectIds.Count == 0 || !conn.PredictedObjectIds.TryDequeue(out int serverPredictedObjectId)) + { + reader.Clear(); + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {conn.ClientId} used predicting spawning without any Ids in queue."); + return; + } + + //Ids should match. Client cannot send predicted spawn Ids out of order, so they should always match the server in order. + if (objectId != serverPredictedObjectId) + { + reader.Clear(); + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"Connection {conn.ClientId} used predicted Id of {objectId} while the server Id is {serverPredictedObjectId}."); + return; + } + + NetworkObject nob = null; + NetworkConnection owner = null; + + /* See if the parent exists. If not, then do not + * continue and send failed response to client. */ + if (parentObjectId.HasValue && !Spawned.TryGetValueIL2CPP(parentObjectId.Value, out NetworkObject _)) + { + NetworkManager.Log($"Predicted spawn failed due to the NetworkObject's parent not being found. Scene object: {sceneObject}, ObjectId {objectId}, CollectionId {collectionId}."); + SendFailedResponse(objectId); + return; + } + owner = reader.ReadNetworkConnection(); + + //Read transform values which differ from serialized values. + base.ReadTransformProperties(reader, out Vector3? nullablePosition, out Quaternion? nullableRotation, out Vector3? nullableScale); + + int prefabId; + ulong sceneId = 0; + string sceneName = string.Empty; + string objectName = string.Empty; + + if (sceneObject) + { + base.ReadSceneObjectId(reader, out sceneId); +#if DEVELOPMENT + if (NetworkManager.ClientManager.IsServerDevelopment) + base.CheckReadSceneObjectDetails(reader, ref sceneName, ref objectName); +#endif + nob = base.GetSceneNetworkObject(sceneId, sceneName, objectName); + } + else + { + prefabId = reader.ReadNetworkObjectId(); + ObjectPoolRetrieveOption retrieveOptions = (ObjectPoolRetrieveOption.MakeActive | ObjectPoolRetrieveOption.LocalSpace); + nob = NetworkManager.GetPooledInstantiated(prefabId, collectionId, retrieveOptions, null, nullablePosition, nullableRotation, nullableScale, false); + } + + /* NetworkObject could not be found. User could be sending an invalid Id, + * or perhaps it was a scene object and the scene had unloaded prior to getting + * this spawn message. */ + if (nob == null) + { + NetworkManager.Log($"Predicted spawn failed due to the NetworkObject not being found. Scene object: {sceneObject}, ObjectId {objectId}, CollectionId {collectionId}."); + SendFailedResponse(objectId); + return; + } + + if (!nob.WasActiveDuringEdit_Set1) + { + string sceneOrPrefabText = (sceneObject) ? $"in scene {nob.gameObject.scene.name}" : "prefab"; + NetworkManager.LogError($"NetworkObject {nob.name} {sceneOrPrefabText}. Please use the Fish-Networking menu -> Utility -> Reserialize NetworkObjects."); + } + + + /* Update sceneObject position. + * There is no need to do this on instantiate since the position is set + * during the instantiation. */ + if (sceneObject) + { + nob.transform.SetLocalPositionRotationAndScale(nullablePosition, nullableRotation, nullableScale); + } + + //Check if nob allows predicted spawning. + if (!base.CanPredictedSpawn(nob, conn, true, reader)) + return; + + nob.SetIsGlobal(isGlobal); + nob.SetIsNetworked(true); + nob.InitializeEarly(NetworkManager, objectId, owner, true); + //Initialize for prediction. + nob.InitializePredictedObject_Server(conn); + + base.ReadPayload(conn, nob, reader); + base.ReadRpcLinks(reader); + base.ReadSyncTypesForSpawn(reader); + + //Check user implementation of trySpawn. + if (!nob.PredictedSpawn.OnTrySpawnServer(conn, owner)) + { + //Inform client of failure. + SendFailedResponse(objectId); + return; + } + + //Once here everything is good. + + //Get connections to send spawn to. + List conns = RetrieveAuthenticatedConnections(); + + SendSuccessResponse(objectId); + //Store caches used. + CollectionCaches.Store(conns); + + //Sends a failed response. + void SendFailedResponse(int lObjectId) + { + SkipRemainingSpawnLength(); + if (nob != null) + { + //TODO support pooling. This first requires a rework of the initialization / clientHost message system. + UnityEngine.Object.Destroy(nob.gameObject); + //base.NetworkManager.StorePooledInstantiated(nob, true); + } + + PooledWriter writer = WriteResponseHeader(success: false, lObjectId); + + conn.SendToClient((byte)Channel.Reliable, writer.GetArraySegment()); + WriterPool.Store(writer); + } + + //Sends a success spawn result and returns nobs recursively spawned, including original. + void SendSuccessResponse(int lObjectId) + { + PooledWriter writer = WriteResponseHeader(success: true, lObjectId); + + SpawnWithoutChecks(nob, recursiveSpawnCache: null, owner, lObjectId, rebuildObservers: true, initializeEarly: false); + conn.SendToClient((byte)Channel.Reliable, writer.GetArraySegment()); + + WriterPool.Store(writer); + } + + //Writes response header and returns writer used. + PooledWriter WriteResponseHeader(bool success, int lObjectId) + { + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(PacketId.PredictedSpawnResult); + writer.WriteBoolean(success); + + //Id of object which was predicted spawned. + writer.WriteNetworkObjectId(lObjectId); + + //Write the next Id even if not succesful. + int nextId; + if (NetworkManager.ServerManager.Objects.GetObjectIdCache().TryDequeue(out nextId)) + conn.PredictedObjectIds.Enqueue(nextId); + else + nextId = NetworkObject.UNSET_OBJECTID_VALUE; + + //Write nextId even if invalid. Client will not add invalid Ids. + writer.WriteNetworkObjectId(nextId); + + return writer; + } + + //Skips remaining data for the spawn. + void SkipRemainingSpawnLength() + { + /* Simply setting the position to readStart + spawnLength works + * too but when possible use supplied API. */ + int skipLength = spawnLength - (reader.Position - readStartPosition); + reader.Skip(skipLength); + } + } + #endregion + + #region Despawning. + /// + /// Cleans recently despawned objects. + /// + private void CleanRecentlyDespawned() + { + //Only iterate if frame ticked to save perf. + if (!base.NetworkManager.TimeManager.FrameTicked) + return; + + List intCache = CollectionCaches.RetrieveList(); + + 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.Add(kvp.Key); + + count++; + if (count == iterations) + break; + } + + //Remove cached entries. + int cCount = intCache.Count; + for (int i = 0; i < cCount; i++) + RecentlyDespawnedIds.Remove(intCache[i]); + + CollectionCaches.Store(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.Add(nob); + } + + /// + /// Tries to removes objectId from PendingDestroy and returns if successful. + /// + internal bool RemoveFromPending(NetworkObject nob) + { + return _pendingDestroy.Remove(nob); + } + + /// + /// Returns a NetworkObject in PendingDestroy. + /// + internal NetworkObject GetFromPending(int objectId) + { + /* Becomes true if there was a null entry and pending must be rebuilt. + * This would be very uncommon */ + bool rebuildPending = false; + + foreach (NetworkObject n in _pendingDestroy) + { + if (n == null) + { + rebuildPending = true; + continue; + } + + if (n.ObjectId == objectId) + return n; + } + + if (rebuildPending) + { + HashSet newPending = CollectionCaches.RetrieveHashSet(); + foreach (NetworkObject n in _pendingDestroy) + { + if (n != null) + newPending.Add(n); + } + + CollectionCaches.Store(_pendingDestroy); + _pendingDestroy = newPending; + } + + //Fall through, nothing found. + return null; + } + + /// + /// Destroys NetworkObjects pending for destruction. + /// + internal void DestroyPending() + { + foreach (NetworkObject item in _pendingDestroy) + { + if (item != null) + UnityEngine.Object.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.ServerManager.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 NetworkObjectDestroyed(NetworkObject nob, bool asServer) + { + //Only finalize despawn if not already deinitialized. + if (!nob.IsDeinitializing) + FinalizeDespawn(nob, DespawnType.Destroy); + + base.NetworkObjectDestroyed(nob, asServer); + } + + /// + /// Finalizes the despawn process. By the time this is called the object is considered unaccessible. + /// + /// + private void FinalizeDespawn(NetworkObject nob, DespawnType despawnType) + { + List dirtiedNbs = _dirtySyncTypeBehaviours; + if (nob != null && nob.ObjectId != NetworkObject.UNSET_OBJECTID_VALUE) + { + // Write out any pending sync types and be sure to clear from the dirty list + // to avoid trying to write out a despawned object later on. + for (int i = 0, count = nob.NetworkBehaviours.Count; i < count; ++i) + { + NetworkBehaviour nb = nob.NetworkBehaviours[i]; + if (nb.SyncTypeDirty && nb.WriteDirtySyncTypes((SyncTypeWriteFlag.ForceReliable | SyncTypeWriteFlag.IgnoreInterval))) + dirtiedNbs.Remove(nb); + } + + WriteDespawnAndSend(nob, despawnType); + + CacheObjectId(nob); + } + } + + /// + /// Writes a despawn and sends it to clients. + /// + private void WriteDespawnAndSend(NetworkObject nob, DespawnType despawnType) + { + HashSet observers = nob.Observers; + if (observers.Count == 0) + return; + + PooledWriter everyoneWriter = WriterPool.Retrieve(); + WriteDespawn(nob, despawnType, everyoneWriter); + + ArraySegment despawnSegment = everyoneWriter.GetArraySegment(); + + //Add observers to a list cache. + List cache = CollectionCaches.RetrieveList(); + /* Must be added into a new collection because the + * user might try and modify the observers in the despawn, which + * would cause a collection modified error. */ + cache.AddRange(observers); + int cacheCount = cache.Count; + + for (int i = 0; i < cacheCount; i++) + { + //Invoke ondespawn and send despawn. + NetworkConnection conn = cache[i]; + nob.InvokeOnServerDespawn(conn); + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, despawnSegment, conn); + //Remove from observers. + //nob.Observers.Remove(conn); + } + + everyoneWriter.Store(); + CollectionCaches.Store(cache); + } + + /// + /// Reads a predicted despawn. + /// + internal void ReadDespawn(Reader reader, NetworkConnection conn) + { + NetworkObject nob = reader.ReadNetworkObject(); + + //Maybe server destroyed the object so don't kick if null. + if (nob == null) + return; + if (nob.IsDeinitializing) + return; + + //Various predicted despawn checks. + if (!base.CanPredictedDespawn(nob, conn, true)) + return; + + //Despawn object. + nob.Despawn(); + } + #endregion + } +} \ No newline at end of file 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..882e597 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d5e7f3005cbc7924f99819311c58651a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/Object/ServerObjects.cs + uploadId: 762203 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..024d9f3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs @@ -0,0 +1,352 @@ +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 GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Managing.Server +{ + public sealed partial class ServerManager : MonoBehaviour + { + #region Private. + /// + /// Handler for registered broadcasts. + /// + private readonly Dictionary _broadcastHandlers = new(); + /// + /// Connections which can be broadcasted to after having excluded removed. + /// + private HashSet _connectionsWithoutExclusionsCache = new(); + #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 + { + if (handler == null) + { + NetworkManager.LogError($"Broadcast cannot be registered because handler is null. This may occur when trying to register to objects which require initialization, such as events."); + return; + } + + ushort key = BroadcastExtensions.GetKey(); + //Create new IBroadcastHandler if needed. + BroadcastHandlerBase bhs; + if (!_broadcastHandlers.TryGetValueIL2CPP(key, out bhs)) + { + bhs = new ClientBroadcastHandler(requireAuthentication); + _broadcastHandlers.Add(key, bhs); + } + //Register handler to IBroadcastHandler. + bhs.RegisterHandler(handler); + } + + /// + /// 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 = BroadcastExtensions.GetKey(); + if (_broadcastHandlers.TryGetValueIL2CPP(key, out BroadcastHandlerBase bhs)) + bhs.UnregisterHandler(handler); + } + + + /// + /// Parses a received broadcast. + /// + + 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 BroadcastHandlerBase bhs)) + { + if (bhs.RequireAuthentication && !conn.IsAuthenticated) + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent a broadcast which requires authentication, but client was not authenticated. Client has been disconnected."); + else + bhs.InvokeHandlers(conn, reader, channel); + } + 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.IsAuthenticated) + { + NetworkManager.LogWarning($"Cannot send broadcast to client because they are not authenticated."); + return; + } + + PooledWriter writer = WriterPool.Retrieve(); + BroadcastsSerializers.WriteBroadcast(NetworkManager, writer, message, ref channel); + ArraySegment segment = writer.GetArraySegment(); + NetworkManager.TransportManager.SendToClient((byte)channel, segment, connection); + writer.Store(); + } + + + /// + /// 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; + PooledWriter writer = WriterPool.Retrieve(); + BroadcastsSerializers.WriteBroadcast(NetworkManager, writer, message, ref channel); + ArraySegment segment = writer.GetArraySegment(); + + foreach (NetworkConnection conn in connections) + { + if (requireAuthenticated && !conn.IsAuthenticated) + failedAuthentication = true; + else + NetworkManager.TransportManager.SendToClient((byte)channel, segment, conn); + } + writer.Store(); + + 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; + } + + _connectionsWithoutExclusionsCache.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) + _connectionsWithoutExclusionsCache.Add(c); + //Remove + _connectionsWithoutExclusionsCache.Remove(excludedConnection); + + Broadcast(_connectionsWithoutExclusionsCache, 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; + } + + _connectionsWithoutExclusionsCache.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) + _connectionsWithoutExclusionsCache.Add(c); + //Remove + foreach (NetworkConnection c in excludedConnections) + _connectionsWithoutExclusionsCache.Remove(c); + + Broadcast(_connectionsWithoutExclusionsCache, 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. + + 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; + PooledWriter writer = WriterPool.Retrieve(); + BroadcastsSerializers.WriteBroadcast(NetworkManager, writer, message, ref channel); + ArraySegment segment = writer.GetArraySegment(); + + foreach (NetworkConnection conn in Clients.Values) + { + // + if (requireAuthenticated && !conn.IsAuthenticated) + failedAuthentication = true; + else + NetworkManager.TransportManager.SendToClient((byte)channel, segment, conn); + } + writer.Store(); + + 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..b86bc32 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1cd9dcd58556e27449ce5cb0d70611cb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/ServerManager.Broadcast.cs + uploadId: 762203 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..0f6d7e4 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs @@ -0,0 +1,224 @@ +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.Collections.Generic; +using System.Runtime.CompilerServices; +using GameKit.Dependencies.Utilities; +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 + + /// + /// Stores a cache and returns a boolean result. + /// + /// + private bool StoreTransportCacheAndReturn(List cache, bool returnedValue) + { + CollectionCaches.Store(cache); + return returnedValue; + } + + /// + /// Returns true if all server sockets have a local connection state of stopped. + /// + public bool AreAllServersStopped() + { + List transports = NetworkManager.TransportManager.GetAllTransports(includeMultipass: false); + + foreach (Transport t in transports) + { + if (t.GetConnectionState(server: true) != LocalConnectionState.Stopped) + return StoreTransportCacheAndReturn(transports, returnedValue: false); + } + + return StoreTransportCacheAndReturn(transports, returnedValue: true); + } + + /// + /// Returns true if only one server is started. + /// + /// + public bool IsOnlyOneServerStarted() + { + List transports = NetworkManager.TransportManager.GetAllTransports(includeMultipass: false); + + int startedCount = 0; + + foreach (Transport t in transports) + { + if (t.GetConnectionState(true) == LocalConnectionState.Started) + startedCount++; + } + + return StoreTransportCacheAndReturn(transports, (startedCount == 1)); + } + + [Obsolete("Use IsOnlyOneServerStarted().")] + public bool OneServerStarted() => IsOnlyOneServerStarted(); + + + /// + /// Returns true if any server socket is in the started state. + /// + /// When set the transport will be ignored. This value is only used with Multipass. + public bool IsAnyServerStarted(Transport excludedTransport) + { + List transports = NetworkManager.TransportManager.GetAllTransports(includeMultipass: false); + + foreach (Transport t in transports) + { + if (t == excludedTransport) + continue; + //Another transport is started, no need to load start scenes again. + if (t.GetConnectionState(true) == LocalConnectionState.Started) + return StoreTransportCacheAndReturn(transports, returnedValue: true); + } + + return StoreTransportCacheAndReturn(transports, returnedValue: false); + } + + /// + /// 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 IsAnyServerStarted(int excludedIndex = TransportConsts.UNSET_TRANSPORT_INDEX) + { + Transport excludedTransport = null; + if (excludedIndex != TransportConsts.UNSET_TRANSPORT_INDEX) + { + if (NetworkManager.TransportManager.Transport is Multipass mp) + excludedTransport = mp.GetTransport(excludedIndex); + } + + return IsAnyServerStarted(excludedTransport); + } + + [Obsolete("Use IsAnyServerStarted.")] + public bool AnyServerStarted(int excludedIndex = TransportConsts.UNSET_TRANSPORT_INDEX) => IsAnyServerStarted(excludedIndex); + + /// + /// 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, UnityEngine.SceneManagement.Scene scene = default) + { + if (go == null) + { + NetworkManager.LogWarning($"GameObject cannot be spawned because it is null."); + return; + } + + NetworkObject nob = go.GetComponent(); + Spawn(nob, ownerConnection, scene); + } + + /// + /// 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, UnityEngine.SceneManagement.Scene scene = default) + { + if (!nob.GetIsSpawnable()) + { + NetworkManager.LogWarning($"NetworkObject {nob} cannot be spawned because it is not marked as spawnable."); + return; + } + Objects.Spawn(nob, ownerConnection, scene); + } + + /// + /// 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. + 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.HasValue) ? networkObject.GetDefaultDespawnType() : despawnType.Value; + + Objects.Despawn(networkObject, resolvedDespawnType, asServer: 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); + } + } +} \ No newline at end of file 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..90913e2 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f2a07d9984be21648bc714ea03bd0d70 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/ServerManager.QOL.cs + uploadId: 762203 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..ccdcf48 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs @@ -0,0 +1,71 @@ +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(); + /// + /// RPCLink indexes which can be used. + /// + private Queue _availableRpcLinkIndexes = new(); + #endregion + + /// + /// Initializes RPC Links for NetworkBehaviours. + /// + private void InitializeRpcLinks() + { + ushort startingLink = NetworkManager.StartingRpcLinkIndex; + for (ushort i = ushort.MaxValue; i >= startingLink; 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.LinkPacketId); + } + } + +} \ 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..121f40d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b9f0a4620d06f5c41b01f20af3f90634 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/ServerManager.RpcLinks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs new file mode 100644 index 0000000..87052b3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs @@ -0,0 +1,968 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +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.Timing; +using FishNet.Managing.Transporting; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility.Extension; +using FishNet.Utility.Performance; +using GameKit.Dependencies.Utilities; +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(); + /// + /// Clients dictionary as a list, containing only values. + /// + private List _clientsList = new(); + /// + /// NetworkManager for server. + /// + [HideInInspector] + public NetworkManager NetworkManager { get; private set; } + #endregion + + #region Serialized. + /// + /// 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; + /// + /// What platforms to enable remote client timeout. + /// + [Tooltip("What platforms to enable remote client timeout.")] + [SerializeField] + private RemoteTimeoutType _remoteClientTimeout = RemoteTimeoutType.Development; + /// + /// How long in seconds client must go without sending any packets before getting disconnected. This is independent of any transport settings. + /// + [Tooltip("How long in seconds a client must go without sending any packets before getting disconnected. This is independent of any transport settings.")] + [Range(1, MAXIMUM_REMOTE_CLIENT_TIMEOUT_DURATION)] + [SerializeField] + private ushort _remoteClientTimeoutDuration = 60; + + /// + /// Sets timeout settings. Can be used at runtime. + /// + /// + public void SetRemoteClientTimeout(RemoteTimeoutType timeoutType, ushort duration) + { + _remoteClientTimeout = timeoutType; + duration = (ushort)Mathf.Clamp(duration, 1, MAXIMUM_REMOTE_CLIENT_TIMEOUT_DURATION); + _remoteClientTimeoutDuration = duration; + } + + /// + /// True to allow clients to use predicted spawning. While true, each NetworkObject you wish this feature to apply towards must have a PredictedSpawn component. + /// Predicted spawns can have custom validation on the server. + /// + internal bool GetAllowPredictedSpawning() => _allowPredictedSpawning; + + [Tooltip("True to allow clients to use predicted spawning. While true, each NetworkObject you wish this feature to apply towards must have a PredictedSpawn component. Predicted spawns can have custom validation on the server.")] + [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, MAXIMUM_RESERVED_OBJECT_IDS)] + [SerializeField] + private ushort _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 ushort GetReservedObjectIds() => _reservedObjectIds; + + /// + /// Default send rate for SyncTypes. A value of 0f will send changed values every tick. + /// + /// + 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() + { + 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; + + /// Sets the maximum frame rate the client may run at. Calling this method will enable ChangeFrameRate. + /// + /// New value. + public void SetFrameRate(ushort value) + { + _frameRate = (ushort)Mathf.Clamp(value, 0, NetworkManager.MAXIMUM_FRAMERATE); + _changeFrameRate = true; + if (NetworkManager != null) + NetworkManager.UpdateFramerate(); + } + + /// + /// True to share the Ids of clients and the objects they own with other clients. No sensitive information is shared. + /// + public 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; + #endregion + + #region Private. + /// + /// The last index checked to see if a client has not sent a packet in awhile. + /// + private int _nextClientTimeoutCheckIndex; + /// + /// Next time a timeout check can be performed. + /// + private float _nextTimeoutCheckTime; + /// + /// Used to read splits. + /// + private SplitReader _splitReader = new(); +#if DEVELOPMENT + /// + /// Logs data about parser to help debug. + /// + private PacketIdHistory _packetIdHistory = new(); +#endif + #endregion + + #region Const. + /// + /// Maximum value the remote client timeout can be set to. + /// + public const ushort MAXIMUM_REMOTE_CLIENT_TIMEOUT_DURATION = 1500; + /// + /// Maximum number of reserved object Ids allowed for predicted spawning. + /// + private const int MAXIMUM_RESERVED_OBJECT_IDS = 100; //QUICK-TEST Increase this to 5000, save, within ServerManager set maximum reserved Ids to max. + #endregion + + private void OnDestroy() + { + Objects?.SubscribeToSceneLoaded(false); + } + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + NetworkManager = manager; + Objects = new(manager); + Objects.SubscribeToSceneLoaded(true); + InitializeRpcLinks(); + //Unsubscribe first incase already subscribed. + SubscribeToTransport(false); + SubscribeToTransport(true); + NetworkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + NetworkManager.SceneManager.OnClientLoadedStartScenes += SceneManager_OnClientLoadedStartScenes; + NetworkManager.TimeManager.OnPostTick += TimeManager_OnPostTick; + + if (_authenticator == null) + _authenticator = GetComponent(); + if (_authenticator != null) + InitializeAuthenticator(); + } + + /// + /// 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. + /// + /// + public void SendDisconnectMessages(int[] connectionIds) + { + List conns = new(); + 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 optionally immediately iterates outgoing. + /// + public void SendDisconnectMessages(List conns, bool iterate) + { + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(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.Store(); + + if (iterate) + NetworkManager.TransportManager.IterateOutgoing(asServer: 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); + } + + /// + /// Checks to timeout client connections. + /// + private void CheckClientTimeout() + { + if (_remoteClientTimeout == RemoteTimeoutType.Disabled) + return; +#if DEVELOPMENT + //If development but not set to development return. + if (_remoteClientTimeout != RemoteTimeoutType.Development) + return; +#endif + //Wait two timing intervals to give packets a chance to come through. + if (NetworkManager.SceneManager.IsIteratingQueue(2f)) + return; + + float unscaledTime = Time.unscaledTime; + if (unscaledTime < _nextTimeoutCheckTime) + return; + //Check for timeouts every 200ms. + const float TIMEOUT_CHECK_FREQUENCY = 0.2f; + _nextTimeoutCheckTime = (unscaledTime + TIMEOUT_CHECK_FREQUENCY); + //No clients. + int clientsCount = Clients.Count; + if (clientsCount == 0) + return; + + /* If here can do checks. */ + //If to reset index. + if (_nextClientTimeoutCheckIndex >= clientsCount) + _nextClientTimeoutCheckIndex = 0; + + //Number of ticks passed for client to be timed out. + uint requiredTicks = NetworkManager.TimeManager.TimeToTicks((double)_remoteClientTimeoutDuration, TickRounding.RoundUp); + + const float FULL_CHECK_TIME = 2f; + /* Number of times this is expected to run every 2 seconds. + * Iterations will try to complete the entire client collection + * over these 2 seconds. */ + int checkCount = Mathf.CeilToInt(FULL_CHECK_TIME / TIMEOUT_CHECK_FREQUENCY); + int targetIterations = Mathf.Max(clientsCount / checkCount, 1); + + uint localTick = NetworkManager.TimeManager.LocalTick; + for (int i = 0; i < targetIterations; i++) + { + if (_nextClientTimeoutCheckIndex >= _clientsList.Count) + _nextClientTimeoutCheckIndex = 0; + + NetworkConnection item = _clientsList[_nextClientTimeoutCheckIndex]; + uint clientLocalTick = item.PacketTick.LocalTick; + /* If client tick has not been set yet then use the tick + * when they connected to the server. */ + if (clientLocalTick == 0) + clientLocalTick = item.ServerConnectionTick; + + uint difference = (localTick - clientLocalTick); + //Client has timed out. + if (difference >= requiredTicks) + item.Kick(KickReason.UnexpectedProblem, LoggingType.Common, $"{item.ToString()} has timed out. You can modify this feature on the ServerManager component."); + + _nextClientTimeoutCheckIndex++; + } + } + + /// + /// Called when the TimeManager calls OnPostTick. + /// + private void TimeManager_OnPostTick() + { + CheckClientTimeout(); + } + + /// + /// 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 = IsAnyServerStarted(); + NetworkManager.ClientManager.Objects.OnServerConnectionState(args); + //If no servers are started then reset data. + if (!Started) + { + MatchCondition.StoreCollections(NetworkManager); + //Despawn without synchronizing network objects. + Objects.DespawnWithoutSynchronization(recursive: true, asServer: true); + //Clear all clients. + Clients.Clear(); + //Clients as list. + _clientsList.Clear(); + } + 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; + string socketInformation = string.Empty; + if (state == LocalConnectionState.Starting) + socketInformation = $" Listening on port {t.GetPort()}."; + NetworkManagerExtensions.Log($"Local server is {state.ToString().ToLower()} for {tName}.{socketInformation}"); + } + + NetworkManager.UpdateFramerate(); + OnServerConnectionState?.Invoke(args); + } + + /// + /// Checks to make sure the client is on the same version. + /// This is to help developers make sure their builds are on the same FishNet version. + /// + private void ParseVersion(PooledReader reader, NetworkConnection conn, int transportId) + { + //Cannot be authenticated if havent sent version yet. This is a duplicate version send, likely exploit attempt. + if (conn.HasSentVersion) + { + conn.Kick(reader, KickReason.ExploitAttempt, LoggingType.Common, $"Connection {conn.ToString()} has sent their FishNet version after being authenticated; this is not possible under normal conditions."); + return; + } + + conn.HasSentVersion = true; + string version = reader.ReadStringAllocated(); + //Version match. + if (version == NetworkManager.FISHNET_VERSION) + { + /* Send to client if server is in development build or not. + * This is to allow the client to utilize some features/information + * received from the server only when it's in dev mode. */ + bool isDevelopmentBuild; +#if DEVELOPMENT + isDevelopmentBuild = true; +#else + isDevelopmentBuild = false; +#endif + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(PacketId.Version); + writer.WriteBoolean(isDevelopmentBuild); + conn.SendToClient((byte)Channel.Reliable, writer.GetArraySegment()); + WriterPool.Store(writer); + + /* If there is an authenticator + * and the transport is not a local transport. */ + Authenticator auth = GetAuthenticator(); + if (auth != null && !NetworkManager.TransportManager.IsLocalTransport(transportId)) + auth.OnRemoteConnection(conn); + else + ClientAuthenticated(conn); + } + else + { + conn.Kick(reader, KickReason.UnexpectedProblem, LoggingType.Warning, $"Connection {conn.ToString()} has been kicked for being on FishNet version {version}. Server version is {NetworkManager.FISHNET_VERSION}."); + } + } + + /// + /// 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; + if (id < 0 || id > NetworkConnection.MAXIMUM_CLIENTID_VALUE) + { + 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 {NetworkConnection.MAXIMUM_CLIENTID_VALUE}. 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(NetworkManager, id, args.TransportIndex, true); + Clients.Add(args.ConnectionId, conn); + _clientsList.Add(conn); + OnRemoteConnectionState?.Invoke(conn, args); + + //Do nothing else until the client sends it's version. + } + //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); + _clientsList.Remove(conn); + Objects.ClientDisconnected(conn); + BroadcastClientConnectionChange(false, conn); + //Return predictedObjectIds. + Queue pqId = conn.PredictedObjectIds; + while (pqId.Count > 0) + Objects.CacheObjectId(pqId.Dequeue()); + + conn.ResetState(); + NetworkManager.Log($"Remote connection stopped for Id {id}."); + } + } + } + } + + /// + /// Sends client their connectionId. + /// + /// + private void SendAuthenticated(NetworkConnection conn) + { + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(PacketId.Authenticated); + writer.WriteNetworkConnection(conn); + /* If predicted spawning is enabled then also send + * reserved objectIds. */ + ; + PredictionManager pm = NetworkManager.PredictionManager; + if (GetAllowPredictedSpawning()) + { + int count = Mathf.Min(Objects.GetObjectIdCache().Count, GetReservedObjectIds()); + if (count > MAXIMUM_RESERVED_OBJECT_IDS) + count = MAXIMUM_RESERVED_OBJECT_IDS; + + List ids = CollectionCaches.RetrieveList(); + for (int i = 0; i < count; i++) + { + if (Objects.GetNextNetworkObjectId(out int nId)) + ids.Add(nId); + } + + writer.WriteSignedPackedWhole(ids.Count); + foreach (int id in ids) + { + writer.WriteNetworkObjectId(id); + conn.PredictedObjectIds.Enqueue(id); + } + + CollectionCaches.Store(ids); + } + + NetworkManager.TransportManager.SendToClient((byte)Channel.Reliable, writer.GetArraySegment(), conn); + writer.Store(); + } + + /// + /// Called when the server socket receives data. + /// + private void Transport_OnServerReceivedData(ServerReceivedDataArgs args) + { + ParseReceived(args); + } + + /// + /// Called when the server receives data. + /// + /// + private void ParseReceived(ServerReceivedDataArgs args) + { + //Not from a valid connection. + if (args.ConnectionId < 0) + return; + + ArraySegment segment; + if (NetworkManager.TransportManager.HasIntermediateLayer) + segment = NetworkManager.TransportManager.ProcessIntermediateIncoming(args.Data, false); + else + segment = args.Data; + + NetworkManager.StatisticsManager.NetworkTraffic.LocalServerReceivedData((ulong)segment.Count); + if (segment.Count <= TransportManager.UNPACKED_TICK_LENGTH) + 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) + { + ExceededMTUKick(); + return; + } + + TimeManager timeManager = NetworkManager.TimeManager; + + bool hasIntermediateLayer = NetworkManager.TransportManager.HasIntermediateLayer; + PacketId packetId = PacketId.Unset; + PooledReader reader = null; +#if !DEVELOPMENT + try + { +#endif + Reader.DataSource dataSource = Reader.DataSource.Client; + reader = ReaderPool.Retrieve(segment, NetworkManager, dataSource); + uint tick = reader.ReadTickUnpacked(); + timeManager.LastPacketTick.Update(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) + { +#if DEVELOPMENT + NetworkManager.PacketIdHistory.ReceivedPacket(PacketId.Split, packetFromServer: false); +#endif + //Skip packetId. + reader.ReadPacketId(); + + int expectedMessages; + _splitReader.GetHeader(reader, out expectedMessages); + //If here split message is to be read into splitReader. + _splitReader.Write(tick, 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. */ + reader.Initialize(fullMessage, NetworkManager, dataSource); + } + + //Parse reader. + while (reader.Remaining > 0) + { + packetId = reader.ReadPacketId(); +#if DEVELOPMENT + NetworkManager.PacketIdHistory.ReceivedPacket(packetId, packetFromServer: false); +#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 {args.ConnectionId} not found within Clients. Connection will be kicked immediately."); + return; + } + conn.LocalTick.Update(timeManager, tick, EstimatedTick.OldTickOption.Discard); + conn.PacketTick.Update(timeManager, tick, EstimatedTick.OldTickOption.SetLastRemoteTick); + /* 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.IsAuthenticated && packetId != PacketId.Version && packetId != PacketId.Broadcast) + { + conn.Kick(KickReason.ExploitAttempt, LoggingType.Common, $"ConnectionId {conn.ClientId} sent packetId {packetId} without being authenticated. Connection will be kicked immediately."); + return; + } + + 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 (!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.ReadSpawn(reader, conn); + } + else if (packetId == PacketId.ObjectDespawn) + { + if (!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.ReadDespawn(reader, conn); + } + else if (packetId == PacketId.Broadcast) + { + ParseBroadcast(reader, conn, args.Channel); + } + else if (packetId == PacketId.PingPong) + { + ParsePingPong(reader, conn); + } + else if (packetId == PacketId.Version) + { + ParseVersion(reader, conn, args.TransportIndex); + } + else + { +#if DEVELOPMENT + NetworkManager.LogError($"Server received an unhandled PacketId of {(ushort)packetId} on channel {args.Channel} from connectionId {args.ConnectionId}. Remaining data has been purged."); + NetworkManager.LogError(NetworkManager.PacketIdHistory.GetReceivedPacketIds(packetsFromServer: false)); +#else + NetworkManager.LogError($"Server received an unhandled PacketId of {(ushort)packetId} on channel {args.Channel} from connectionId {args.ConnectionId}. Connection will be kicked immediately."); + NetworkManager.TransportManager.Transport.StopConnection(args.ConnectionId, true); +#endif + return; + } + } +#if !DEVELOPMENT + } + 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}."); + } + finally + { + reader?.Store(); + } +#else + reader?.Store(); +#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.ReadTickUnpacked(); + 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) + { + //Only send if the connection was authenticated. + if (!conn.IsAuthenticated) + return; + //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() + { + Connected = connected, + Id = conn.ClientId + }; + foreach (NetworkConnection c in Clients.Values) + { + if (c.IsAuthenticated) + Broadcast(c, 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. + List cache = CollectionCaches.RetrieveList(); + foreach (int key in Clients.Keys) + cache.Add(key); + + ConnectedClientsBroadcast allMsg = new() + { + Values = cache + }; + conn.Broadcast(allMsg); + CollectionCaches.Store(cache); + } + } + //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() + { + Connected = connected, + Id = conn.ClientId + }; + Broadcast(conn, changeMsg, true, Channel.Reliable); + } + } + } + } +} \ No newline at end of file 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..4e37c3b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Server/ServerManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 68828c85278210948b9d50a8db3aab74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Server/ServerManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Statistic.meta b/Assets/FishNet/Runtime/Managing/Statistic.meta new file mode 100644 index 0000000..9917e76 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3c83bbce17084ee4bb71193b542a5d10 +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..0fb9285 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1fb953771006e0541ba76e564a90c21d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficArgs.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs new file mode 100644 index 0000000..64e6ef7 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs @@ -0,0 +1,206 @@ + + +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 = { "B", "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.IsClientStarted) + OnClientNetworkTraffic?.Invoke(new(_client_toServerBytes, _client_fromServerBytes)); + if (UpdateServer && _networkManager.IsServerStarted) + OnServerNetworkTraffic?.Invoke(new(_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(float bytes) + { + int decimalPlaces = 2; + if (bytes < 1f || float.IsInfinity(bytes) || float.IsNaN(bytes)) + return ReturnZero(); + + string ReturnZero() + { + decimalPlaces = 0; + return string.Format("{0:n" + decimalPlaces + "} B/s", 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..5317915 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9cc04f6ae0339c94e9153396dce3f46e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Statistic/NetworkTrafficStatistics.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs b/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs new file mode 100644 index 0000000..587e96b --- /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(); + + 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..766fc0d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 756c28cd3141c4140ae776188ee26729 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Statistic/StatisticsManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Timing.meta b/Assets/FishNet/Runtime/Managing/Timing.meta new file mode 100644 index 0000000..54c0b08 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ff89542cb898b9345ada2af3e88721e5 +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..a44bfae --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31f64c354c60cc748a3c0956b14def5b +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..28e3263 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs @@ -0,0 +1,81 @@ +#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++; + if (_physicsMode.intValue == (int)FishNet.Managing.Timing.PhysicsMode.TimeManager) + EditorGUILayout.HelpBox($"Time.fixedDeltaTime will be overriden with TimeManager.TickDelta ({(1f / (float)_tickRate.intValue).ToString("0.###")})", MessageType.Info); + else + EditorGUILayout.HelpBox("If you are using physics interactions be sure to change the PhysicsMode to TimeManager and implement physics within the TimeManager tick events. NetworkTransform may also jitter when not using PhysicsMode.TimeManager.", MessageType.Warning); + EditorGUILayout.PropertyField(_physicsMode); + 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..2ddb5b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 12987a8c0302190489ecb55f6fbd494e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/Editor/TimeManagerEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs new file mode 100644 index 0000000..dcf2e88 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs @@ -0,0 +1,101 @@ +using FishNet.Documenting; +using System; + +namespace FishNet.Managing.Timing +{ + + [APIExclude] + public class MovingAverage : IDisposable + { + #region Public. + /// + /// Average from samples favoring the most recent sample. + /// + public float Average { get; private set; } + /// + /// Sample size being used. + /// + public int SampleSize { 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 < 2) + { + NetworkManagerExtensions.LogWarning("Using a sampleSize of less than 2 will always return the most recent value as Average."); + sampleSize = 1; + } + + SampleSize = sampleSize; + _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]; + + } + + /// + /// Resets values. + /// + public void Reset() + { + _sampleAccumulator = 0f; + _writeIndex = 0; + _writtenSamples = 0; + } + + public void Dispose() + { + Reset(); + } + } + + +} \ 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..ecbb0eb --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 03d05f88778c5c744810e48f251f2d3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/MovingAverage.cs + uploadId: 762203 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..9c9ccdd --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 502e9f31bebd41f4f9088a19eae53735 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/PhysicsMode.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs new file mode 100644 index 0000000..09e517e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs @@ -0,0 +1,195 @@ +using System; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; + +namespace FishNet.Managing.Timing +{ + public readonly struct PreciseTick : IEquatable + { + /// + /// The current tick. + /// + public readonly uint Tick; + /// + /// Percentage of the tick returned between 0d and 1d. + /// + public readonly double PercentAsDouble; + /// + /// Percentage of the tick returned between 0 and 100. + /// + public readonly byte PercentAsByte; + + /// + /// Maximum value a percent can be as a double. + /// + public const double MAXIMUM_DOUBLE_PERCENT = 1d; + /// + /// Maximum value a percent can be as a byte. + /// + public const byte MAXIMUM_BYTE_PERCENT = 100; + + /// + /// Value to use when a precise tick is unset. + /// + public static PreciseTick GetUnsetValue() => new(TimeManager.UNSET_TICK, (byte)0); + + /// + /// Creates a precise tick where the percentage is 0. + /// + public PreciseTick(uint tick) + { + Tick = tick; + PercentAsByte = 0; + PercentAsDouble = 0d; + } + + /// + /// Creates a precise tick where the percentage is a byte between 0 and 100. + /// + public PreciseTick(uint tick, byte percentAsByte) + { + Tick = tick; + + percentAsByte = Maths.ClampByte(percentAsByte, 0, MAXIMUM_BYTE_PERCENT); + PercentAsByte = percentAsByte; + PercentAsDouble = (percentAsByte / 100d); + } + + /// + /// Creates a precise tick where the percentage is a double between 0d and 1d. + /// + public PreciseTick(uint tick, double percent) + { + Tick = tick; + percent = Maths.ClampDouble(percent, 0d, MAXIMUM_DOUBLE_PERCENT); + PercentAsByte = (byte)(percent * 100d); + PercentAsDouble = percent; + } + + public bool IsValid() => (Tick != TimeManager.UNSET_TICK); + + /// + /// Prints PreciseTick information as a string. + /// + /// + public override string ToString() => $"Tick {Tick}, Percent {PercentAsByte.ToString("000")}"; + + public static bool operator ==(PreciseTick a, PreciseTick b) + { + return (a.Tick == b.Tick && a.PercentAsByte == b.PercentAsByte); + } + + public static bool operator !=(PreciseTick a, PreciseTick b) + { + return !(a == b); + } + + public static bool operator >=(PreciseTick a, PreciseTick b) + { + if (b.Tick > a.Tick) return false; + if (a.Tick > b.Tick) return true; + //If here ticks are the same. + return a.PercentAsByte >= b.PercentAsByte; + } + + public static bool operator <=(PreciseTick a, PreciseTick b) => (b >= a); + + public static bool operator >(PreciseTick a, PreciseTick b) + { + if (b.Tick > a.Tick) return false; + if (a.Tick > b.Tick) return true; + //if here ticks are the same. + return a.PercentAsByte > b.PercentAsByte; + } + + public static bool operator <(PreciseTick a, PreciseTick b) => (b > a); + + public bool Equals(PreciseTick other) => (Tick == other.Tick && PercentAsByte == other.PercentAsByte); + + public override bool Equals(object obj) => obj is PreciseTick other && Equals(other); + + public override int GetHashCode() => HashCode.Combine(Tick, PercentAsDouble, PercentAsByte); + } + + public static class PreciseTickExtensions + { + /// + /// Adds value onto a PreciseTick. + /// + /// Value to add. + /// Tick delta. + /// + public static PreciseTick Add(this PreciseTick pt, PreciseTick value, double delta) + { + double ptDouble = pt.AsDouble(delta); + double valueDouble = value.AsDouble(delta); + + double next = (ptDouble + valueDouble); + + return next.AsPreciseTick(delta); + } + + /// + /// Subtracts value from a PreciseTick. + /// + /// Value to subtract. + /// Tick delta. + /// + public static PreciseTick Subtract(this PreciseTick pt, PreciseTick value, double delta) + { + double ptDouble = pt.AsDouble(delta); + double valueDouble = value.AsDouble(delta); + + double remainder = (ptDouble - valueDouble); + + return remainder.AsPreciseTick(delta); + } + + /// + /// Converts a PreciceTick to a double. + /// + /// Tick delta. + /// + public static double AsDouble(this PreciseTick pt, double delta) + { + return ((double)pt.Tick * delta) + (pt.PercentAsDouble * delta); + } + + /// + /// Converts a double to a PreciseTick. + /// + /// Tick delta. + /// + public static PreciseTick AsPreciseTick(this double ptDouble, double delta) + { + if (ptDouble <= 0) + return new(0, 0); + + ulong whole = (ulong)Math.Floor(ptDouble / delta); + //Overflow. + if (whole >= uint.MaxValue) + return PreciseTick.GetUnsetValue(); + + double remainder = (ptDouble % delta); + + double percent = (remainder / delta); + return new((uint)whole, percent); + } + } + + public static class PreciseTickSerializer + { + public static void WritePreciseTick(this Writer writer, PreciseTick value) + { + writer.WriteTickUnpacked(value.Tick); + writer.WriteUInt8Unpacked(value.PercentAsByte); + } + + public static PreciseTick ReadPreciseTick(this Reader reader) + { + uint tick = reader.ReadTickUnpacked(); + byte percentByte = reader.ReadUInt8Unpacked(); + return new(tick, percentByte); + } + } +} \ 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..399752e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a96dd6b21066a424199583b80746464f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/PreciseTick.cs + uploadId: 762203 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..5b5c9ac --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d12f8894fc7343b4bbe332464dc4bce5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/TickRounding.cs + uploadId: 762203 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..cac342a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TickType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 489fba8b0da3c9b4b9ff4e7a46804473 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/TickType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs new file mode 100644 index 0000000..a0d51fa --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs @@ -0,0 +1,1183 @@ +using FishNet.Connection; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System; +using System.Runtime.CompilerServices; +using UnityEngine; +using SystemStopwatch = System.Diagnostics.Stopwatch; + +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 + } + + /// + /// When OnUpdate is performed. + /// + private enum UpdateOrder : byte + { + BeforeTick = 0, + AfterTick = 1, + } + #endregion + + #region Public. + /// + /// NetworkManager used with this. + /// + public NetworkManager NetworkManager { get; private set; } + + /// + /// 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; + + /// + /// How many ticks must pass to update timing. + /// + internal uint TimingTickInterval => _tickRate; + + /// + /// RoundTripTime in milliseconds. This value includes latency from the tick rate. + /// + public long RoundTripTime { get; private set; } + /// + /// Returns half value of RoundTripTime rounded to nearest whole. + /// + public long HalfRoundTripTime => (long)Math.Round((double)RoundTripTime / 2d); + + /// + /// 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 EstimatedTick LastPacketTick { get; internal set; } = new(); + + /// + /// 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 = true; + /// + /// 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 = 3; + /// + /// + /// + [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. + /// + public byte PingInterval => _pingInterval; + + /// + /// + /// + [Tooltip("How to perform physics.")] + [SerializeField] + private PhysicsMode _physicsMode = PhysicsMode.Unity; + + /// + /// How to perform physics. + /// + public PhysicsMode PhysicsMode => _physicsMode; + #endregion + + #region Private. + /// + /// + /// + 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.IsServerStarted) ? Tick : _localTick; + private set => _localTick = value; + } + + /// + /// Stopwatch used for pings. + /// + private SystemStopwatch _pingStopwatch = new(); + /// + /// Ticks passed since last ping. + /// + private uint _pingTicks; + /// + /// MovingAverage instance used to calculate mean ping. + /// + private MovingAverage _pingAverage = new(5); + /// + /// Accumulating frame time to determine when to increase tick. + /// + private double _elapsedTickTime; + /// + /// Internal deltaTime for clients. Controlled by the server. + /// + private double _adjustedTickDelta; + /// + /// 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; + /// + /// True if FixedUpdate called this frame and using Unity physics mode. + /// + private bool _fixedUpdateTimeStep; + /// + /// + /// + private float _physicsTimeScale = 1f; + + /// + /// Gets the current physics time scale. + /// + /// + public float GetPhysicsTimeScale() => _physicsTimeScale; + + /// + /// Sets the physics time scale. + /// This is not automatically synchronized. + /// + /// New value. + public void SetPhysicsTimeScale(float value) + { + value = Mathf.Clamp(value, 0f, float.PositiveInfinity); + _physicsTimeScale = value; + } + #endregion + + #region Const. + /// + /// Value for a tick that is invalid. + /// + public const uint UNSET_TICK = 0; + /// + /// 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) + { + /* If fixedUpdateTimeStep then that means + * FixedUpdate already called for this frame, which + * means a post physics should also be called. + * This can only happen if a FixedUpdate occurs + * multiple times per frame. */ + if (_fixedUpdateTimeStep) + OnPostPhysicsSimulation?.Invoke(Time.fixedDeltaTime); + + _fixedUpdateTimeStep = true; + OnPrePhysicsSimulation?.Invoke(Time.fixedDeltaTime); + } + } + + /// + /// Called when Update ticks. This is called before any other script. + /// + internal void TickUpdate() + { + if (NetworkManager.IsServerStarted) + ServerUptime += Time.deltaTime; + if (NetworkManager.IsClientStarted) + 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 && _fixedUpdateTimeStep) + { + _fixedUpdateTimeStep = false; + 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; + LastPacketTick.Initialize(networkManager.TimeManager); + 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) + { + if (!NetworkManager.IsServerStarted) + LastPacketTick.ResetTicks(); + _pingStopwatch.Stop(); + ClientUptime = 0f; + + //Only reset ticks if also not server. + if (!NetworkManager.IsServerStarted) + { + LocalTick = 0; + Tick = 0; + SetTickRate(TickRate); + } + } + //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.IsAnyServerStarted()) + { + LastPacketTick.ResetTicks(); + 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() + { + SetAutomaticPhysicsSimulation(true); + + float simulationTime = PlayerPrefs.GetFloat(SAVED_FIXED_TIME_TEXT, float.MinValue); + if (simulationTime != float.MinValue) + Time.fixedDeltaTime = simulationTime; + } + + /// + /// Sets automatic physics simulation mode. + /// + /// + private void SetAutomaticPhysicsSimulation(bool automatic) + { +#if UNITY_2022_1_OR_NEWER + if (automatic) + { + Physics.simulationMode = SimulationMode.FixedUpdate; + Physics2D.simulationMode = SimulationMode2D.FixedUpdate; + } + else + { + Physics.simulationMode = SimulationMode.Script; + Physics2D.simulationMode = SimulationMode2D.Script; + } +#else + Physics.autoSimulation = automatic; + if (automatic) + Physics2D.simulationMode = SimulationMode2D.FixedUpdate; + else + Physics2D.simulationMode = SimulationMode2D.Script; +#endif + } + + /// + /// 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) + SetAutomaticPhysicsSimulation(false); + //Automatically simulate. + else + SetAutomaticPhysicsSimulation(true); + } + + #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; + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(PacketId.PingPong); + writer.WriteTickUnpacked(tick); + NetworkManager.TransportManager.SendToServer((byte)Channel.Unreliable, writer.GetArraySegment()); + writer.Store(); + } + + /// + /// Sends a pong to a client. + /// + internal void SendPong(NetworkConnection conn, uint clientTick) + { + if (!conn.IsActive || !conn.IsAuthenticated) + return; + + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(PacketId.PingPong); + writer.WriteTickUnpacked(clientTick); + conn.SendToClient((byte)Channel.Unreliable, writer.GetArraySegment()); + writer.Store(); + } + #endregion + + /// + /// Increases the tick based on simulation rate. + /// + private void IncreaseTick() + { + bool isClient = NetworkManager.IsClientStarted; + bool isServer = NetworkManager.IsServerStarted; + + double timePerSimulation = (isServer) ? TickDelta : _adjustedTickDelta; + if (timePerSimulation == 0d) + { + NetworkManagerExtensions.LogWarning($"Simulation delta cannot be 0. Network timing will not continue."); + return; + } + + 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) + { + //If ticks require dropping. Set exactly to maximum ticks. + if (ticksCount > _maximumFrameTicks) + _elapsedTickTime = (timePerSimulation * (double)_maximumFrameTicks); + } + + bool variableTiming = (_timingType == TimingType.Variable); + bool frameTicked = FrameTicked; + float tickDelta = ((float)TickDelta * GetPhysicsTimeScale()); + + do + { + if (frameTicked) + 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) + { + //Tell predicted objecs to reconcile before OnTick. + NetworkManager.PredictionManager.ReconcileToStates(); + OnTick?.Invoke(); + + if (PhysicsMode == PhysicsMode.TimeManager && tickDelta > 0f) + { + OnPrePhysicsSimulation?.Invoke(tickDelta); + Physics.Simulate(tickDelta); + Physics2D.Simulate(tickDelta); + OnPostPhysicsSimulation?.Invoke(tickDelta); + } + + OnPostTick?.Invoke(); + //After post tick send states. + NetworkManager.PredictionManager.SendStateUpdate(); + + /* If isClient this is the + * last tick during this loop. */ + bool lastTick = (_elapsedTickTime < (timePerSimulation * 2d)); + if (isClient && lastTick) + TrySendPing(LocalTick + 1); + if (NetworkManager.IsServerStarted) + SendTimingAdjustment(); + } + + //Send out data. + if (frameTicked || variableTiming) + TryIterateData(false); + + if (frameTicked) + { + _elapsedTickTime -= timePerSimulation; + Tick++; + LocalTick++; + } + } while (_elapsedTickTime >= timePerSimulation); + } + + + #region Tick conversions. + /// + /// Returns the percentage of how far the TimeManager is into the next tick as a double. + /// Value will return between 0d and 1d. + /// + /// + public double GetTickPercentAsDouble() + { + if (NetworkManager == null) + return 0d; + + double percent = (_elapsedTickTime / TickDelta); + return percent; + } + + /// + /// Returns the current elapsed amount for the next tick. + /// + /// + public double GetTickElapsedAsDouble() => _elapsedTickTime; + + /// + /// Returns the percentage of how far the TimeManager is into the next tick. + /// Value will return between 0 and 100. + /// + public byte GetTickPercentAsByte() + { + double result = GetTickPercentAsDouble(); + return (byte)(result * 100d); + } + + /// + /// Converts a 0 to 100 byte value to a 0d to 1d percent value. + /// This does not check for excessive byte values, such as anything over 100. + /// + public static double GetTickPercentAsDouble(byte value) + { + return (value / 100d); + } + + /// + /// Returns a PreciseTick. + /// + /// Tick to set within the returned PreciseTick. + /// + public PreciseTick GetPreciseTick(uint tick) + { + if (NetworkManager == null) + return default; + + double delta = (NetworkManager.IsServerStarted) ? TickDelta : _adjustedTickDelta; + double percent = (_elapsedTickTime / delta); + + return new(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.LastRemoteTick); + } + else + { + NetworkManager.LogError($"TickType {tickType.ToString()} is unhandled."); + return default; + } + } + + + /// + /// Converts current ticks to time. + /// + /// TickType to compare against. + /// + + 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.LastRemoteTick); + } + else + { + NetworkManager.LogError($"TickType {tickType} is unhandled."); + return 0d; + } + } + + /// + /// Converts a PreciseTick to time. + /// + /// PreciseTick to convert. + /// + + public double TicksToTime(PreciseTick pt) + { + double tickTime = TicksToTime(pt.Tick); + double percentTime = (pt.PercentAsDouble * TickDelta); + return (tickTime + percentTime); + } + + /// + /// 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. + /// + + 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. + /// + + public double TimePassed(PreciseTick preciseTick, bool allowNegative = false) + { + PreciseTick currentPt = GetPreciseTick(TickType.Tick); + + long tickDifference = ((long)currentPt.Tick - (long)preciseTick.Tick); + double percentDifference = (currentPt.PercentAsDouble - preciseTick.PercentAsDouble); + + /* 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 percentTime = (percentDifference * 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. + /// + + 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 as decimal. + /// + + 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); + } + + /// + /// Converts time to ticks. + /// + /// Time to convert as whole (milliseconds) + /// + + public uint TimeToTicks(long time, TickRounding rounding = TickRounding.RoundNearest) + { + double dTime = ((double)time / 1000d); + return TimeToTicks(dTime, rounding); + } + + + /// + /// Converts time to a PreciseTick. + /// + /// Time to convert. + /// + public PreciseTick TimeToPreciseTick(double time) => time.AsPreciseTick(TickDelta); + + /// + /// 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.IsServerStarted) + return tick; + + long difference = (Tick - tick); + + 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.IsServerStarted) + return localTick; + + long difference = (LocalTick - localTick); + + 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 due to + * there possibly being multiple ticks per frame. */ + int frameCount = Time.frameCount; + if (frameCount == _lastIncomingIterationFrame) + return; + _lastIncomingIterationFrame = frameCount; + + NetworkManager.TransportManager.IterateIncoming(asServer: true); + NetworkManager.TransportManager.IterateIncoming(asServer: false); + } + else + { + NetworkManager.TransportManager.IterateOutgoing(asServer: true); + NetworkManager.TransportManager.IterateOutgoing(asServer: false); + } + } + + + #region Timing adjusting. + /// + /// Changes the adjustedTickDelta, increasing or decreasing it. + /// + /// Amount to multiply expected change by. This can be used to make larger or smaller changes. + internal void ChangeAdjustedTickDelta(bool speedUp, double additionalMultiplier = 1d) + { + double share = (TickDelta * 0.01d) * additionalMultiplier; + if (speedUp) + _adjustedTickDelta -= share; + else + _adjustedTickDelta += share; + } + + /// + /// Sends a TimingUpdate packet to clients. + /// + private void SendTimingAdjustment() + { + //Send every second. + if (LocalTick % TimingTickInterval == 0) + { + //Now send using a packetId. + PooledWriter writer = WriterPool.Retrieve(); + foreach (NetworkConnection item in NetworkManager.ServerManager.Clients.Values) + { + if (!item.IsAuthenticated) + continue; + + writer.WritePacketIdUnpacked(PacketId.TimingUpdate); + writer.WriteTickUnpacked(item.PacketTick.Value()); + item.SendToClient((byte)Channel.Unreliable, writer.GetArraySegment()); + writer.Clear(); + } + + writer.Store(); + } + } + + /// + /// Called on client when server sends a timing update. + /// + /// + internal void ParseTimingUpdate(Reader reader) + { + uint clientTick = reader.ReadTickUnpacked(); + //Don't adjust timing on server. + if (NetworkManager.IsServerStarted) + return; + /* This should never be possible since the server is sending a tick back + * that the client previously sent. In other words, the value returned should + * always be in the past. */ + if (LocalTick < clientTick) + return; + + /* Use the last ordered remote tick rather than + * lastPacketTick. This will help with out of order + * packets where the timing update sent before + * the remote tick but arrived after. By using ordered + * remote tick we are comparing against however many + * ticks really passed rather than the difference + * between the out of order/late packet. */ + uint lastPacketTick = LastPacketTick.RemoteTick; + //Set Tick based on difference between localTick and clientTick, added onto lastPacketTick. + uint prevTick = Tick; + //Added ticks for delay in reading packet. + const uint socketReadDelay = 1; + uint nextTick = ((LocalTick - clientTick) / 2) + lastPacketTick + socketReadDelay; + long difference = ((long)nextTick - (long)prevTick); + Tick = nextTick; + + //Maximum difference allowed before resetting values. + const int maximumDifference = 4; + //Difference is extreme, reset to default timings. Client probably had an issue. + if (Mathf.Abs(difference) > maximumDifference) + { + _adjustedTickDelta = TickDelta; + } + //Otherwise adjust the delta marginally. + else if (difference != 0) + { + /* A negative tickDifference indicates the client is + * moving too fast, while positive indicates too slow. */ + bool speedUp = (difference > 0); + ChangeAdjustedTickDelta(speedUp); + } + } + #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; + } + + #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..b8fc770 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3fdaae44044276a49a52229c1597e33b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Timing/TimeManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Transporting.meta b/Assets/FishNet/Runtime/Managing/Transporting.meta new file mode 100644 index 0000000..72b7c9e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 177682847d7bda74daa089260df1122e +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..15f3b6b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs @@ -0,0 +1,39 @@ + +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 HandleOutgoing(ArraySegment src, bool toServer); + + /// + /// Initializes this IntermediateLayer for use. + /// + public virtual 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..dd6c67a --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3f8a2e0f9aaea614887f5f7b15350e46 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Transporting/IntermediateLayer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs new file mode 100644 index 0000000..15d786e --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs @@ -0,0 +1,380 @@ +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(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(); + /// + /// Unreliable messages to the server. + /// + private List _toServerUnreliable = new(); + /// + /// Reliable messages to clients. + /// + private List _toClientReliable = new(); + /// + /// Unreliable messages to clients. + /// + private List _toClientUnreliable = new(); + /// + /// NetworkManager for this instance. + /// + private NetworkManager _networkManager; + /// + /// Used to generate chances of latency. + /// + private readonly System.Random _random = new(); + #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) + { + //If not enabled exit early to save work. + if (!GetEnabled()) + return; + + 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.IsHostStarted) + { + /* 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(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 to send data from the local server to clients, false to send from the local client to server. + public void IterateOutgoing(bool asServer) + { + if (_transport == null) + { + Reset(); + return; + } + + if (asServer) + { + IterateCollection(_toClientReliable, Channel.Reliable); + IterateCollection(_toClientUnreliable, Channel.Unreliable); + } + else + { + IterateCollection(_toServerReliable, Channel.Reliable); + IterateCollection(_toServerUnreliable, 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 (asServer) + _transport.SendToClient(cByte, msg.GetSegment(), msg.ConnectionId); + else + _transport.SendToServer(cByte, msg.GetSegment()); + + iterations++; + } + + if (iterations > 0) + { + for (int i = 0; i < iterations; i++) + ByteArrayPool.Store(collection[i].Data); + collection.RemoveRange(0, iterations); + } + } + + _transport.IterateOutgoing(asServer); + } + + /// + /// 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..fd22bf1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 82bfafe804acb534fbf04c88de6eeed1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Transporting/LatencySimulator.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs b/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs new file mode 100644 index 0000000..d12d5a7 --- /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.Retrieve(); + #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.Clear(); + } + + } + + +} \ 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..cb650b1 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a1c06be1540b77842be35823aa54b19b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Transporting/SplitReader.cs + uploadId: 762203 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..38be457 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using FishNet.Connection; +using FishNet.Transporting; +using FishNet.Transporting.Multipass; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Managing.Transporting +{ + + /// + /// Communicates with the Transport to send and receive data. + /// + public sealed partial class TransportManager : MonoBehaviour + { + /// + /// Returns IsLocalTransport for the transportId, optionally checking against a connectionId. + /// + public bool IsLocalTransport(int transportId, int connectionId = NetworkConnection.UNSET_CLIENTID_VALUE) + { + if (Transport == null) + return false; + + if (Transport is Multipass mp) + return mp.IsLocalTransport(transportId, connectionId); + else + return Transport.IsLocalTransport(connectionId); + } + + /// + /// 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); + } + } + + /// + /// Returns all transports configured on the TransportManager. + /// + /// True to add Multipass to the results if being used. When false and using Multipass only the transport specified within Multipass will be returned. + /// + /// This returns a collection from cache. + public List GetAllTransports(bool includeMultipass) + { + List results = CollectionCaches.RetrieveList(); + + //If using multipass check all transports. + if (Transport is Multipass mp) + { + if (includeMultipass) + results.Add(Transport); + + + foreach (Transport t in mp.Transports) + results.Add(t); + } + //Not using multipass. + else + { + results.Add(Transport); + } + + return results; + } + } + +} \ 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..f582a7d --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aac8eab4e511d7e4dbc81eb74aea7f23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Transporting/TransportManager.QOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs new file mode 100644 index 0000000..51f6f4b --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs @@ -0,0 +1,921 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Connection; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Transporting.Multipass; +using System; +using System.Collections.Generic; +using GameKit.Dependencies.Utilities; +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. + /// + /// Returns if an IntermediateLayer is in use. + /// + public bool HasIntermediateLayer => (_intermediateLayer != null); + /// + /// 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(); + /// + /// Latency simulation settings. + /// + public LatencySimulator LatencySimulator + { + get + { + //Shouldn't ever be null unless the user nullifies it. + if (_latencySimulator == null) + _latencySimulator = new(); + return _latencySimulator; + } + } + #endregion + + #region Private. + /// + /// NetworkConnections on the server which have to send data to clients. + /// + private List _dirtyToClients = new(); + /// + /// PacketBundles to send to the server. + /// + private List _toServerBundles = new(); + /// + /// NetworkManager handling this TransportManager. + /// + private NetworkManager _networkManager; + /// + /// Clients which are pending disconnects. + /// + private List _disconnectingClients = new(); + /// + /// Lowest MTU of all transports for channels. + /// + private int[] _lowestMtus; + /// + /// Lowest MTU of all transports of all channels. + /// + private int _lowestMtu = 0; + /// + /// Custom amount to reserve on the MTU. + /// + private int _customMtuReserve = MINIMUM_MTU_RESERVE; + #endregion + + #region Consts. + /// + /// Number of bytes sent for PacketId. + /// + public const byte PACKETID_LENGTH = 2; + /// + /// Number of bytes sent for ObjectId. + /// + public const byte OBJECT_ID_LENGTH = 2; + /// + /// Number of bytes sent for ComponentIndex. + /// + public const byte COMPONENT_INDEX_LENGTH = 1; + /// + /// Number of bytes sent for Tick. + /// + public const byte UNPACKED_TICK_LENGTH = 4; + /// + /// Number of bytes sent for an unpacked size, such as a collection or array size. + /// + public const byte UNPACKED_SIZE_LENGTH = 4; + /// + /// Number of bytes sent to indicate split count. + /// + private const byte SPLIT_COUNT_LENGTH = 4; + /// + /// Number of bytes required for split data. + /// //todo: This shouldn't have to include TickBytes but there is a parse error if it's not included. Figure out why. + public const byte SPLIT_INDICATOR_LENGTH = (UNPACKED_TICK_LENGTH + PACKETID_LENGTH + SPLIT_COUNT_LENGTH); + /// + /// Number of channels supported. + /// + public const byte CHANNEL_COUNT = 2; + /// + /// MTU reserved for internal use. + /// 1 byte is used to specify channel in packets for transports that do not include channel within their packet header. This is transport dependent. + /// + public const int MINIMUM_MTU_RESERVE = 1; + /// + /// Value to use when a MTU could not be found. + /// + public const int INVALID_MTU = -1; + #endregion + + /// + /// Initializes this script for use. + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + _networkManager = manager; + TryAddDefaultTransport(); + Transport.Initialize(_networkManager, 0); + SetLowestMTUs(); + InitializeToServerBundles(); + + manager.ServerManager.OnServerConnectionState += ServerManager_OnServerConnectionState; + manager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState; + + if (_intermediateLayer != null) + _intermediateLayer.InitializeOnce(this); +#if DEVELOPMENT + _latencySimulator.Initialize(manager, Transport); +#endif + } + + /// + /// Sets the lowest MTU values. + /// + private void SetLowestMTUs() + { + //Already set. + if (_lowestMtu != 0) + return; + + /* At least one transport is required. + * Try to add default. If a transport is already + * specified the add method will just exit early. */ + TryAddDefaultTransport(); + + int allLowest = int.MaxValue; + //Cache lowest Mtus. + _lowestMtus = new int[CHANNEL_COUNT]; + for (byte i = 0; i < CHANNEL_COUNT; i++) + { + int channelLowest = int.MaxValue; + if (Transport is Multipass mp) + { + foreach (Transport t in mp.Transports) + { + int mtu = t.GetMTU(i); + if (mtu != INVALID_MTU) + channelLowest = Mathf.Min(channelLowest, mtu); + } + } + else + { + channelLowest = Transport.GetMTU(i); + } + + _lowestMtus[i] = channelLowest; + _lowestMtu = Mathf.Min(allLowest, channelLowest); + } + } + + /// + /// Adds the default transport if a transport is not yet specified. + /// + private void TryAddDefaultTransport() + { + if (Transport == null && !gameObject.TryGetComponent(out Transport)) + Transport = gameObject.AddComponent(); + } + + /// + /// Called when the local connection state changes for the client. + /// + private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs obj) + { + //Not stopped. + if (obj.ConnectionState != LocalConnectionState.Stopped) + return; + + //Reset toServer data. + foreach (PacketBundle pb in _toServerBundles) + pb.Reset(resetSendLast: true); + } + + /// + /// Called when the local connection state changes for the server. + /// + private void ServerManager_OnServerConnectionState(ServerConnectionStateArgs obj) + { + //Not stopped. + if (obj.ConnectionState != LocalConnectionState.Stopped) + return; + + //If no server is started just clear all dirtyToClients. + if (!_networkManager.ServerManager.IsAnyServerStarted()) + { + _dirtyToClients.Clear(); + return; + } + + //Only one server is stopped, remove connections for that server. + int index = obj.TransportIndex; + + List clientsForIndex = CollectionCaches.RetrieveList(); + foreach (NetworkConnection conn in _dirtyToClients) + { + if (conn.TransportIndex == index) + clientsForIndex.Add(conn); + } + + foreach (NetworkConnection conn in clientsForIndex) + _dirtyToClients.Remove(conn); + + CollectionCaches.Store(clientsForIndex); + } + + ///// + ///// Gets port for the first transport, or client transport if using Multipass. + ///// + //private ushort GetPort(bool asServer) + //{ + // if (Transport is Multipass mp) + // { + // if (asServer) + // return mp.Transports[0].GetPort(); + // else + // return mp.ClientTransport.GetPort(); + // } + // else + // { + // return Transport.GetPort(); + // } + //} + + ///// + ///// Stops the local server or client connection. + ///// + //internal bool StopConnection(bool asServer) + //{ + // return Transport.StopConnection(asServer); + //} + + ///// + ///// Starts the local server or client connection. + ///// + //internal bool StartConnection(bool asServer) + //{ + // return Transport.StartConnection(asServer); + //} + + ///// + ///// Starts the local server or client connection. + ///// + //internal bool StartConnection(string address, bool asServer) + //{ + // return StartConnection(address, GetPort(asServer), asServer); + //} + + ///// + ///// Starts the local server or client connection on the first transport or ClientTransport if using Multipass and as client. + ///// + //internal bool StartConnection(string address, ushort port, bool asServer) + //{ + // Transport t; + // if (Transport is Multipass mp) + // { + // if (asServer) + // t = mp.Transports[0]; + // else + // t = mp.ClientTransport; + // } + // else + // { + // t = Transport; + // } + + // /* SetServerBindAddress must be called explictly. Only + // * set address if for client. */ + // if (!asServer) + // t.SetClientAddress(address); + // t.SetPort(port); + + // return t.StartConnection(asServer); + //} + + /// + /// 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(_networkManager, mtu)); + } + } + + #region GetMTU. + /// + /// Returns MTU excluding reserve amount. + /// + private int GetMTUWithReserve(int mtu) + { + int value = (mtu - MINIMUM_MTU_RESERVE - _customMtuReserve); + /* If MTU is extremely low then warn user. + * The number choosen has no significant value. */ + if (value <= 100) + { + string msg = $"Available MTU of {mtu} is significantly low; an invalid MTU will be returned. Check transport settings, or reduce MTU reserve if you set one using {nameof(TransportManager.SetMTUReserve)}"; + _networkManager.LogWarning(msg); + + return INVALID_MTU; + } + + return value; + } + + /// + /// Sets a custom value to reserve for the internal buffers. + /// This value is also deducted from transport MTU when using GetMTU methods. + /// + /// Value to use. + public void SetMTUReserve(int value) + { + if (_networkManager != null && _networkManager.IsClientStarted || _networkManager.IsServerStarted) + { + _networkManager.LogError($"A custom MTU reserve cannot be set after the server or client have been started or connected."); + return; + } + + if (value < MINIMUM_MTU_RESERVE) + { + _networkManager.Log($"MTU reserve {value} is below minimum value of {MINIMUM_MTU_RESERVE}. Value has been updated to {MINIMUM_MTU_RESERVE}."); + value = MINIMUM_MTU_RESERVE; + } + + _customMtuReserve = value; + InitializeToServerBundles(); + } + + /// + /// Returns the current MTU reserve. + /// + /// + public int GetMTUReserve() => _customMtuReserve; + + /// + /// Returns the lowest MTU of all channels. When using multipass this will evaluate all transports within Multipass. + /// + /// + /// + public int GetLowestMTU() + { + SetLowestMTUs(); + return GetMTUWithReserve(_lowestMtu); + } + + /// + /// Returns the lowest MTU for a channel. When using multipass this will evaluate all transports within Multipass. + /// + /// + /// + public int GetLowestMTU(byte channel) + { + SetLowestMTUs(); + return GetMTUWithReserve(_lowestMtus[channel]); + } + + /// + /// Gets MTU on the current transport for channel. + /// + /// Channel to get MTU of. + /// + public int GetMTU(byte channel) + { + SetLowestMTUs(); + int mtu = Transport.GetMTU(channel); + if (mtu == INVALID_MTU) + return mtu; + + return GetMTUWithReserve(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. + /// + public int GetMTU(int transportIndex, byte channel) + { + if (Transport is Multipass mp) + { + int mtu = mp.GetMTU(channel, transportIndex); + if (mtu == INVALID_MTU) + return INVALID_MTU; + + return GetMTUWithReserve(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. + /// + public int GetMTU(byte channel) where T : Transport + { + Transport transport = GetTransport(); + if (transport != null) + { + int mtu = transport.GetMTU(channel); + if (mtu == INVALID_MTU) + return mtu; + + return GetMTUWithReserve(mtu); + } + + //Fall through. + return INVALID_MTU; + } + #endregion + + /// + /// Passes received to the intermediate layer. + /// + internal ArraySegment ProcessIntermediateIncoming(ArraySegment src, bool fromServer) + { + return _intermediateLayer.HandleIncoming(src, fromServer); + } + + /// + /// Passes sent to the intermediate layer. + /// + private ArraySegment ProcessIntermediateOutgoing(ArraySegment src, bool toServer) + { + return _intermediateLayer.HandleOutgoing(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. + internal void SendToClient(byte channelId, ArraySegment segment, NetworkConnection connection, bool splitLargeMessages = true, DataOrderType orderType = DataOrderType.Default) + { + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredMessages, out int maxSplitMessageSize); + SendToClient(channelId, segment, connection, requiredMessages, maxSplitMessageSize, orderType); + } + + private void SendToClient(byte channelId, ArraySegment segment, NetworkConnection connection, int requiredSplitMessages, int maxSplitMessageSize, DataOrderType orderType = DataOrderType.Default) + { + if (connection == null) + return; + + if (requiredSplitMessages > 1) + SendSplitData(connection, ref segment, requiredSplitMessages, maxSplitMessageSize, orderType); + else + connection.SendToClient(channelId, segment, false, orderType); + } + + /// + /// Sends data to observers. + /// + internal void SendToClients(byte channelId, ArraySegment segment, HashSet observers, HashSet excludedConnections = null, bool splitLargeMessages = true, DataOrderType orderType = DataOrderType.Default) + { + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredMessages, out int maxSplitMessageSize); + SendToClients(channelId, segment, observers, excludedConnections, requiredMessages, maxSplitMessageSize, orderType); + } + + private void SendToClients(byte channelId, ArraySegment segment, HashSet observers, HashSet excludedConnections, int requiredSplitMessages, int maxSplitMessageSize, DataOrderType orderType = DataOrderType.Default) + { + if (excludedConnections == null || excludedConnections.Count == 0) + { + foreach (NetworkConnection conn in observers) + SendToClient(channelId, segment, conn, requiredSplitMessages, maxSplitMessageSize, orderType); + } + else + { + foreach (NetworkConnection conn in observers) + { + if (excludedConnections.Contains(conn)) + continue; + SendToClient(channelId, segment, conn, requiredSplitMessages, maxSplitMessageSize, orderType); + } + } + } + + /// + /// 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. + internal void SendToClients(byte channelId, ArraySegment segment, bool splitLargeMessages = true) + { + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredMessages, out int maxSplitMessageSize); + SendToClients_Internal(channelId, segment, requiredMessages, 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(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. + internal void SendToServer(byte channelId, ArraySegment segment, bool splitLargeMessages = true, DataOrderType orderType = DataOrderType.Default) + { + SetSplitValues(channelId, segment, splitLargeMessages, out int requiredMessages, out int maxSplitMessageSize); + SendToServer(channelId, segment, requiredMessages, maxSplitMessageSize, orderType); + } + + private void SendToServer(byte channelId, ArraySegment segment, int requiredMessages, int maxSplitMessageSize, DataOrderType orderType) + { + if (channelId >= _toServerBundles.Count) + channelId = (byte)Channel.Reliable; + + if (requiredMessages > 1) + SendSplitData(null, ref segment, requiredMessages, maxSplitMessageSize, orderType); + else + _toServerBundles[channelId].Write(segment, false, orderType); + } + + #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 requiredMessages, out int maxSplitMessageSize) + { + if (!split) + { + requiredMessages = 0; + maxSplitMessageSize = 0; + } + else + { + SplitRequired(channelId, segment.Count, out requiredMessages, out maxSplitMessageSize); + } + } + + /// + /// Checks to set channel to reliable if dataLength is too long. + /// + internal void CheckSetReliableChannel(int dataLength, ref Channel channel) + { + if (channel == Channel.Reliable) + return; + + bool requiresMultipleMessages = (GetRequiredMessageCount((byte)channel, dataLength, out _) > 1); + if (requiresMultipleMessages) + channel = Channel.Reliable; + } + + /// + /// Gets the required number of messages needed for segmentSize and channel. + /// + private int GetRequiredMessageCount(byte channelId, int segmentSize, out int maxMessageSize) + { + maxMessageSize = GetLowestMTU(channelId) - SPLIT_INDICATOR_LENGTH; + return Mathf.CeilToInt((float)segmentSize / maxMessageSize); + } + + /// + /// True if data must be split. + /// + /// + /// + private bool SplitRequired(byte channelId, int segmentSize, out int requiredMessages, out int maxMessageSize) + { + requiredMessages = GetRequiredMessageCount(channelId, segmentSize, out maxMessageSize); + + bool splitRequired = (requiredMessages > 1); + if (splitRequired && channelId != (byte)Channel.Reliable) + _networkManager.LogError($"A message of length {segmentSize} requires the reliable channel but was sent on channel {(Channel)channelId}. Please file this stack trace as a bug report."); + + return splitRequired; + } + + /// + /// 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, DataOrderType orderType) + { + 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.Retrieve(); + headerWriter.WritePacketIdUnpacked(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(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, orderType); + _toServerBundles[channelId].Write(splitSegment, false, orderType); + } + + writeIndex += chunkSize; + } + + headerWriter.Store(); + } + #endregion + + /// + /// Processes data received by the socket. + /// + /// True to read data from clients, false to read data from the server. + internal void IterateIncoming(bool asServer) + { + OnIterateIncomingStart?.Invoke(asServer); + Transport.IterateIncoming(asServer); + OnIterateIncomingEnd?.Invoke(asServer); + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to send data from the local server to clients, false to send from the local client to server. + internal void IterateOutgoing(bool asServer) + { + if (asServer && _networkManager.ServerManager.AreAllServersStopped()) + return; + + OnIterateOutgoingStart?.Invoke(); + int channelCount = CHANNEL_COUNT; + ulong sentBytes = 0; +#if DEVELOPMENT + bool latencySimulatorEnabled = LatencySimulator.CanSimulate; +#endif + if (asServer) + SendAsServer(); + else + SendAsClient(); + + //Sends data as server. + void SendAsServer() + { + 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)) + { + ProcessPacketBundle(pb); + ProcessPacketBundle(pb.GetSendLastBundle(), true); + + void ProcessPacketBundle(PacketBundle ppb, bool isLast = false) + { + for (int i = 0; i < ppb.WrittenBuffers; i++) + { + //Length should always be more than 0 but check to be safe. + if (ppb.GetBuffer(i, out ByteBuffer bb)) + { + ArraySegment segment = new(bb.Data, 0, bb.Length); + if (HasIntermediateLayer) + segment = ProcessIntermediateOutgoing(segment, false); +#if DEVELOPMENT + if (latencySimulatorEnabled) + _latencySimulator.AddOutgoing(channel, segment, false, conn.ClientId); + else +#endif + Transport.SendToClient(channel, segment, conn.ClientId); + sentBytes += (ulong)segment.Count; + } + } + + ppb.Reset(false); + } + } + } + + /* 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(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); + } + + //Sends data as client. + void SendAsClient() + { + for (byte channel = 0; channel < channelCount; channel++) + { + if (PacketBundle.GetPacketBundle(channel, _toServerBundles, out PacketBundle pb)) + { + ProcessPacketBundle(pb); + ProcessPacketBundle(pb.GetSendLastBundle()); + + void ProcessPacketBundle(PacketBundle ppb) + { + for (int i = 0; i < ppb.WrittenBuffers; i++) + { + if (ppb.GetBuffer(i, out ByteBuffer bb)) + { + ArraySegment segment = new(bb.Data, 0, bb.Length); + if (HasIntermediateLayer) + segment = ProcessIntermediateOutgoing(segment, true); +#if DEVELOPMENT + if (latencySimulatorEnabled) + _latencySimulator.AddOutgoing(channel, segment); + else +#endif + Transport.SendToServer(channel, segment); + sentBytes += (ulong)segment.Count; + } + } + + ppb.Reset(false); + } + } + } + + _networkManager.StatisticsManager.NetworkTraffic.LocalClientSentData(sentBytes); + } + +#if DEVELOPMENT + if (latencySimulatorEnabled) + _latencySimulator.IterateOutgoing(asServer); +#endif + + Transport.IterateOutgoing(asServer); + 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..34efca8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 34e4a322dca349547989b14021da4e23 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Transporting/TransportManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Managing/Utility.meta b/Assets/FishNet/Runtime/Managing/Utility.meta new file mode 100644 index 0000000..42aa1f3 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2661a72b34311dc419a0f1eda6e2aaab +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..e6160c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Utility/Utility.cs @@ -0,0 +1,54 @@ +using FishNet.Object; +using FishNet.Serializing; +using FishNet.Transporting; + +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. */ + /* //todo: we will not always get length on reconciles once + * the issue with parsed headers is resolved. */ + PacketId pid = (PacketId)packetId; + if (channel == Channel.Reliable || + pid == PacketId.Broadcast || + pid == PacketId.SyncType || + pid == PacketId.Reconcile + ) + { + 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 + { + reader.NetworkManager.LogError($"Operation is unhandled for packetId {(PacketId)packetId} on channel {channel}."); + return (int)MissingObjectPacketLength.PurgeRemaiming; + } + } + } + + +} \ 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..b7811cc --- /dev/null +++ b/Assets/FishNet/Runtime/Managing/Utility/Utility.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: facab6859c82ccd49b2d1c6b80404726 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Managing/Utility/Utility.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object.meta b/Assets/FishNet/Runtime/Object.meta new file mode 100644 index 0000000..321855c --- /dev/null +++ b/Assets/FishNet/Runtime/Object.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a4837ad94997ddf4d800cba09641c72a +folderAsset: yes +DefaultImporter: + externalObjects: {} + 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..d54114b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs @@ -0,0 +1 @@ +//Remove on V5 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta new file mode 100644 index 0000000..9b7c293 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1870202c019b99348aaedbe2029caf33 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/ChangedTransformProperties.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Editor.meta b/Assets/FishNet/Runtime/Object/Editor.meta new file mode 100644 index 0000000..83eb22c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0cd019316f743a94a8131c7c60d8ebd6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs b/Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs new file mode 100644 index 0000000..425cca1 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs @@ -0,0 +1,187 @@ +#if UNITY_EDITOR +using FishNet.Component.Transforming; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Object.Editing +{ + [CustomEditor(typeof(NetworkObject), true)] + [CanEditMultipleObjects] + public class NetworkObjectEditor : Editor + { + private SerializedProperty _isNetworked; + private SerializedProperty _isSpawnable; + private SerializedProperty _isGlobal; + private SerializedProperty _initializeOrder; + private SerializedProperty _preventDespawnOnDisconnect; + private SerializedProperty _defaultDespawnType; + + private SerializedProperty _enablePrediction; + private SerializedProperty _enableStateForwarding; + private SerializedProperty _networkTransform; + private SerializedProperty _predictionType; + private SerializedProperty _graphicalObject; + private SerializedProperty _detachGraphicalObject; + + private SerializedProperty _ownerSmoothedProperties; + private SerializedProperty _spectatorSmoothedProperties; + private SerializedProperty _ownerInterpolation; + private SerializedProperty _adaptiveInterpolation; + private SerializedProperty _spectatorInterpolation; + private SerializedProperty _enableTeleport; + private SerializedProperty _teleportThreshold; + + private int _tabIndex; + private int _savedTabIndex; + + private const string TAB_INDEX_PREFS_NAME = "FishNet_NetworkObject_TabIndex"; + + protected virtual void OnEnable() + { + _tabIndex = EditorPrefs.GetInt(TAB_INDEX_PREFS_NAME); + _savedTabIndex = _tabIndex; + + _isNetworked = serializedObject.FindProperty(nameof(_isNetworked)); + _isSpawnable = serializedObject.FindProperty(nameof(_isSpawnable)); + _isGlobal = serializedObject.FindProperty(nameof(_isGlobal)); + _initializeOrder = serializedObject.FindProperty(nameof(_initializeOrder)); + _preventDespawnOnDisconnect = serializedObject.FindProperty(nameof(_preventDespawnOnDisconnect)); + _defaultDespawnType = serializedObject.FindProperty(nameof(_defaultDespawnType)); + + _enablePrediction = serializedObject.FindProperty(nameof(_enablePrediction)); + _enableStateForwarding = serializedObject.FindProperty(nameof(_enableStateForwarding)); + _networkTransform = serializedObject.FindProperty(nameof(_networkTransform)); + _predictionType = serializedObject.FindProperty(nameof(_predictionType)); + _graphicalObject = serializedObject.FindProperty(nameof(_graphicalObject)); + _detachGraphicalObject = serializedObject.FindProperty(nameof(_detachGraphicalObject)); + + _ownerSmoothedProperties = serializedObject.FindProperty(nameof(_ownerSmoothedProperties)); + _ownerInterpolation = serializedObject.FindProperty(nameof(_ownerInterpolation)); + _adaptiveInterpolation = serializedObject.FindProperty(nameof(_adaptiveInterpolation)); + _spectatorSmoothedProperties = serializedObject.FindProperty(nameof(_spectatorSmoothedProperties)); + _spectatorInterpolation = serializedObject.FindProperty(nameof(_spectatorInterpolation)); + _enableTeleport = serializedObject.FindProperty(nameof(_enableTeleport)); + _teleportThreshold = serializedObject.FindProperty(nameof(_teleportThreshold)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + NetworkObject nob = (NetworkObject)target; + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(nob), typeof(NetworkObject), false); + GUI.enabled = true; + EditorGUILayout.Space(); + + _tabIndex = GUILayout.Toolbar(_tabIndex, new string[] { "Settings", "Prediction" }); + EditorGUILayout.Space(); + switch (_tabIndex) + { + case 0: + ShowSettingsTab(); + break; + case 1: + ShowPredictionTab(); + break; + default: + ShowSettingsTab(); + break; + } + + + void ShowSettingsTab() + { + SaveTabIndex(); + EditorGUILayout.PropertyField(_isNetworked); + EditorGUILayout.PropertyField(_isSpawnable); + EditorGUILayout.PropertyField(_isGlobal); + EditorGUILayout.PropertyField(_initializeOrder); + EditorGUILayout.PropertyField(_preventDespawnOnDisconnect); + EditorGUILayout.PropertyField(_defaultDespawnType); + } + + void ShowPredictionTab() + { + SaveTabIndex(); + EditorGUILayout.PropertyField(_enablePrediction); + if (_enablePrediction.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_predictionType); + EditorGUILayout.PropertyField(_enableStateForwarding); + if (_enableStateForwarding.boolValue == false) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_networkTransform); + EditorGUI.indentLevel--; + } + + EditorGUILayout.HelpBox("Smoothing settings on the NetworkObject will be obsoleted soon. Please unset the graphicalObject and use NetworkTickSmoother instead.", MessageType.Warning); + + bool graphicalSet = (_graphicalObject.objectReferenceValue != null); + EditorGUILayout.PropertyField(_graphicalObject); + if (graphicalSet) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_detachGraphicalObject); + EditorGUI.indentLevel--; + } + + EditorGUILayout.LabelField("Smoothing", EditorStyles.boldLabel); + if (!graphicalSet) + { + EditorGUILayout.HelpBox($"More smoothing settings will be displayed when a graphicalObject is set.", MessageType.Info); + } + else + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_enableTeleport); + if (_enableTeleport.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_teleportThreshold, new GUIContent("Teleport Threshold")); + EditorGUI.indentLevel--; + } + + EditorGUILayout.LabelField("Owner", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_ownerInterpolation, new GUIContent("Interpolation")); + EditorGUILayout.PropertyField(_ownerSmoothedProperties, new GUIContent("Smoothed Properties")); + EditorGUI.indentLevel--; + + EditorGUILayout.LabelField("Spectator", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_adaptiveInterpolation); + if (_adaptiveInterpolation.intValue == (int)AdaptiveInterpolationType.Off) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_spectatorInterpolation, new GUIContent("Interpolation")); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_spectatorSmoothedProperties, new GUIContent("Smoothed Properties")); + EditorGUI.indentLevel--; + } + + EditorGUI.indentLevel--; + } + } + + serializedObject.ApplyModifiedProperties(); + } + + /// + /// Saves tabIndex if it has changed. + /// + private void SaveTabIndex() + { + if (_tabIndex == _savedTabIndex) + return; + + _savedTabIndex = _tabIndex; + EditorPrefs.SetInt(TAB_INDEX_PREFS_NAME, _tabIndex); + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs.meta b/Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs.meta new file mode 100644 index 0000000..9540140 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b2d667433eee79d4e8661a84b1587b8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Editor/NetworkObjectEditor.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Helping.meta b/Assets/FishNet/Runtime/Object/Helping.meta new file mode 100644 index 0000000..0ffb171 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4af6267cb928f34fa47fdf8f80eccf9 +folderAsset: yes +DefaultImporter: + externalObjects: {} + 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..7465216 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs @@ -0,0 +1,40 @@ +using FishNet.Object.Helping; +using FishNet.Transporting; + +namespace FishNet.Object +{ + + #region Types. + /// + /// Lookup data for a RPC Link. + /// + internal readonly struct RpcLink + { + /// + /// ObjectId for link. + /// + public readonly int ObjectId; + /// + /// NetworkBehaviour component index on ObjectId. + /// + public readonly byte ComponentIndex; + /// + /// RpcHash for link. + /// + public readonly uint RpcHash; + /// + /// PacketId used for the Rpc type when not using links. + /// + public readonly PacketId RpcPacketId; + + public RpcLink(int objectId, byte componentIndex, uint rpcHash, PacketId packetId) + { + ObjectId = objectId; + ComponentIndex = componentIndex; + RpcHash = rpcHash; + RpcPacketId = packetId; + } + } + #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..a732025 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcLink.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 05a91745dd829d043aadf391ac7b233e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Helping/RpcLink.cs + uploadId: 762203 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..dac6874 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/RpcType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 09f9e7236f988c64fad54645f4cc7f8b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Helping/RpcType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs b/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs new file mode 100644 index 0000000..60df932 --- /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.IsServerStarted; + } + + /// + /// Returns if running as client. + /// + /// + /// + public static bool IsClient(NetworkBehaviour nb) + { + if (nb == null) + return false; + + return nb.IsClientStarted; + } + + } + + +} \ 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..6023dca --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 149cde0042627604d810c2c7fc0f9176 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Helping/StaticShortcuts.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour.meta new file mode 100644 index 0000000..1364b07 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aed4a4fd57ff1f44abf1397c968a94ae +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs new file mode 100644 index 0000000..83f2e95 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs @@ -0,0 +1,202 @@ +using FishNet.Managing.Logging; +using FishNet.Transporting; +using System; +using UnityEngine; + +namespace FishNet.Object +{ + public enum DataOrderType + { + /// + /// Data will buffer in the order originally intended. + /// EG: SyncTypes will always send last, and RPCs will always send in the order they were called. + /// + Default = 0, + /// + /// Data will be attached to the end of the packet. + /// RPCs can be sent after all SyncTypes by using this value. Multiple RPCs with this order type will send last, in the order they were called. + /// + Last = 1, + } + + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class RpcAttribute : Attribute + { + /// + /// 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; + /// + /// Order in which to send data for this RPC. + /// + public DataOrderType OrderType = DataOrderType.Default; + } + + /// + /// ServerRpc methods will send messages to the server. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ServerRpcAttribute : RpcAttribute + { + /// + /// True to only allow the owning client to call this RPC. + /// + public bool RequireOwnership = true; + /// + /// Type of logging to use when the IsServer check fails. + /// + public LoggingType Logging = LoggingType.Warning; + } + + /// + /// ObserversRpc methods will send messages to all observers. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class ObserversRpcAttribute : RpcAttribute + { + /// + /// 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; + /// + /// Type of logging to use when the IsServer check fails. + /// + public LoggingType Logging = LoggingType.Warning; + } + + /// + /// TargetRpc methods will send messages to a single client. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + public class TargetRpcAttribute : RpcAttribute + { + /// + /// True to prevent the connection from receiving this Rpc if they are also server. + /// + public bool ExcludeServer = 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; + /// + /// Type of logging to use when the IsServer check fails. + /// + public LoggingType Logging = LoggingType.Warning; + } + + /// + /// 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; + /// + /// False to prefer using networkObject.IsServer/ClientInitialized. True to use InstanceFinder.IsServer/ClientStarted. + /// + public bool UseIsStarted = false; + } + + /// + /// 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; + /// + /// False to prefer using networkObject.IsServer/ClientInitialized. True to use InstanceFinder.IsServer/ClientStarted. + /// + public bool UseIsStarted = 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. + /// + [Obsolete("This no longer functions. See console errors and Break Solutions in the documentation for resolution.")] + [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; + /// + /// Network roles which may update values. + /// + public WritePermission WritePermissions = WritePermission.ServerOnly; + /// + /// True if to require the readonly attribute. + /// Setting to false will allow inspector serialization of this object. When false you must still initialize this object on it's field declaration, but never anywhere else. + /// + public bool RequireReadOnly = true; + } + + /// + /// Synchronizes a variable from server to clients automatically. + /// Value must be changed on server. + /// + [Obsolete("This no longer functions. Use SyncVar instead. See console errors and Break Solutions in the documentation for resolution.")] + [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; + /// + /// Network roles which may update values. + /// + public WritePermission WritePermissions = WritePermission.ServerOnly; + /// + /// 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/NetworkBehaviour/Attributes.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs.meta new file mode 100644 index 0000000..089652c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e2c79ec60813585469c43b4539e3d0c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/Attributes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs new file mode 100644 index 0000000..59786e4 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs @@ -0,0 +1,13 @@ +using FishNet.Connection; +using FishNet.Serializing; +using FishNet.Transporting; +using FishNet.Utility; +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); + public delegate bool SyncVarReadDelegate(PooledReader reader, byte index, bool asServer); +} diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs.meta new file mode 100644 index 0000000..bf46625 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5dbd9cdda4843f34ab416273d80f83c8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/Delegates.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs new file mode 100644 index 0000000..19aed6a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/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/NetworkBehaviour/EmptyNetworkBehaviour.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs.meta new file mode 100644 index 0000000..84b0874 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8a6a39c46bf52104ba8efe3100bce3f7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/EmptyNetworkBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs new file mode 100644 index 0000000..30ebabe --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs @@ -0,0 +1,211 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Runtime.CompilerServices; +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 + + /* Payloads are written and read immediatley after the header containing the target NetworkObject/Behaviour. */ + /// + /// Called when writing a spawn. This may be used to deliver information for predicted spawning, or simply have values set before initialization without depending on SyncTypes. + /// + /// Connection receiving the payload. When sending to the server connection.IsValid will return false. + public virtual void WritePayload(NetworkConnection connection, Writer writer) { } + + /// + /// Called before network start callbacks, but after the object is initialized with network values. This may be used to read information from predicted spawning, or simply have values set before initialization without depending on SyncTypes. + /// + /// Connection sending the payload. When coming from the server connection.IsValid will return false. + public virtual void ReadPayload(NetworkConnection connection, Reader reader) { } + + /// + /// Invokes OnStartXXXX for synctypes, letting them know the NetworkBehaviour start cycle has been completed. + /// + internal void InvokeSyncTypeOnStartCallbacks(bool asServer) + { + foreach (SyncBase item in _syncTypes.Values) + item.OnStartCallback(asServer); + } + + /// + /// Invokes OnStopXXXX for synctypes, letting them know the NetworkBehaviour stop cycle is about to start. + /// + internal void InvokeSyncTypeOnStopCallbacks(bool asServer) + { + // if (_syncTypes == null) + // return; + foreach (SyncBase item in _syncTypes.Values) + item.OnStopCallback(asServer); + } + + /// + /// Invokes the OnStart/StopNetwork. + /// + internal void InvokeOnNetwork_Internal(bool start) + { + if (start) + { + if (_onStartNetworkCalled) + return; + + if (!gameObject.activeInHierarchy) + { + NetworkInitialize___Early(); + NetworkInitialize___Late(); + } + OnStartNetwork_Internal(); + } + else + { + if (_onStopNetworkCalled) + return; + OnStopNetwork_Internal(); + } + } + + internal virtual void OnStartNetwork_Internal() + { + _onStartNetworkCalled = true; + _onStopNetworkCalled = false; + OnStartNetwork(); + } + + /// + /// 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. + /// + public virtual void OnStartNetwork() { } + + internal virtual void OnStopNetwork_Internal() + { + _onStopNetworkCalled = true; + _onStartNetworkCalled = false; + + OnStopNetwork(); + } + + /// + /// 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. + /// + public virtual void OnStopNetwork() { } + + internal void OnStartServer_Internal() + { + OnStartServerCalled = true; + OnStartServer(); + } + + /// + /// Called on the server after initializing this object. + /// SyncTypes modified before or during this method will be sent to clients in the spawn message. + /// + public virtual void OnStartServer() { } + + internal void OnStopServer_Internal() + { + OnStartServerCalled = false; + ReturnRpcLinks(); + OnStopServer(); + } + + /// + /// Called on the server before deinitializing this object. + /// + public virtual void OnStopServer() { } + + internal void OnOwnershipServer_Internal(NetworkConnection prevOwner) + { + ResetState_Prediction(true); + OnOwnershipServer(prevOwner); + } + + /// + /// Called on the server after ownership has changed. + /// + /// Previous owner of this object. + public virtual void OnOwnershipServer(NetworkConnection prevOwner) { } + + /// + /// 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. + 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. + /// + public virtual void OnDespawnServer(NetworkConnection connection) { } + + internal void OnStartClient_Internal() + { + OnStartClientCalled = true; + OnStartClient(); + } + + /// + /// Called on the client after initializing this object. + /// + public virtual void OnStartClient() { } + + internal void OnStopClient_Internal() + { + OnStartClientCalled = false; + OnStopClient(); + } + + /// + /// Called on the client before deinitializing this object. + /// + public virtual void OnStopClient() { } + + internal void OnOwnershipClient_Internal(NetworkConnection prevOwner) + { + //If losing or gaining ownership then clear replicate cache. + if (IsOwner || prevOwner == LocalConnection) + { + ResetState_Prediction(false); + } + + OnOwnershipClient(prevOwner); + } + + /// + /// Called on the client after gaining or losing ownership. + /// + /// Previous owner of this object. + public virtual void OnOwnershipClient(NetworkConnection prevOwner) { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs.meta new file mode 100644 index 0000000..10f638f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a9ddaf08801752b49bcfe720217df74a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Callbacks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs new file mode 100644 index 0000000..3685e21 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs @@ -0,0 +1,23 @@ +using FishNet.Managing; +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.CanLog(loggingType); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs.meta new file mode 100644 index 0000000..0eb644d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ad4dd2795a9a00e4d814892ac1a67157 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Logging.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs new file mode 100644 index 0000000..fba73f1 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs @@ -0,0 +1,1515 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.CodeGenerating; +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Predicting; +using FishNet.Managing.Server; +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; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] + +namespace FishNet.Object +{ + #region Types. + internal static class ReplicateTickFinder + { + public enum DataPlacementResult + { + /// + /// Something went wrong; this should never be returned. + /// + Error, + /// + /// Tick was found on an index. + /// + Exact, + /// + /// Tick was not found because it is lower than any of the replicates. + /// This is also used when there are no datas. + /// + InsertBeginning, + /// + /// Tick was not found but can be inserted in the middle of the collection. + /// + InsertMiddle, + /// + /// Tick was not found because it is larger than any of the replicates. + /// + InsertEnd, + } + + /// + /// Gets the index in replicates where the tick matches. + /// + public static int GetReplicateHistoryIndex(uint tick, RingBuffer> replicatesHistory, out DataPlacementResult findResult) where T : IReplicateData, new() + { + int replicatesCount = replicatesHistory.Count; + if (replicatesCount == 0) + { + findResult = DataPlacementResult.InsertBeginning; + return 0; + } + + uint firstTick = replicatesHistory[0].Data.GetTick(); + + //Try to find by skipping ahead the difference between tick and start. + int diff = (int)(tick - firstTick); + /* If the difference is larger than replicatesCount + * then that means the replicates collection is missing + * entries. EG if replicates values were 4, 7, 10 and tick were + * 10 the difference would be 6. While replicates does contain the value + * there is no way it could be found by pulling index 'diff' since that + * would be out of bounds. This should never happen under normal conditions, return + * missing if it does. */ + //Do not need to check less than 0 since we know if here tick is larger than first entry. + if (diff >= replicatesCount) + { + //Try to return value using brute force. + int index = FindIndexBruteForce(out findResult); + return index; + } + else if (diff < 0) + { + findResult = DataPlacementResult.InsertBeginning; + return 0; + } + else + { + /* If replicatesHistory contained the ticks + * of 1 2 3 4 5, and the tick is 3, then the difference + * would be 2 (because 3 - 1 = 2). As we can see index + * 2 of replicatesHistory does indeed return the proper tick. */ + //Expected diff to be result but was not. + if (replicatesHistory[diff].Data.GetTick() != tick) + { + //Try to return value using brute force. + int index = FindIndexBruteForce(out findResult); + return index; + } + //Exact was found, this is the most ideal situation. + else + { + findResult = DataPlacementResult.Exact; + return diff; + } + } + + //Tries to find the index by brute forcing the collection. + int FindIndexBruteForce(out DataPlacementResult result) + { + /* Some quick exits to save perf. */ + //If tick is lower than first then it must be inserted at the beginning. + if (tick < firstTick) + { + result = DataPlacementResult.InsertBeginning; + return 0; + } + //If tick is larger the last then it must be inserted at the end. + else if (tick > replicatesHistory[replicatesCount - 1].Data.GetTick()) + { + result = DataPlacementResult.InsertEnd; + return replicatesCount; + } + else + { + //Brute check. + for (int i = 0; i < replicatesCount; i++) + { + uint lTick = replicatesHistory[i].Data.GetTick(); + //Exact match found. + if (lTick == tick) + { + result = DataPlacementResult.Exact; + return i; + } + /* The checked data is greater than + * what was being searched. This means + * to insert right before it. */ + else if (lTick > tick) + { + result = DataPlacementResult.InsertMiddle; + return i; + } + } + + //Should be impossible to get here. + result = DataPlacementResult.Error; + return -1; + } + } + } + } + + //See todo below. + /* Update codegen to remove arrBuffer from replicate method calls. + * Update codegen to remove channel from replicate method calls where applicable. + * Convert BasicQueue/RingBuffer to BasicQueue/RingBuffer>. + * */ + #endregion + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Public. + /// + /// True if this NetworkBehaviour is reconciling. + /// If this NetworkBehaviour does not implemnent prediction methods this value will always be false. + /// Value will be false if there is no data to reconcile to, even if the PredictionManager IsReconciling. + /// Data may be missing if it were intentionally not sent, or due to packet loss. + /// + public bool IsBehaviourReconciling { get; internal set; } + #endregion + + #region Private. + /// + /// Registered Replicate methods. + /// + private Dictionary _replicateRpcDelegates; + /// + /// Registered Reconcile methods. + /// + private Dictionary _reconcileRpcDelegates; + /// + /// Number of replicate resends which may occur. + /// + private int _remainingReplicateResends; + /// + /// Number of reconcile resends which may occur. + /// + private int _remainingReconcileResends; + /// + /// Last replicate tick read from remote. This can be the server reading a client or the other way around. + /// + private uint _lastReplicateReadRemoteTick = TimeManager.UNSET_TICK; + /// + /// Tick when replicates should begun to run. This is set and used when inputs are just received and need to queue to create a buffer. + /// + private uint _replicateStartTick = TimeManager.UNSET_TICK; + /// + /// Last tick to replicate which was not replayed. + /// + private uint _lastOrderedReplicatedTick = TimeManager.UNSET_TICK; + /// + /// Last tick read for a replicate. + /// + private uint _lastReadReplicateTick = TimeManager.UNSET_TICK; + /// + /// Last tick read for a reconcile. This is only set on the client. + /// + private uint _lastReadReconcileRemoteTick = TimeManager.UNSET_TICK; + /// + /// Last tick this object reconciled on. + /// + private uint _lastReconcileTick = TimeManager.UNSET_TICK; + /// + /// Last values when checking for transform changes since previous tick. + /// + private Vector3 _lastTransformPosition; + /// + /// Last values when checking for transform changes since previous tick. + /// + private Quaternion _lastTransformRotation; + /// + /// Last values when checking for transform changes since previous tick. + /// + private Vector3 _lastTransformScale; + /// + /// True if this Networkbehaviour implements prediction methods. + /// + [APIExclude] + private bool _usesPrediction; + #endregion + + /// + /// Initializes the NetworkBehaviour for prediction. + /// + internal void Preinitialize_Prediction(bool asServer) { } + + /// + /// Deinitializes the NetworkBehaviour for prediction. + /// + internal void Deinitialize_Prediction(bool asServer) { } + + /// + /// Called when the object is destroyed. + /// + internal void OnDestroy_Prediction() + { + CollectionCaches.StoreAndDefault(ref _replicateRpcDelegates); + CollectionCaches.StoreAndDefault(ref _reconcileRpcDelegates); + } + + /// + /// Registers a RPC method. + /// Internal use. + /// + /// + /// + [MakePublic] + internal void RegisterReplicateRpc(uint hash, ReplicateRpcDelegate del) + { + _usesPrediction = true; + + if (_replicateRpcDelegates == null) + _replicateRpcDelegates = CollectionCaches.RetrieveDictionary(); + _replicateRpcDelegates[hash] = del; + } + + /// + /// Registers a RPC method. + /// Internal use. + /// + /// + /// + [MakePublic] + internal void RegisterReconcileRpc(uint hash, ReconcileRpcDelegate del) + { + if (_reconcileRpcDelegates == null) + _reconcileRpcDelegates = CollectionCaches.RetrieveDictionary(); + _reconcileRpcDelegates[hash] = del; + } + + /// + /// Called when a replicate is received. + /// + internal void OnReplicateRpc(uint? methodHash, PooledReader reader, NetworkConnection sendingClient, Channel channel) + { + if (methodHash == null) + methodHash = ReadRpcHash(reader); + + reader.NetworkManager = _networkObjectCache.NetworkManager; + + 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. + /// + internal void OnReconcileRpc(uint? methodHash, PooledReader reader, Channel channel) + { + if (methodHash == null) + methodHash = ReadRpcHash(reader); + + reader.NetworkManager = _networkObjectCache.NetworkManager; + + 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."); + } + + /// + /// Resets cached ticks used by prediction, such as last read and replicate tick. + /// This is generally used when the ticks will be different then what was previously used; eg: when ownership changes. + /// + private void ResetState_Prediction(bool asServer) + { + if (!asServer) + { + _lastReadReconcileRemoteTick = TimeManager.UNSET_TICK; + _lastReconcileTick = TimeManager.UNSET_TICK; + } + + _lastOrderedReplicatedTick = TimeManager.UNSET_TICK; + _lastReplicateReadRemoteTick = TimeManager.UNSET_TICK; + _lastReadReplicateTick = TimeManager.UNSET_TICK; + + ClearReplicateCache(); + } + + /// + /// Clears cached replicates for server and client. This can be useful to call on server and client after teleporting. + /// + public virtual void ClearReplicateCache() { } + + /// + /// Clears cached replicates and histories. + /// + [MakePublic] + internal void ClearReplicateCache_Internal(BasicQueue> replicatesQueue, RingBuffer> replicatesHistory, RingBuffer> reconcilesHistory, ref T lastReadReplicate, ref T2 lastReadReconcile) where T : IReplicateData, new() where T2 : IReconcileData, new() + { + while (replicatesQueue.Count > 0) + { + ReplicateDataContainer dataContainer = replicatesQueue.Dequeue(); + dataContainer.Dispose(); + } + + if (lastReadReplicate != null) + lastReadReplicate.Dispose(); + lastReadReplicate = default; + + if (lastReadReconcile != null) + lastReadReconcile.Dispose(); + lastReadReconcile = default; + + for (int i = 0; i < replicatesHistory.Count; i++) + { + ReplicateDataContainer dataContainer = replicatesHistory[i]; + dataContainer.Dispose(); + } + replicatesHistory.Clear(); + + ClearReconcileHistory(reconcilesHistory); + } + + /// + /// Sends a RPC to target. + /// Internal use. + /// + [MakePublic] + private void Server_SendReconcileRpc(uint hash, ref T lastReconcileData, T reconcileData, Channel channel) where T : IReconcileData + { + if (!IsSpawned) + return; + + //If channel is reliable set remaining resends to 1. + if (channel == Channel.Reliable) + _remainingReconcileResends = 1; + + if (_remainingReconcileResends == 0) + return; + _remainingReconcileResends--; + + //No owner and no state forwarding, nothing to do. + bool stateForwarding = _networkObjectCache.EnableStateForwarding; + if (!Owner.IsValid && !stateForwarding) + return; + + /* Set the channel for Rpcs to reliable to that the length + * is written. The data does not actually send reliable, unless + * the channel is of course that to start. */ + /* This is a temporary solution to resolve an issue which was + * causing parsing problems due to states sending unreliable and reliable + * headers being written, or sending reliably and unreliable headers being written. + * Using an extra byte to write length is more preferred than always forcing reliable + * until properly resolved. */ + const Channel rpcChannel = Channel.Reliable; + + PooledWriter methodWriter = WriterPool.Retrieve(); + /* Tick does not need to be written because it will always + * be the localTick of the server. For the clients, this will + * be the LastRemoteTick of the packet. + * + * The exception is for the owner, which we send the last replicate + * tick so the owner knows which to roll back to. */ + +//#if !FISHNET_STABLE_SYNCTYPES +#if DO_NOT_USE + methodWriter.WriteDeltaReconcile(lastReconcileData, reconcileData, GetDeltaSerializeOption()); +#else + methodWriter.WriteReconcile(reconcileData); +#endif + lastReconcileData = reconcileData; + + PooledWriter writer; +#if DEVELOPMENT + if (!NetworkManager.DebugManager.DisableReconcileRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#else + if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#endif + writer = CreateLinkedRpc(link, methodWriter, rpcChannel); + else + writer = CreateRpc(hash, methodWriter, PacketId.Reconcile, rpcChannel); + + //If state forwarding is not enabled then only send to owner. + if (!stateForwarding) + { + Owner.WriteState(writer); + } + //State forwarding, send to all. + else + { + foreach (NetworkConnection nc in Observers) + nc.WriteState(writer); + } + + methodWriter.Store(); + writer.Store(); + } + + /// + /// Returns if there is a chance the transform may change after the tick. + /// + /// + private bool TransformChanged() + { + if (TimeManager.PhysicsMode == PhysicsMode.Disabled) + return false; + + /* 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 anyChanged = false; + anyChanged |= (transform.position - _lastTransformPosition).sqrMagnitude > changeDistance; + if (!anyChanged) + anyChanged |= (transform.rotation.eulerAngles - _lastTransformRotation.eulerAngles).sqrMagnitude > changeDistance; + if (!anyChanged) + anyChanged |= (transform.localScale - _lastTransformScale).sqrMagnitude > changeDistance; + + //If transform changed update last values. + if (anyChanged) + { + _lastTransformPosition = transform.position; + _lastTransformRotation = transform.rotation; + _lastTransformScale = transform.localScale; + } + + return anyChanged; + } + + /// + /// Performs a replicate for current tick. + /// + [MakePublic] + internal void Replicate_Current(ReplicateUserLogicDelegate del, uint methodHash, BasicQueue> replicatesQueue, RingBuffer> replicatesHistory, ReplicateDataContainer dataContainer) where T : IReplicateData, new() + { + /* Do not run if currently reconciling. + * This change allows devs to call inherited replicates + * from replays to only run the method logic without + * prompting for network action. */ + if (_networkObjectCache.PredictionManager.IsReconciling) + return; + + if (_networkObjectCache.IsController) + Replicate_Authoritative(del, methodHash, replicatesHistory, dataContainer); + else + Replicate_NonAuthoritative(del, replicatesQueue, replicatesHistory); + } + + /// + /// Returns if a replicates data changed and updates resends as well data tick. + /// + /// True to enqueue data for replaying. + /// True if data has changed.. + private void Replicate_Authoritative(ReplicateUserLogicDelegate del, uint methodHash, RingBuffer> replicatesHistory, ReplicateDataContainer dataContainer) where T : IReplicateData, new() + { + bool ownerlessAndServer = (!Owner.IsValid && IsServerStarted); + if (!IsOwner && !ownerlessAndServer) + return; + + Func isDefaultDel = PublicPropertyComparer.IsDefault; + if (isDefaultDel == null) + { + NetworkManager.LogError($"{nameof(PublicPropertyComparer)} not found for type {typeof(T).FullName}"); + return; + } + + PredictionManager pm = NetworkManager.PredictionManager; + uint dataTick = TimeManager.LocalTick; + + /* The following code is to remove replicates from replicatesHistory + * which exceed the buffer allowance. Replicates are kept for up to + * x seconds to clients can re-run them during a reconcile. The reconcile + * method removes old histories but given the server does not reconcile, + * it will never perform that operation. + * The server would not actually need to keep replicates history except + * when it is also client(clientHost). This is because the clientHost must + * send redundancies to other clients still, therefor that redundancyCount + * must be the allowance when clientHost. */ + if (IsHostStarted) + { + int replicatesHistoryCount = replicatesHistory.Count; + int maxCount = pm.RedundancyCount; + //Number to remove which is over max count. + int removeCount = (replicatesHistoryCount - maxCount); + //If there are any to remove. + if (removeCount > 0) + { + //Dispose first. + for (int i = 0; i < removeCount; i++) + replicatesHistory[i].Dispose(); + + //Then remove range. + replicatesHistory.RemoveRange(true, removeCount); + } + } + + dataContainer.SetDataTick(dataTick); + AddReplicatesHistory(replicatesHistory, dataContainer); + + //Check to reset resends. + bool isDefault = isDefaultDel.Invoke(dataContainer.Data); + bool resetResends = (!isDefault || TransformChanged()); + + byte redundancyCount = PredictionManager.RedundancyCount; + + //Standard delta serialize option. + //+1 to redundancy so lastFirstRead is pushed out to the last actual input when server reads. + if (resetResends) + { + _remainingReplicateResends = redundancyCount; + _remainingReconcileResends = redundancyCount; + } + + bool sendData = (_remainingReplicateResends > 0); + if (sendData) + { + /* If not server then send to server. + * If server then send to clients. */ + bool toServer = !IsServerStarted; + Replicate_SendAuthoritative(toServer, methodHash, redundancyCount, replicatesHistory, dataTick, dataContainer.Channel, GetDeltaSerializeOption()); + _remainingReplicateResends--; + } + + SetReplicateTick(dataTick, createdReplicate: true); + +#if !FISHNET_STABLE_REPLICATESTATES + //Owner always replicates with new data. + del.Invoke(dataContainer.Data, (ReplicateState.Ticked | ReplicateState.Created), dataContainer.Channel); +#else + del.Invoke(dataContainer.Data, ReplicateState.CurrentCreated, dataContainer.Channel); +#endif + } + + /// + /// Gets the next replicate in perform when server or non-owning client. + /// + /// + private void Replicate_NonAuthoritative(ReplicateUserLogicDelegate del, BasicQueue> replicatesQueue, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + bool serverStarted = _networkObjectCache.IsServerStarted; + bool ownerlessAndServer = (!Owner.IsValid && serverStarted); + if (IsOwner || ownerlessAndServer) + return; + /* Still need to run inputs if server, even if forwarding + * is not enabled.*/ + if (!_networkObjectCache.EnableStateForwarding && !serverStarted) + return; + + TimeManager tm = _networkObjectCache.TimeManager; + PredictionManager pm = _networkObjectCache.PredictionManager; + uint localTick = tm.LocalTick; + bool isServer = _networkObjectCache.IsServerStarted; + bool isAppendedOrder = pm.IsAppendedStateOrder; + + //Server is initialized or appended state order. + if (isServer || isAppendedOrder) + { + int count = replicatesQueue.Count; + /* If count is 0 then data must be set default + * and as predicted. */ + if (count == 0) + { + ReplicateDefaultData(); + } + //Not predicted, is user created. + else + { + //Check to unset start tick, which essentially voids it resulting in inputs being run immediately. + /* As said above, if start tick is unset then replicates + * can run. When still set that means the start condition has + * not been met yet. */ + if (localTick >= _replicateStartTick) + { + _replicateStartTick = TimeManager.UNSET_TICK; + ReplicateDataContainer queueEntry; + bool queueEntryValid = false; + while (replicatesQueue.TryDequeue(out queueEntry)) + { + if (queueEntry.Data.GetTick() > _lastReconcileTick) + { + queueEntryValid = true; + break; + } + } + + if (queueEntryValid) + { + _remainingReconcileResends = pm.RedundancyCount; + +#if !FISHNET_STABLE_REPLICATESTATES + ReplicateData(queueEntry, (ReplicateState.Ticked | ReplicateState.Created)); +#else + ReplicateData(queueEntry, ReplicateState.CurrentCreated); +#endif + + //Update count since old entries were dropped and one replicate run. + count = replicatesQueue.Count; + + bool consumeExcess = (!pm.DropExcessiveReplicates || IsClientOnlyStarted); + int leaveInBuffer = _networkObjectCache.PredictionManager.StateInterpolation; + + //Only consume if the queue count is over leaveInBuffer. + if (consumeExcess && count > leaveInBuffer) + { + const byte maximumAllowedConsumes = 1; + int maximumPossibleConsumes = (count - leaveInBuffer); + int consumeAmount = Mathf.Min(maximumAllowedConsumes, maximumPossibleConsumes); + + for (int i = 0; i < consumeAmount; i++) +#if !FISHNET_STABLE_REPLICATESTATES + ReplicateData(replicatesQueue.Dequeue(), (ReplicateState.Ticked | ReplicateState.Created)); +#else + ReplicateData(replicatesQueue.Dequeue(), ReplicateState.CurrentCreated); +#endif + } + } + } + //Not enough ticks passed yet to run actually data. + else + { + ReplicateDefaultData(); + } + } + } + //Is client only and not using future state order. + else + { + ReplicateDefaultData(); + } + + //Performs a replicate using default data. + void ReplicateDefaultData() + { + uint tick = (GetDefaultedLastReplicateTick() + 1); + ReplicateDataContainer dataContainer = ReplicateDataContainer.GetDefault(tick); +#if !FISHNET_STABLE_REPLICATESTATES + ReplicateData(dataContainer, ReplicateState.Ticked); +#else + ReplicateData(dataContainer, ReplicateState.CurrentFuture); +#endif + } + + void ReplicateData(ReplicateDataContainer data, ReplicateState state) + { + uint tick = data.Data.GetTick(); +#if !FISHNET_STABLE_REPLICATESTATES + SetReplicateTick(tick, state.ContainsCreated()); +#else + SetReplicateTick(tick, (state == ReplicateState.CurrentCreated)); +#endif + /* If server or appended state order then insert/add to history when run + * within this method. + * Whether data is inserted/added into the past (replicatesHistory) depends on + * if client only && and state order. + * + * Server only adds onto the history after running the inputs. This is so + * the server can send past inputs with redundancy. + * + * Client inserts into the history under two scenarios: + * - If state order is using inserted. This is done when the data is read so it + * can be iterated during the next reconcile, since the data is not added to + * a queue otherwise. This is what causes the requirement to reconcile to run + * datas. + * - If the state order if using append, and the state just ran. This is so that + * the reconcile does not replay data which hasn't yet run. But, the data should still + * be inserted at point of run so reconciles can correct to the state at the right + * point in history.*/ + + //Server always adds. + if (isServer) + AddReplicatesHistory(replicatesHistory, data); + //If client insert value into history. + else + InsertIntoReplicateHistory(data, replicatesHistory); + + del.Invoke(data.Data, state, data.Channel); + } + + //Returns a replicate tick for when data is not created. + uint GetDefaultedLastReplicateTick() + { + if (_lastOrderedReplicatedTick == TimeManager.UNSET_TICK) + _lastOrderedReplicatedTick = (tm.LastPacketTick.Value() + pm.StateInterpolation); + + return _lastOrderedReplicatedTick; + } + } + + /// + /// Called internally when an input from localTick should be replayed. + /// + internal virtual void Replicate_Replay_Start(uint replayTick) { } + + /// + /// Replays inputs from replicates. + /// + /// The server calls this from codegen but it never completes as IsBehaviourReconciling will always be false on server. + [MakePublic] + internal void Replicate_Replay(uint replayTick, ReplicateUserLogicDelegate del, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + //Reconcile data was not received so cannot replay. + if (!IsBehaviourReconciling) + return; + + if (_networkObjectCache.IsController) + Replicate_Replay_Authoritative(replayTick, del, replicatesHistory); + else + Replicate_Replay_NonAuthoritative(replayTick, del, replicatesHistory); + } + + /// + /// Replays an input for authoritative entity. + /// + private void Replicate_Replay_Authoritative(uint replayTick, ReplicateUserLogicDelegate del, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + ReplicateTickFinder.DataPlacementResult findResult; + int replicateIndex = ReplicateTickFinder.GetReplicateHistoryIndex(replayTick, replicatesHistory, out findResult); + + ReplicateDataContainer dataContainer; + ReplicateState state; + //If found then the replicate has been received by the server. + if (findResult == ReplicateTickFinder.DataPlacementResult.Exact) + { + dataContainer = replicatesHistory[replicateIndex]; +#if !FISHNET_STABLE_REPLICATESTATES + state = (ReplicateState.Replayed | ReplicateState.Ticked | ReplicateState.Created); +#else + state = ReplicateState.ReplayedCreated; +#endif + + //SetReplicateTick(data.GetTick(), true); + del.Invoke(dataContainer.Data, state, dataContainer.Channel); + } + } + + /// + /// Replays an input for non authoritative entity. + /// + [MakePublic] + private void Replicate_Replay_NonAuthoritative(uint replayTick, ReplicateUserLogicDelegate del, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + + ReplicateDataContainer dataContainer; + ReplicateState state; + bool isAppendedOrder = _networkObjectCache.PredictionManager.IsAppendedStateOrder; + //If the first replay. + if (isAppendedOrder || replayTick == (_networkObjectCache.PredictionManager.ServerStateTick + 1)) + { + ReplicateTickFinder.DataPlacementResult findResult; + int replicateIndex = ReplicateTickFinder.GetReplicateHistoryIndex(replayTick, replicatesHistory, out findResult); + //If not found then something went wrong. + if (findResult == ReplicateTickFinder.DataPlacementResult.Exact) + { + dataContainer = replicatesHistory[replicateIndex]; + +#if !FISHNET_STABLE_REPLICATESTATES + state = ReplicateState.Replayed; + + bool isCreated = dataContainer.IsCreated; + //Set if created. + if (isCreated) + state |= ReplicateState.Created; + /* Ticked will be true if value had ticked outside of reconcile, + * or if data is created. It's possible for data to be created + * and not yet ticked if state order is inserted rather than append. */ + if (replayTick <= _lastOrderedReplicatedTick || isCreated) + state |= ReplicateState.Ticked; +#else + //state = ReplicateState.ReplayedCreated; + state = (dataContainer.IsCreated) ? ReplicateState.ReplayedCreated : ReplicateState.ReplayedFuture; +#endif + } + else + { + SetDataToDefault(); + } + } + //Not the first replay tick. + else + { + SetDataToDefault(); + } + + void SetDataToDefault() + { + dataContainer = ReplicateDataContainer.GetDefault(replayTick); +#if !FISHNET_STABLE_REPLICATESTATES + state = ReplicateState.Replayed; +#else + state = ReplicateState.ReplayedFuture; +#endif + } + + del.Invoke(dataContainer.Data, state, dataContainer.Channel); + } + + /// + /// This is overriden by codegen to call EmptyReplicatesQueueIntoHistory(). + /// This should only be called when client only. + /// + [MakePublic] + internal virtual void EmptyReplicatesQueueIntoHistory_Start() { } + + /// + /// Replicates which are enqueued will be removed from the queue and put into replicatesHistory. + /// This should only be called when client only. + /// + [MakePublic] + internal void EmptyReplicatesQueueIntoHistory(BasicQueue> replicatesQueue, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + while (replicatesQueue.TryDequeue(out ReplicateDataContainer data)) + InsertIntoReplicateHistory(data, replicatesHistory); + } + + /// + /// Returns the DeltaSerializeOption to use for the tick. + /// + /// + /// + private DeltaSerializerOption GetDeltaSerializeOption() + { + uint localTick = _networkObjectCache.TimeManager.LocalTick; + ushort tickRate = _networkObjectCache.TimeManager.TickRate; + /* New observers so send a full serialize next replicate. + * This could go out to only the newly added observers, but it + * would generate a lot more complexity to save presumably + * a small amount of occasional bandwidth. */ + if (_networkObjectCache.ObserverAddedTick == localTick) + return DeltaSerializerOption.FullSerialize; + //Send full every half a second. + //else if (localTick % tickRate == 0 || localTick % (tickRate / 2) == 0) + // return DeltaSerializerOption.FullSerialize; + //Send full every second. + else if (localTick % tickRate == 0) + return DeltaSerializerOption.FullSerialize; + //Otherwise return rootSerialize, the default for sending the child most data. + else + return DeltaSerializerOption.RootSerialize; + } + + /// + /// Sends a Replicate to server or clients. + /// + private void Replicate_SendAuthoritative(bool toServer, uint hash, int pastInputs, RingBuffer> replicatesHistory, uint queuedTick, Channel channel, DeltaSerializerOption deltaOption) where T : IReplicateData, new() + { + /* Do not use IsSpawnedWithWarning because the server + * will still call this a tick or two as clientHost when + * an owner disconnects. This comes from calling Replicate(default) + * for the server-side processing in NetworkBehaviours. */ + if (!IsSpawned) + return; + + int historyCount = replicatesHistory.Count; + //Nothing to send; should never be possible. + if (historyCount <= 0) + return; + + //Number of past inputs to send. + if (historyCount < pastInputs) + pastInputs = historyCount; + /* Where to start writing from. When passed + * into the writer values from this offset + * and forward will be written. + * Always write up to past inputs. */ + int offset = (historyCount - pastInputs); + + //Write history to methodWriter. + PooledWriter methodWriter = WriterPool.Retrieve(WriterPool.LENGTH_BRACKET); + /* If going to clients from the server then + * write the queueTick. */ + if (!toServer) + methodWriter.WriteTickUnpacked(queuedTick); +//#if !FISHNET_STABLE_SYNCTYPES +#if DO_NOT_USE + methodWriter.WriteDeltaReplicate(replicatesHistory, offset, deltaOption); +#else + methodWriter.WriteReplicate(replicatesHistory, offset); +#endif + _transportManagerCache.CheckSetReliableChannel(methodWriter.Length + MAXIMUM_RPC_HEADER_SIZE, ref channel); + PooledWriter writer = CreateRpc(hash, methodWriter, PacketId.Replicate, channel); + + /* toServer will never be true if clientHost. + * When clientHost and here replicates will + * always just send to clients, while + * excluding clientHost. */ + if (toServer) + { + NetworkManager.TransportManager.SendToServer((byte)channel, writer.GetArraySegment(), splitLargeMessages: true); + } + else + { + /* If going to clients from server, then only send + * if state forwarding is enabled. */ + if (_networkObjectCache.EnableStateForwarding) + { + //Exclude owner and if clientHost, also localClient. + _networkConnectionCache.Clear(); + _networkConnectionCache.Add(Owner); + if (IsClientStarted) + _networkConnectionCache.Add(ClientManager.Connection); + + NetworkManager.TransportManager.SendToClients((byte)channel, writer.GetArraySegment(), Observers, _networkConnectionCache, splitLargeMessages: true); + } + } + + /* If sending as reliable there is no reason + * to perform resends, so clear remaining resends. */ + if (channel == Channel.Reliable) + _remainingReplicateResends = 0; + + methodWriter.StoreLength(); + writer.StoreLength(); + } + + /// + /// Reads a replicate the client. + /// + [MakePublic] + internal void Replicate_Reader(uint hash, PooledReader reader, NetworkConnection sender, ref ReplicateDataContainer lastReadReplicate, BasicQueue> replicatesQueue, RingBuffer> replicatesHistory, Channel channel) where T : IReplicateData, new() + { + /* This will never be received on owner, except in the condition + * the server is the owner and also a client. In such condition + * the method is exited after data is parsed. */ + PredictionManager pm = _networkObjectCache.PredictionManager; + TimeManager tm = _networkObjectCache.TimeManager; + bool fromServer = (reader.Source == Reader.DataSource.Server); + + uint tick; + /* If coming from the server then read the tick. Server sends tick + * if authority or if relaying from another client. The tick which + * arrives will be the tick the replicate will run on the server. */ + if (fromServer) + tick = reader.ReadTickUnpacked(); + /* When coming from a client it will always be owner. + * Client sends out replicates soon as they are run. + * It's safe to use the LastRemoteTick from the client + * in addition to QueuedInputs. */ + else + tick = (tm.LastPacketTick.LastRemoteTick); + +//#if !FISHNET_STABLE_SYNCTYPES +#if DO_NOT_USE + receivedReplicatesCount = reader.ReadDeltaReplicate(lastReadReplicate, ref arrBuffer, tick); +#else + List> readReplicates = reader.ReadReplicate(tick); +#endif + //Update first read if able. + if (readReplicates.Count > 0) + { + lastReadReplicate.Dispose(); + lastReadReplicate = readReplicates[^1]; + } + + //If received on clientHost simply ignore after parsing data. + if (fromServer && IsHostStarted) + return; + + /* Replicate rpc readers relay to this method and + * do not have an owner check in the generated code. + * Only server needs to check for owners. Clients + * should accept the servers data regardless. + * + * If coming from a client and that client is not owner then exit. */ + if (!fromServer && !OwnerMatches(sender)) + return; + //Early exit if old data. + if (TimeManager.LastPacketTick.LastRemoteTick < _lastReplicateReadRemoteTick) + return; + _lastReplicateReadRemoteTick = TimeManager.LastPacketTick.LastRemoteTick; + + //If from a client that is not clientHost do some safety checks. + if (!fromServer && !Owner.IsLocalClient) + { + if (readReplicates.Count > pm.RedundancyCount) + { + sender.Kick(reader, KickReason.ExploitAttempt, LoggingType.Common, $"Connection {sender.ToString()} sent too many past replicates. Connection will be kicked immediately."); + return; + } + } + + Replicate_EnqueueReceivedReplicate(readReplicates, replicatesQueue, replicatesHistory); + Replicate_SendNonAuthoritative(hash, replicatesQueue, channel); + + CollectionCaches>.Store(readReplicates); + } + + /// + /// Sends data from a reader which only contains the replicate packet. + /// + [MakePublic] + internal void Replicate_SendNonAuthoritative(uint hash, BasicQueue> replicatesQueue, Channel channel) where T : IReplicateData, new() + { + if (!IsServerStarted) + return; + if (!_networkObjectCache.EnableStateForwarding) + return; + + int queueCount = replicatesQueue.Count; + //None to send. + if (queueCount == 0) + return; + + //If the only observer is the owner then there is no need to write. + int observersCount = Observers.Count; + //Quick exit for no observers other than owner. + if (observersCount == 0 || (Owner.IsValid && observersCount == 1)) + return; + + PooledWriter methodWriter = WriterPool.Retrieve(WriterPool.LENGTH_BRACKET); + + uint localTick = _networkObjectCache.TimeManager.LocalTick; + /* Write when the last entry will run. + * + * Typically, the last entry will run on localTick + (queueCount - 1). + * 1 is subtracted from queueCount because in most cases the first entry + * is going to run same tick. + * An exception is when the replicateStartTick is set, then there is going + * to be a delayed based on start tick difference. */ + uint runTickOflastEntry = localTick + ((uint)queueCount - 1); + //If start tick is set then add on the delay. + if (_replicateStartTick != TimeManager.UNSET_TICK) + runTickOflastEntry += (_replicateStartTick - TimeManager.LocalTick); + //Write the run tick now. + methodWriter.WriteTickUnpacked(runTickOflastEntry); + //Write the replicates. + int redundancyCount = (int)Mathf.Min(_networkObjectCache.PredictionManager.RedundancyCount, queueCount); +//#if !FISHNET_STABLE_SYNCTYPES +#if DO_NOT_USE + methodWriter.WriteDeltaReplicate(replicatesQueue, redundancyCount, GetDeltaSerializeOption()); +#else + methodWriter.WriteReplicate(replicatesQueue, redundancyCount); +#endif + PooledWriter writer = CreateRpc(hash, methodWriter, PacketId.Replicate, channel); + + //Exclude owner and if clientHost, also localClient. + _networkConnectionCache.Clear(); + if (Owner.IsValid) + _networkConnectionCache.Add(Owner); + if (IsClientStarted && !Owner.IsLocalClient) + _networkConnectionCache.Add(ClientManager.Connection); + + NetworkManager.TransportManager.SendToClients((byte)channel, writer.GetArraySegment(), Observers, _networkConnectionCache, false); + + methodWriter.StoreLength(); + writer.StoreLength(); + } + + /// + /// Handles a received replicate packet. + /// + private void Replicate_EnqueueReceivedReplicate(List> readDatas, BasicQueue> replicatesQueue, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + int startQueueCount = replicatesQueue.Count; + /* Owner never gets this for their own object so + * this can be processed under the assumption data is only + * handled on unowned objects. */ + PredictionManager pm = PredictionManager; + + bool isServer = _networkObjectCache.IsServerStarted; + bool isAppendedOrder = pm.IsAppendedStateOrder; + + //Maximum number of replicates allowed to be queued at once. + int maximmumReplicates = (IsServerStarted) ? pm.GetMaximumServerReplicates() : pm.MaximumPastReplicates; + + for (int i = 0; i < readDatas.Count; i++) + { + ReplicateDataContainer dataContainer = readDatas[i]; + dataContainer.IsCreated = true; + uint tick = dataContainer.Data.GetTick(); + + //Skip if old data. + if (tick <= _lastReadReplicateTick) + { + dataContainer.Dispose(); + continue; + } + + _lastReadReplicateTick = tick; + + //Cannot queue anymore, discard oldest. + if (replicatesQueue.Count > maximmumReplicates) + { + ReplicateDataContainer disposableDataContainer = replicatesQueue.Dequeue(); + disposableDataContainer.Dispose(); + } + + /* Check if replicate is already in history. + * This can occur when the replicate method has a predicted + * state for the tick, but a user created replicate comes + * through afterward. + * + * Only perform this check if not the server, since server + * does not reconcile it will never use replicatesHistory. + * + * When clients are also using ReplicateStateOrder.Future the replicates + * do not need to be put into the past, as they're always added onto + * the end of the queue. + * + * The server also does not predict replicates in the same way + * a client does. When an owner sends a replicate to the server + * the server only uses the owner tick to check if it's an old replicate. + * But when running the replicate, the server applies it's local tick and + * sends that to spectators. */ + //Add automatically if server or future order. + if (isServer || isAppendedOrder) + replicatesQueue.Enqueue(dataContainer); + //Run checks to replace data if not server. + else + InsertIntoReplicateHistory(dataContainer, replicatesHistory); + } + + /* If entries are being added after nothing then + * start the queued inputs delay. Only the server needs + * to do this since clients implement the queue delay + * by holding reconcile x ticks rather than not running received + * x ticks. */ + if ((isServer || isAppendedOrder) && startQueueCount == 0 && replicatesQueue.Count > 0) + _replicateStartTick = (_networkObjectCache.TimeManager.LocalTick + pm.StateInterpolation); + } + + /// + /// Inserts data into the replicatesHistory collection. + /// This should only be called when client only. + /// + private void InsertIntoReplicateHistory(ReplicateDataContainer dataContainer, RingBuffer> replicatesHistory) where T : IReplicateData, new() + { + /* See if replicate tick is in history. Keep in mind + * this is the localTick from the server, not the localTick of + * the client which is having their replicate relayed. */ + ReplicateTickFinder.DataPlacementResult findResult; + int index = ReplicateTickFinder.GetReplicateHistoryIndex(dataContainer.Data.GetTick(), replicatesHistory, out findResult); + + /* Exact entry found. This is the most likely + * scenario. Client would have already run the tick + * in the future, and it's now being replaced with + * the proper data. */ + if (findResult == ReplicateTickFinder.DataPlacementResult.Exact) + { + ReplicateDataContainer prevEntry = replicatesHistory[index]; + prevEntry.Dispose(); + replicatesHistory[index] = dataContainer; + } + else if (findResult == ReplicateTickFinder.DataPlacementResult.InsertMiddle) + { + InsertReplicatesHistory(replicatesHistory, dataContainer, index); + } + else if (findResult == ReplicateTickFinder.DataPlacementResult.InsertEnd) + { + AddReplicatesHistory(replicatesHistory, dataContainer); + } + + /* Insert beginning should not happen unless the data is REALLY old. + * This would mean the network was in an unplayable state. Discard the + * data. */ + if (findResult == ReplicateTickFinder.DataPlacementResult.InsertBeginning) + InsertReplicatesHistory(replicatesHistory, dataContainer, 0); + } + + /// + /// Adds to replicate history disposing of old entries if needed. + /// + private void AddReplicatesHistory(RingBuffer> replicatesHistory, ReplicateDataContainer value) where T : IReplicateData, new() + { + ReplicateDataContainer prev = replicatesHistory.Add(value); + if (prev.Data != null) + prev.Dispose(); + } + + /// + /// Inserts to replicate history disposing of old entries if needed. + /// + private void InsertReplicatesHistory(RingBuffer> replicatesHistory, ReplicateDataContainer value, int index) where T : IReplicateData, new() + { + ReplicateDataContainer prev = replicatesHistory.Insert(index, value); + if (prev.Data != null) + prev.Dispose(); + } + + /// + /// Override this method to create your reconcile data, and call your reconcile method. + /// + public virtual void CreateReconcile() { } + + /// + /// Sends a reconcile to clients. + /// + [MakePublic] + internal void Reconcile_Server(uint methodHash, ref T lastReconcileData, T data, Channel channel) where T : IReconcileData + { + //Tick does not need to be set for reconciles since they come in as state updates, which have the tick included globally. + if (IsServerInitialized) + Server_SendReconcileRpc(methodHash, ref lastReconcileData, data, channel); + } + + /// + /// This is called when the NetworkBehaviour should perform a reconcile. + /// Codegen overrides this calling Reconcile_Client with the needed data. + /// + [MakePublic] + internal virtual void Reconcile_Client_Start() { } + + /// + /// Adds a reconcile to local reconcile history. + /// + [MakePublic] + internal void Reconcile_Client_AddToLocalHistory(RingBuffer> reconcilesHistory, T data) where T : IReconcileData + { + //Server does not need to store these locally. + if (_networkObjectCache.IsServerStarted) + return; + if (!_networkObjectCache.PredictionManager.CreateLocalStates) + return; + + /* This is called by the local client when creating + * a local reconcile state. These states should always + * be in order, so we will add data to the end + * of the collection. */ + + /* These datas are used to fill missing reconciles + * be it the packet dropped, server doesnt need to send, + * or if the player is throttling reconciles. */ + + uint tick = _networkObjectCache.PredictionManager.GetCreateReconcileTick(_networkObjectCache.IsOwner); + //Tick couldn't be retrieved. + if (tick == TimeManager.UNSET_TICK) + return; + + data.SetTick(tick); + + //Build LocalReconcile. + LocalReconcile lr = new(); + lr.Initialize(tick, data); + + reconcilesHistory.Add(lr); + } + + /// + /// Called by codegen with data provided by user, such as from overriding CreateReconcile. + /// + [MakePublic] + internal void Reconcile_Current(uint hash, ref T lastReconcileData, RingBuffer> reconcilesHistory, T data, Channel channel) where T : IReconcileData, new() + { + if (_networkObjectCache.PredictionManager.IsReconciling) + return; + + if (_networkObjectCache.IsServerInitialized) + Reconcile_Server(hash, ref lastReconcileData, data, channel); + else + Reconcile_Client_AddToLocalHistory(reconcilesHistory, data); + } + + /// + /// Runs a reconcile. Prefers server data if available, otherwise uses local history data. + /// + [MakePublic] + internal void Reconcile_Client(ReconcileUserLogicDelegate reconcileDel, RingBuffer> replicatesHistory, RingBuffer> reconcilesHistory, T data) where T : IReconcileData where T2 : IReplicateData, new() + { + bool isBehaviourReconciling = IsBehaviourReconciling; + + const long unsetHistoryIndex = -1; + long historyIndex = unsetHistoryIndex; + + /* There should always be entries, except when the object + * first spawns. + * + * Find the history index associated with the reconcile tick. */ + if (reconcilesHistory.Count > 0) + { + //If reconcile data received then use that tick, otherwise get estimated tick for this reconcile. + uint reconcileTick = (isBehaviourReconciling) ? data.GetTick() : _networkObjectCache.PredictionManager.GetReconcileStateTick(_networkObjectCache.IsOwner); + + uint firstHistoryTick = reconcilesHistory[0].Tick; + historyIndex = ((long)reconcileTick - (long)firstHistoryTick); + + /* If difference is negative then + * the first history is beyond the tick being reconciled. + * EG: if history index 0 is 100 and reconcile tick is 90 then + * (90 - 100) = -10. + * This should only happen when first connecting and data hasn't been made yet. */ + if (!IsHistoryIndexValid((int)historyIndex)) + { + historyIndex = unsetHistoryIndex; + ClearReconcileHistory(reconcilesHistory); + } + //Valid history index. + else + { + //Get the tick at the index. + uint lrTick = reconcilesHistory[(int)historyIndex].Tick; + /* Since we store reconcile data every tick moving ahead a set number of ticks + * should usually match up to the reconcile tick. There are exceptions where the tick + * used to locally create the reconcile was for non owner, so using the server tick, + * and there is a slight misalignment in the server tick. This is not unusual as the + * client corrects it's tick timing regularly, but such an alignment could make this not line up. */ + /* If the history tick does not match the reconcile tick try to find + * the correct history tick. This should rarely happen but since these reconciles + * are created locally and client timing can vary slightly it's still possible. */ + if (lrTick != reconcileTick) + { + /* Get the difference between what tick is stored vs reconcile tick. + * Adjust the index based on this difference. */ + long tickDifference = ((long)reconcileTick - (long)lrTick); + + /* Add difference onto history index and again validate that it + * is in range of the collection. */ + historyIndex += tickDifference; + //Invalid. + if (!IsHistoryIndexValid((int)historyIndex)) + { + /* This shouldn't ever happen. Something went very wrong if here. + * When this does happen clear out the entire history collection + * and start over. */ + ClearReconcileHistory(reconcilesHistory); + //Unset index. + historyIndex = unsetHistoryIndex; + } + } + + //If index is set and behaviour is not reconciling then apply data. + if (!isBehaviourReconciling && historyIndex != unsetHistoryIndex) + { + LocalReconcile localReconcile = reconcilesHistory[(int)historyIndex]; + //Before disposing get the writer and call reconcile reader so it's parsed. + PooledWriter reconcileWritten = localReconcile.Writer; + /* Although this is actually from the local client the datasource is being set to server since server + * is what typically sends reconciles. */ + PooledReader reader = ReaderPool.Retrieve(reconcileWritten.GetArraySegment(), _networkObjectCache.NetworkManager, Reader.DataSource.Server); + data = Reconcile_Reader_Local(localReconcile.Tick, reader); + ReaderPool.Store(reader); + } + } + } + + //Returns if a history index can be within history collection. + bool IsHistoryIndexValid(int index) => (index >= 0 && (index < reconcilesHistory.Count)); + + //Dispose of old reconcile histories. + if (historyIndex != unsetHistoryIndex) + { + int index = (int)historyIndex; + //If here everything is good, remove up to used index. + for (int i = 0; i < index; i++) + reconcilesHistory[i].Dispose(); + + reconcilesHistory.RemoveRange(true, (int)historyIndex); + } + + //If does not have data still then exit method. + if (!IsBehaviourReconciling) + return; + + //Set on the networkObject that a reconcile can now occur. + _networkObjectCache.IsObjectReconciling = true; + + uint dataTick = data.GetTick(); + _lastReconcileTick = dataTick; + + if (replicatesHistory.Count > 0) + { + /* Remove replicates up to reconcile. Since the reconcile + * is the state after a replicate for it's tick we no longer + * need any replicates prior. */ + //Find the closest entry which can be removed. + int removeCount = 0; + //A few quick tests. + if (replicatesHistory.Count > 0) + { + /* If the last entry in history is less or equal + * to datatick then all histories need to be removed + * as reconcile is beyond them. */ + if (replicatesHistory[^1].Data.GetTick() <= dataTick) + { + removeCount = replicatesHistory.Count; + } + //Somewhere in between. Find what to remove up to. + else + { + for (int i = 0; i < replicatesHistory.Count; i++) + { + uint entryTick = replicatesHistory[i].Data.GetTick(); + /* Soon as an entry beyond dataTick is + * found remove up to that entry. */ + if (entryTick > dataTick) + { + removeCount = i; + break; + } + } + } + } + + for (int i = 0; i < removeCount; i++) + replicatesHistory[i].Dispose(); + replicatesHistory.RemoveRange(true, removeCount); + } + + //Call reconcile user logic. + reconcileDel?.Invoke(data, Channel.Reliable); + } + + internal void Reconcile_Client_End() + { + IsBehaviourReconciling = false; + } + + /// + /// Disposes and clears LocalReconciles. + /// + private void ClearReconcileHistory(RingBuffer> reconcilesHistory) where T : IReconcileData + { + foreach (LocalReconcile localReconcile in reconcilesHistory) + localReconcile.Dispose(); + + reconcilesHistory.Clear(); + } + + /// + /// Reads a reconcile from the server. + /// + public void Reconcile_Reader(PooledReader reader, ref T lastReconcileData) where T : IReconcileData + { + uint tick = (IsOwner) ? PredictionManager.ClientStateTick : PredictionManager.ServerStateTick; +//#if !FISHNET_STABLE_SYNCTYPES +#if DO_NOT_USE + T newData = reader.ReadDeltaReconcile(lastReconciledata); +#else + T newData = reader.ReadReconcile(); +#endif + //Do not process if an old state. + if (tick < _lastReadReconcileRemoteTick) + return; + + lastReconcileData = newData; + lastReconcileData.SetTick(tick); + + IsBehaviourReconciling = true; + _networkObjectCache.IsObjectReconciling = true; + _lastReadReconcileRemoteTick = tick; + } + + /// + /// Reads a local reconcile from the client. + /// + public T Reconcile_Reader_Local(uint tick, PooledReader reader) where T : IReconcileData + { + reader.NetworkManager = _networkObjectCache.NetworkManager; + T newData = reader.ReadReconcile(); + newData.SetTick(tick); + + IsBehaviourReconciling = true; + + return newData; + } + + /// + /// Sets the last tick this NetworkBehaviour replicated with. + /// + /// True to set unordered value, false to set ordered. + private void SetReplicateTick(uint value, bool createdReplicate) + { + _lastOrderedReplicatedTick = value; + _networkObjectCache.SetReplicateTick(value, createdReplicate); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs.meta new file mode 100644 index 0000000..a2969bc --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 074dac4dd3f9f6a4d8c1eb1191334472 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.Prediction.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs new file mode 100644 index 0000000..58dc813 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs @@ -0,0 +1,335 @@ +using FishNet.CodeAnalysis.Annotations; +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 System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object +{ + + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Public. + #region Obsoletes + //Remove on v5 + [Obsolete("Use IsClientOnlyInitialized. Note the difference between IsClientOnlyInitialized and IsClientOnlyStarted.")] + public bool IsClientOnly => IsClientOnlyInitialized; + [Obsolete("Use IsServerOnlyInitialized. Note the difference between IsServerOnlyInitialized and IsServerOnlyStarted.")] + public bool IsServerOnly => IsServerOnlyInitialized; + [Obsolete("Use IsHostInitialized. Note the difference between IsHostInitialized and IsHostStarted.")] + public bool IsHost => IsHostInitialized; + [Obsolete("Use IsClientInitialized. Note the difference between IsClientInitialized and IsClientStarted.")] + public bool IsClient => IsClientInitialized; + [Obsolete("Use IsServerInitialized. Note the difference between IsServerInitialized and IsServerStarted.")] + public bool IsServer => IsServerInitialized; + #endregion + /// + /// 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 this object has been initialized on the client side. + /// This is set true right before client start callbacks and after stop callbacks. + /// + public bool IsClientInitialized => _networkObjectCache.IsClientInitialized; + /// + /// True if the client is started and authenticated. + /// + public bool IsClientStarted => _networkObjectCache.IsClientStarted; + /// + /// True if this object has been initialized only on the client side. + /// This is set true right before server start callbacks and after stop callbacks. + public bool IsClientOnlyInitialized => _networkObjectCache.IsClientOnlyInitialized; + /// + /// True if only the client is started and authenticated. + /// + public bool IsClientOnlyStarted => _networkObjectCache.IsClientOnlyStarted; + /// + /// True if this object has been initialized on the server side. + /// This is set true right before server start callbacks and after stop callbacks. + /// + public bool IsServerInitialized => _networkObjectCache.IsServerInitialized; + /// + /// True if server is started. + /// + public bool IsServerStarted => _networkObjectCache.IsServerStarted; + /// + /// True if this object has been initialized only on the server side. + /// This is set true right before server start callbacks and after stop callbacks. + public bool IsServerOnlyInitialized => _networkObjectCache.IsServerOnlyInitialized; + /// + /// True if only the server is started. + /// + public bool IsServerOnlyStarted => _networkObjectCache.IsServerOnlyStarted; + /// + /// True if this object has been initialized on the server and client side. + /// + public bool IsHostInitialized => _networkObjectCache.IsHostInitialized; + /// + /// True if client and server are started. + /// + public bool IsHostStarted => _networkObjectCache.IsHostStarted; + /// + /// True if client nor server are started. + /// + public bool IsOffline => _networkObjectCache.IsOffline; + /// + /// 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. + /// To check if server or client has been initialized on this object use IsXYZInitialized. + /// + [Obsolete("Use GetIsNetworked.")] //Remove on V5. + public bool IsNetworked => GetIsNetworked(); + + /// + /// 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. + /// To check if server or client has been initialized on this object use IsXYZInitialized. + /// + public bool GetIsNetworked() => _networkObjectCache.GetIsNetworked(); + + /// + /// Sets IsNetworked value. This method must be called before Start. + /// + /// New IsNetworked value. + public void SetIsNetworked(bool value) => _networkObjectCache.SetIsNetworked(value); + + /// + /// True if a reconcile is occuring on the PredictionManager. Note the difference between this and IsBehaviourReconciling. + /// + public bool IsManagerReconciling => _networkObjectCache.IsManagerReconciling; + /// + /// Observers for this NetworkBehaviour. + /// + public HashSet Observers => _networkObjectCache.Observers; + /// + /// True if the local client is the owner of this object. + /// + [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", "")] + public bool IsOwner => _networkObjectCache.IsOwner; + /// + /// True if IsOwner, or if IsServerInitialized with no Owner. + /// + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "OnStartServer", "")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "OnStartNetwork", " Use (base.Owner.IsLocalClient || (base.IsServerInitialized && !Owner.Isvalid) instead.")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "Awake", "")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "Start", "")] + public bool IsController => (_networkObjectCache.IsOwner || (_networkObjectCache.IsServerInitialized && !_networkObjectCache.Owner.IsValid)); + [Obsolete("Use IsController.")] + public bool HasAuthority => IsController; + /// + /// 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; + #endregion + + /// + /// 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, UnityEngine.SceneManagement.Scene scene = default) + { + if (IsNetworkObjectNull(true)) + return; + _networkObjectCache.Spawn(go, ownerConnection, scene); + } + /// + /// 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, UnityEngine.SceneManagement.Scene scene = default) + { + if (IsNetworkObjectNull(true)) + return; + _networkObjectCache.Spawn(nob, ownerConnection, scene); + } + /// + /// 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.RemoveOwnership(); + /// + /// Gives ownership to newOwner. + /// + + public void GiveOwnership(NetworkConnection newOwner) => _networkObjectCache.GiveOwnership(newOwner, asServer: true, recursive: false); + + /// + /// Gives ownership to newOwner. + /// + + public void GiveOwnership(NetworkConnection newOwner, bool includeNested) => _networkObjectCache.GiveOwnership(newOwner, asServer: true, includeNested); + + #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); + /// + /// Tries to registers a new component to this NetworkManager. + /// This will not register the instance if another already exists. + /// + /// Type to register. + /// Reference of the component being registered. + /// True if was able to register, false if an instance is already registered. + public bool TryRegisterInstance(T component) where T : UnityEngine.Component => _networkObjectCache.TryRegisterInstance(component); + /// + /// 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/NetworkBehaviour.QOL.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs.meta new file mode 100644 index 0000000..2be2478 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d7aef532208d06c4880973c51b1906f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.QOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs new file mode 100644 index 0000000..a8b40d0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs @@ -0,0 +1,166 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Managing.Server; +using FishNet.Object.Helping; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Collections.Generic; +using System.Linq; +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(); + #endregion + + #region Consts. + /// + /// Number of bytes written for each RPCLinks. + /// + internal const int RPCLINK_RESERVED_BYTES = 2; + #endregion + + /// + /// Initializes RpcLinks. This will only call once even as host. + /// + private void InitializeRpcLinks() + { + /* 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. + if (_observersRpcDelegates != null) + { + foreach (uint rpcHash in _observersRpcDelegates.Keys) + { + if (!MakeLink(rpcHash, PacketId.ObserversRpc)) + return; + } + } + //TargetRpcs. + if (_targetRpcDelegates != null) + { + foreach (uint rpcHash in _targetRpcDelegates.Keys) + { + if (!MakeLink(rpcHash, PacketId.TargetRpc)) + return; + } + } + //ReconcileRpcs. + if (_reconcileRpcDelegates != null) + { + foreach (uint rpcHash in _reconcileRpcDelegates.Keys) + { + if (!MakeLink(rpcHash, PacketId.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, PacketId packetId) + { + if (serverManager.GetRpcLink(out ushort linkIndex)) + { + _rpcLinks[rpcHash] = new(rpcHash, packetId, linkIndex); + 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. + /// + private PooledWriter CreateLinkedRpc(RpcLinkType link, PooledWriter methodWriter, Channel channel) + { + int rpcHeaderBufferLength = GetEstimatedRpcHeaderLength(); + int methodWriterLength = methodWriter.Length; + //Writer containing full packet. + PooledWriter writer = WriterPool.Retrieve(rpcHeaderBufferLength + methodWriterLength); + writer.WriteUInt16(link.LinkPacketId); + +#if DEVELOPMENT + int written = WriteDebugForValidateRpc(writer, link.RpcPacketId, link.RpcHash); +#endif + + //Write length only if reliable. + if (channel == Channel.Reliable) + writer.WriteInt32(methodWriter.Length); + //Data. + writer.WriteArraySegment(methodWriter.GetArraySegment()); + +#if DEVELOPMENT + WriteDebugLengthForValidateRpc(writer, written); +#endif + + 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) + { + int rpcLinksCount = _rpcLinks.Count; + if (rpcLinksCount == 0) + return; + + writer.WriteNetworkBehaviourId(this); + writer.WriteUInt16((ushort)rpcLinksCount); + + foreach (KeyValuePair item in _rpcLinks) + { + //RpcLink index. + writer.WriteUInt16Unpacked(item.Value.LinkPacketId); + //Hash. + writer.WriteUInt16Unpacked((ushort)item.Key); + //True/false if observersRpc. + writer.WriteUInt16Unpacked((ushort)item.Value.RpcPacketId); + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs.meta new file mode 100644 index 0000000..75331a0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7136e9ee3f7eee44abab09285ab7b939 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCLinks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs new file mode 100644 index 0000000..cbbeb6b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs @@ -0,0 +1,519 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using System; +using FishNet.CodeGenerating; +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Managing.Transporting; +using FishNet.Object.Delegating; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using System.Text; +using FishNet.Serializing.Helping; +using UnityEngine; + +namespace FishNet.Object +{ + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Types. + private struct BufferedRpc + { + /// + /// Writer containing the full RPC. + /// + public PooledWriter Writer; + /// + /// Which order to send the data in relation to other packets. + /// + public DataOrderType OrderType; + + public BufferedRpc(PooledWriter writer, DataOrderType orderType) + { + Writer = writer; + OrderType = orderType; + } + } + #endregion + + #region Private. + /// + /// Registered ServerRpc methods. + /// + private readonly Dictionary _serverRpcDelegates = new(); + /// + /// Registered ObserversRpc methods. + /// + private readonly Dictionary _observersRpcDelegates = new(); + /// + /// Registered TargetRpc methods. + /// + private readonly Dictionary _targetRpcDelegates = new(); + /// + /// 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 readonly Dictionary _bufferedRpcs = new(); + /// + /// Connections to exclude from RPCs, such as ExcludeOwner or ExcludeServer. + /// + private readonly HashSet _networkConnectionCache = new(); + #endregion + + #region Const. + /// + /// This is an estimated value of what the maximum possible size of a RPC could be. + /// Realistically this value is much smaller but this value is used as a buffer. + /// + private const int MAXIMUM_RPC_HEADER_SIZE = 10; +#if DEVELOPMENT + /// + /// Bytes used to write length for validating Rpc length. + /// + private const int VALIDATE_RPC_LENGTH_BYTES = 4; +#endif + #endregion + + /// + /// Called when buffered RPCs should be sent. + /// + internal void SendBufferedRpcs(NetworkConnection conn) + { + TransportManager tm = _networkObjectCache.NetworkManager.TransportManager; + foreach (BufferedRpc bRpc in _bufferedRpcs.Values) + tm.SendToClient((byte)Channel.Reliable, bRpc.Writer.GetArraySegment(), conn, true, bRpc.OrderType); + } + + /// + /// Registers a RPC method. + /// + /// + /// + [APIExclude] + [MakePublic] + internal void RegisterServerRpc(uint hash, ServerRpcDelegate del) + { + if (_serverRpcDelegates.TryAdd(hash, del)) + IncreaseRpcMethodCount(); + else + NetworkManager.LogError($"ServerRpc key {hash} has already been added for {GetType().FullName} on {gameObject.name}"); + } + + /// + /// Registers a RPC method. + /// + /// + /// + [APIExclude] + [MakePublic] + internal void RegisterObserversRpc(uint hash, ClientRpcDelegate del) + { + if (_observersRpcDelegates.TryAdd(hash, del)) + IncreaseRpcMethodCount(); + else + NetworkManager.LogError($"ObserversRpc key {hash} has already been added for {GetType().FullName} on {gameObject.name}"); + } + + /// + /// Registers a RPC method. + /// + /// + /// + [APIExclude] + [MakePublic] + internal void RegisterTargetRpc(uint hash, ClientRpcDelegate del) + { + if (_targetRpcDelegates.TryAdd(hash, del)) + IncreaseRpcMethodCount(); + else + NetworkManager.LogError($"TargetRpc key {hash} has already been added for {GetType().FullName} on {gameObject.name}"); + } + + /// + /// 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 (BufferedRpc bRpc in _bufferedRpcs.Values) + bRpc.Writer.Store(); + _bufferedRpcs.Clear(); + } + + /// + /// Reads a RPC hash. + /// + /// + /// + private uint ReadRpcHash(PooledReader reader) + { + if (_rpcHashSize == 1) + return reader.ReadUInt8Unpacked(); + else + return reader.ReadUInt16(); + } + + /// + /// Called when a ServerRpc is received. + /// + internal void ReadServerRpc(bool fromRpcLink, uint methodHash, PooledReader reader, NetworkConnection sendingClient, Channel channel) + { + if (!fromRpcLink) + 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.LogError($"ServerRpc not found for hash {methodHash} on object {gameObject.name} [id {ObjectId}]. Remainder of packet may become corrupt."); + } + + /// + /// Called when an ObserversRpc is received. + /// + internal void ReadObserversRpc(bool fromRpcLink, uint methodHash, PooledReader reader, Channel channel) + { + if (!fromRpcLink) + methodHash = ReadRpcHash(reader); + + if (_observersRpcDelegates.TryGetValueIL2CPP(methodHash, out ClientRpcDelegate del)) + del.Invoke(reader, channel); + else + _networkObjectCache.NetworkManager.LogError($"ObserversRpc not found for hash {methodHash} on object {gameObject.name} [id {ObjectId}] . Remainder of packet may become corrupt."); + } + + /// + /// Called when an TargetRpc is received. + /// + internal void ReadTargetRpc(bool fromRpcLink, uint methodHash, PooledReader reader, Channel channel) + { + if (!fromRpcLink) + methodHash = ReadRpcHash(reader); + + if (_targetRpcDelegates.TryGetValueIL2CPP(methodHash, out ClientRpcDelegate del)) + del.Invoke(reader, channel); + else + _networkObjectCache.NetworkManager.LogError($"TargetRpc not found for hash {methodHash} on object {gameObject.name} [id {ObjectId}] . Remainder of packet may become corrupt."); + } + + /// + /// Sends a RPC to server. + /// + /// + /// + /// + [MakePublic] + internal void SendServerRpc(uint hash, PooledWriter methodWriter, Channel channel, DataOrderType orderType) + { + if (!IsSpawnedWithWarning()) + return; + + _transportManagerCache.CheckSetReliableChannel(methodWriter.Length + MAXIMUM_RPC_HEADER_SIZE, ref channel); + + PooledWriter writer = CreateRpc(hash, methodWriter, PacketId.ServerRpc, channel); + _networkObjectCache.NetworkManager.TransportManager.SendToServer((byte)channel, writer.GetArraySegment(), true, orderType); + writer.StoreLength(); + } + + /// + /// Sends a RPC to observers. + /// + /// + /// + /// + [APIExclude] + [MakePublic] + internal void SendObserversRpc(uint hash, PooledWriter methodWriter, Channel channel, DataOrderType orderType, bool bufferLast, bool excludeServer, bool excludeOwner) + { + if (!IsSpawnedWithWarning()) + return; + + _transportManagerCache.CheckSetReliableChannel(methodWriter.Length + MAXIMUM_RPC_HEADER_SIZE, ref channel); + + PooledWriter writer = lCreateRpc(channel); + SetNetworkConnectionCache(excludeServer, excludeOwner); + _networkObjectCache.NetworkManager.TransportManager.SendToClients((byte)channel, writer.GetArraySegment(), _networkObjectCache.Observers, _networkConnectionCache, true, orderType); + + /* 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 (bufferLast) + { + if (_bufferedRpcs.TryGetValueIL2CPP(hash, out BufferedRpc result)) + result.Writer.StoreLength(); + + /* If sent on unreliable the RPC has to be rebuilt for + * reliable headers since buffered RPCs always send reliably + * to new connections. */ + if (channel == Channel.Unreliable) + { + writer.StoreLength(); + writer = lCreateRpc(Channel.Reliable); + } + _bufferedRpcs[hash] = new(writer, orderType); + } + //If not buffered then dispose immediately. + else + { + writer.StoreLength(); + } + + PooledWriter lCreateRpc(Channel c) + { +#if DEVELOPMENT + if (!NetworkManager.DebugManager.DisableObserversRpcLinks && _rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#else + if (_rpcLinks.TryGetValueIL2CPP(hash, out RpcLinkType link)) +#endif + writer = CreateLinkedRpc(link, methodWriter, c); + else + writer = CreateRpc(hash, methodWriter, PacketId.ObserversRpc, c); + + return writer; + } + } + + /// + /// Sends a RPC to target. + /// + [MakePublic] + internal void SendTargetRpc(uint hash, PooledWriter methodWriter, Channel channel, DataOrderType orderType, NetworkConnection target, bool excludeServer, bool validateTarget = true) + { + if (!IsSpawnedWithWarning()) + return; + + _transportManagerCache.CheckSetReliableChannel(methodWriter.Length + MAXIMUM_RPC_HEADER_SIZE, ref channel); + + 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 DEVELOPMENT + if (!NetworkManager.DebugManager.DisableTargetRpcLinks && _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, true, orderType); + writer.Store(); + } + + /// + /// Adds excluded connections to ExcludedRpcConnections. + /// + private void SetNetworkConnectionCache(bool addClientHost, bool addOwner) + { + _networkConnectionCache.Clear(); + if (addClientHost && IsClientStarted) + _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. + /// + 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.Retrieve(rpcHeaderBufferLength + methodWriterLength); + writer.WritePacketIdUnpacked(packetId); + +#if DEVELOPMENT + int written = WriteDebugForValidateRpc(writer, packetId, hash); +#endif + + writer.WriteNetworkBehaviour(this); + + //Only write length if reliable. + if (channel == Channel.Reliable) + writer.WriteInt32(methodWriterLength + _rpcHashSize); + + //Hash and data. + WriteRpcHash(hash, writer); + writer.WriteArraySegment(methodWriter.GetArraySegment()); + +#if DEVELOPMENT + WriteDebugLengthForValidateRpc(writer, written); +#endif + + return writer; + } + +#if DEVELOPMENT + /// + /// Gets the method name for a Rpc using packetId and Rpc hash. + /// + private string GetRpcMethodName(PacketId packetId, uint hash) + { + try + { + if (packetId == PacketId.ObserversRpc) + return _observersRpcDelegates[hash].Method.Name; + else if (packetId == PacketId.TargetRpc) + return _targetRpcDelegates[hash].Method.Name; + else if (packetId == PacketId.ServerRpc) + return _serverRpcDelegates[hash].Method.Name; + else if (packetId == PacketId.Replicate) + return _replicateRpcDelegates[hash].Method.Name; + else if (packetId == PacketId.Reconcile) + return _reconcileRpcDelegates[hash].Method.Name; + else + _networkObjectCache.NetworkManager.LogError($"Unhandled packetId of {packetId} for hash {hash}."); + } + //This should not ever happen. + catch + { + _networkObjectCache.NetworkManager.LogError($"Rpc method name not found for packetId {packetId}, hash {hash}."); + } + + return "Error"; + } +#endif + + /// + /// Writes rpcHash to writer. + /// + /// + /// + private void WriteRpcHash(uint hash, PooledWriter writer) + { + if (_rpcHashSize == 1) + writer.WriteUInt8Unpacked((byte)hash); + else + writer.WriteUInt16((byte)hash); + } + +#if DEVELOPMENT + private int WriteDebugForValidateRpc(Writer writer, PacketId packetId, uint hash) + { + if (!_networkObjectCache.NetworkManager.DebugManager.ValidateRpcLengths) + return -1; + + writer.Skip(VALIDATE_RPC_LENGTH_BYTES); + int positionStart = writer.Position; + + string txt = $"NetworkObject Details: {_networkObjectCache.ToString()}. NetworkBehaviour Details: Name [{GetType().Name}]. Rpc Details: Name [{GetRpcMethodName(packetId, hash)}] PacketId [{packetId}] Hash [{hash}]"; + writer.WriteString(txt); + + return positionStart; + } + + private void WriteDebugLengthForValidateRpc(Writer writer, int positionStart) + { + if (!_networkObjectCache.NetworkManager.DebugManager.ValidateRpcLengths) + return; + + //Write length. + int writtenLength = (writer.Position - positionStart); + writer.InsertInt32Unpacked(writtenLength, positionStart - VALIDATE_RPC_LENGTH_BYTES); + } + + /// + /// Parses written data used to validate a Rpc packet. + /// + internal static void ReadDebugForValidatedRpc(NetworkManager manager, PooledReader reader, out int readerRemainingAfterLength, out string rpcInformation, out uint expectedReadAmount) + { + rpcInformation = null; + expectedReadAmount = 0; + readerRemainingAfterLength = 0; + + if (!manager.DebugManager.ValidateRpcLengths) + return; + + expectedReadAmount = (uint)reader.ReadInt32Unpacked(); + readerRemainingAfterLength = reader.Remaining; + + rpcInformation = reader.ReadStringAllocated(); + } + + /// + /// Prints an error if an Rpc packet did not validate correctly. + /// + /// True if an error occurred. + internal static bool TryPrintDebugForValidatedRpc(bool fromRpcLink, NetworkManager manager, PooledReader reader, int startReaderRemaining, string rpcInformation, uint expectedReadAmount, Channel channel) + { + if (!manager.DebugManager.ValidateRpcLengths) + return false; + + int readAmount = (startReaderRemaining - reader.Remaining); + if (readAmount != expectedReadAmount) + { + string src = (fromRpcLink) ? "RpcLink" : "Rpc"; + string msg = $"A {src} read an incorrect amount of data on channel {channel}. Read length was {readAmount}, expected length is {expectedReadAmount}. {rpcInformation}." + $" {manager.PacketIdHistory.GetReceivedPacketIds(packetsFromServer: (reader.Source == Reader.DataSource.Server))}."; + manager.LogError(msg); + + return true; + } + + return false; + } +#endif + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs.meta new file mode 100644 index 0000000..a118b88 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 938eacb83fa7d0046bd769b31dac7e80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.RPCs.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs new file mode 100644 index 0000000..cdd8783 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs @@ -0,0 +1,522 @@ +using FishNet.CodeGenerating; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Transporting; +using FishNet.Object.Synchronizing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using FishNet.Serializing.Helping; +using FishNet.Utility.Extension; +using UnityEngine; + +namespace FishNet.Object +{ + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Types. + /// + /// Used to generate data sent from synctypes. + /// + private struct SyncTypeWriter + { + /// + /// Writers for each channel. + /// + public List Writers; + + /// + /// Resets Writers. + /// + public void Reset() + { + if (Writers == null) + return; + + for (int i = 0; i < Writers.Count; i++) + Writers[i].Clear(); + } + + public void Initialize() + { + Writers = CollectionCaches.RetrieveList(); + for (int i = 0; i < TransportManager.CHANNEL_COUNT; i++) + Writers.Add(WriterPool.Retrieve()); + } + } + #endregion + + #region Private. + /// + /// Writers for syncTypes. A writer will exist for every ReadPermission type. + /// + private static Dictionary _syncTypeWriters = new(); + /// + /// SyncTypes within this NetworkBehaviour. + /// + private Dictionary _syncTypes = new(); + /// + /// True if at least one syncType is dirty. + /// + internal bool SyncTypeDirty; + /// + /// All ReadPermission values. + /// This is used to build SyncTypeWriters on initialization. + /// + private static List _readPermissions; + #endregion + + #region Consts. + /// + /// Bytes to reserve for writing SyncType headers. + /// + /// + internal const byte SYNCTYPE_RESERVE_BYTES = 4; + /// + /// Bytes to reserve for writing payload headers. + /// + /// + internal const byte PAYLOAD_RESERVE_BYTES = 4; + #endregion + + /// + /// Registers a SyncType. + /// + /// + /// + internal void RegisterSyncType(SyncBase sb, uint index) + { + if (_syncTypes == null) + _syncTypes = CollectionCaches.RetrieveDictionary(); + if (!_syncTypes.TryAdd(index, sb)) + NetworkManager.LogError($"SyncType key {index} has already been added for {GetType().FullName} on {gameObject.name}"); + } + + /// + /// Sets a SyncType as dirty. + /// + /// True if able to dirty SyncType. + internal bool DirtySyncType() + { + if (!IsServerStarted) + 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. Clients which become observers + * will get the latest values in the spawn message, which is separate + * from writing dirty syncTypes. */ + if (_networkObjectCache.Observers.Count == 0 && !_networkObjectCache.PredictedSpawner.IsValid) + return false; + if (!SyncTypeDirty) + _networkObjectCache.NetworkManager.ServerManager.Objects.SetDirtySyncType(this); + + SyncTypeDirty = true; + + return true; + } + + /// + /// Initializes SyncTypes. This will only call once even as host. + /// + private void SyncTypes_Preinitialize(bool asServer) + { + if (_networkObjectCache.DoubleLogic(asServer)) + return; + + //This only runs once since SyncTypeWriters are static. + if (_syncTypeWriters.Count == 0) + { + List readPermissions = new(); + System.Array arr = System.Enum.GetValues(typeof(ReadPermission)); + foreach (ReadPermission rp in arr) + readPermissions.Add(rp); + + foreach (ReadPermission rp in readPermissions) + { + SyncTypeWriter syncTypeWriter = new(); + syncTypeWriter.Initialize(); + _syncTypeWriters[rp] = syncTypeWriter; + } + } + + /* Initialize synctypes every spawn because there could be + * callbacks which occur that the user or even we may implement + * during the initialization. */ + foreach (SyncBase sb in _syncTypes.Values) + sb.PreInitialize(_networkObjectCache.NetworkManager, asServer); + } + + + /// + /// Reads a SyncType. + /// + internal void ReadSyncType(PooledReader reader, int writtenLength, bool asServer = false) + { + int endPosition = (reader.Position + writtenLength); + while (reader.Position < endPosition) + { + byte syncTypeId = reader.ReadUInt8Unpacked(); + if (_syncTypes.TryGetValueIL2CPP(syncTypeId, out SyncBase sb)) + sb.Read(reader, asServer); + else + NetworkManager.LogError($"SyncType not found for index {syncTypeId} on {transform.name}, component {GetType().FullName}. The remainder of the packet will become corrupt likely resulting in unforeseen issues for this tick, such as data missing or objects not spawning."); + } + + if (reader.Position > endPosition) + { + NetworkManager.LogError($"Remaining bytes in SyncType reader are less than expected. Something did not serialize or deserialize properly which will likely result in a SyncType being incorrect."); + //Fix position. + reader.Position = endPosition; + } + } + + /// + /// Writes only dirty SyncTypes. + /// + /// True if there are no pending dirty sync types. + internal bool WriteDirtySyncTypes(SyncTypeWriteFlag flags) + { + // /* IsSpawned 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. + // * + // * No observers can occur when the server changes a syncType + // * value but gained no observers in the same tick. We still + // * want to mark a syncType as dirty in this situation because + // * it needs to write in a despawn message in the scenario the object + // * is spawned (no observers), synctype changed, then despawned immediately + // * after. + // */ + // if (!IsSpawned || _networkObjectCache.Observers.Count == 0) + // { + // ResetState_SyncTypes(asServer: true); + // return true; + // } + + /* IsSpawned 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) + { + ResetState_SyncTypes(asServer: true); + return true; + } + + /* Additional checks need to appear below the reset check + * above. Resets should place priority as this method was called + * when it should not have been, such as during a despawn. */ + + //None dirty or no synctypes. + if (!SyncTypeDirty || _syncTypes.Count == 0) + return true; + + //Number of syncTypes which are/were dirty. + int dirtyCount = 0; + //Number of syncTypes which were written. + int writtenCount = 0; + + //Flags as boolean. + bool ignoreInterval = flags.FastContains(SyncTypeWriteFlag.IgnoreInterval); + bool forceReliable = flags.FastContains(SyncTypeWriteFlag.ForceReliable); + + uint tick = _networkObjectCache.NetworkManager.TimeManager.Tick; + bool ownerIsActive = _networkObjectCache.Owner.IsActive; + + //Reset syncTypeWriters. + foreach (SyncTypeWriter stw in _syncTypeWriters.Values) + stw.Reset(); + + HashSet writtenReadPermissions = CollectionCaches.RetrieveHashSet(); + + foreach (SyncBase sb in _syncTypes.Values) + { + //This entry is not dirty. + if (!sb.IsDirty) + continue; + + /* Mark that at least one is still dirty. + * This does not mean that anything was written + * as there are still blocks to bypass. */ + dirtyCount++; + + //Interval not yet met. + if (!ignoreInterval && !sb.IsNextSyncTimeMet(tick)) + continue; + + //Unset that SyncType is dirty as it will be written now. + sb.ResetDirty(); + + /* SyncType is for owner only but the owner is not valid, therefor + * nothing can be written. It's possible for a SyncType to be dirty + * and owner only, with no owner, if the owner dropped after the syncType + * was dirtied. */ + ReadPermission rp = sb.Settings.ReadPermission; + //If ReadPermission is owner but no owner skip this syncType write. + if (!ownerIsActive && rp == ReadPermission.OwnerOnly) + continue; + + writtenCount++; + + if (forceReliable) + sb.SetCurrentChannel(Channel.Reliable); + + //Get channel + byte channel = (byte)sb.Channel; + + /* Writer can be obtained quickly by using the readPermission byte value. + * Byte values are in order starting at 0. */ + + + //Find writer to use. Should never fail. + if (!_syncTypeWriters.TryGetValueIL2CPP(rp, out SyncTypeWriter stw)) + continue; + + /* Channel for syncType is beyond available channels in transport. + * Use default reliable. */ + if (channel >= TransportManager.CHANNEL_COUNT) + channel = (byte)Channel.Reliable; + + writtenReadPermissions.Add(rp); + + sb.WriteDelta(stw.Writers[channel]); + } + + //If no dirty were found. + if (dirtyCount == 0) + { + SyncTypeDirty = false; + CollectionCaches.Store(writtenReadPermissions); + return true; + } + //Nothing was written, but some are still dirty. + else if (writtenReadPermissions.Count == 0) + { + CollectionCaches.Store(writtenReadPermissions); + return false; + } + + /* If here something was written. */ + + PooledWriter fullWriter = WriterPool.Retrieve(); + TransportManager tm = _networkObjectCache.NetworkManager.TransportManager; + + foreach (ReadPermission rp in writtenReadPermissions) + { + //Find writer to use. Should never fail. + if (!_syncTypeWriters.TryGetValueIL2CPP(rp, out SyncTypeWriter stw)) + continue; + + for (int i = 0; i < stw.Writers.Count; i++) + { + PooledWriter writer = stw.Writers[i]; + //None written for this channel. + if (writer.Length == 0) + continue; + + CompleteSyncTypePacket(fullWriter, writer); + writer.Clear(); + + //Should not be the case but check for safety. + if (fullWriter.Length == 0) + continue; + + byte channel = (byte)i; + + switch (rp) + { + //Send to everyone or excludeOwner. + case ReadPermission.Observers: + tm.SendToClients(channel, fullWriter.GetArraySegment(), _networkObjectCache.Observers); + break; + //Everyone but owner. + case ReadPermission.ExcludeOwner: + _networkConnectionCache.Clear(); + if (ownerIsActive) + _networkConnectionCache.Add(_networkObjectCache.Owner); + tm.SendToClients(channel, fullWriter.GetArraySegment(), _networkObjectCache.Observers, _networkConnectionCache); + break; + //Owner only. Owner will always be valid if here. + case ReadPermission.OwnerOnly: + tm.SendToClient(channel, fullWriter.GetArraySegment(), _networkObjectCache.Owner); + break; + } + + fullWriter.Clear(); + } + } + + fullWriter.Store(); + CollectionCaches.Store(writtenReadPermissions); + + //Return if all dirty were written. + bool allDirtyWritten = (dirtyCount == writtenCount); + if (allDirtyWritten) + SyncTypeDirty = false; + + return allDirtyWritten; + } + + /// + /// Writes all SyncTypes for a connection if readPermissions match. + /// + + internal void WriteSyncTypesForConnection(NetworkConnection conn, ReadPermission readPermissions) + { + //There are no syncTypes. + if (_syncTypes.Count == 0) + return; + + //It will always exist but we need to out anyway. + if (!_syncTypeWriters.TryGetValueIL2CPP(readPermissions, out SyncTypeWriter stw)) + return; + + //Reset syncTypeWriters. + stw.Reset(); + + PooledWriter fullWriter = WriterPool.Retrieve(); + + foreach (SyncBase sb in _syncTypes.Values) + { + if (sb.Settings.ReadPermission != readPermissions) + continue; + + PooledWriter writer = stw.Writers[(byte)sb.Settings.Channel]; + sb.WriteFull(writer); + } + + for (int i = 0; i < stw.Writers.Count; i++) + { + PooledWriter writer = stw.Writers[i]; + CompleteSyncTypePacket(fullWriter, writer); + writer.Clear(); + + byte channel = (byte)Channel.Reliable; + _networkObjectCache.NetworkManager.TransportManager.SendToClient(channel, fullWriter.GetArraySegment(), conn); + } + + fullWriter.Store(); + } + + /// + /// Completes the writing of a SyncType by writing the header and serialized values. + /// + private void CompleteSyncTypePacket(PooledWriter fullWriter, PooledWriter syncTypeWriter) + { + //None written for this writer. + if (syncTypeWriter.Length == 0) + return; + + fullWriter.Clear(); + fullWriter.WritePacketIdUnpacked(PacketId.SyncType); + fullWriter.WriteNetworkBehaviour(this); + + ReservedLengthWriter reservedWriter = ReservedWritersExtensions.Retrieve(); + reservedWriter.Initialize(fullWriter, SYNCTYPE_RESERVE_BYTES); + + fullWriter.WriteArraySegment(syncTypeWriter.GetArraySegment()); + + reservedWriter.WriteLength(); + reservedWriter.Store(); + } + + /// + /// Writes syncTypes for a spawn message. + /// + /// Connection SyncTypes are being written for. + internal void WriteSyncTypesForSpawn(PooledWriter writer, NetworkConnection conn) + { + //There are no syncTypes. + if (_syncTypes.Count == 0) + return; + + //True if connection passed in is the owner of this object. + bool connIsOwner = (conn == _networkObjectCache.Owner); + + //Reserved bytes for componentIndex and amount written. + const byte reservedBytes = 2; + writer.Skip(reservedBytes); + int positionAfterReserve = writer.Position; + + byte written = 0; + + foreach (SyncBase sb in _syncTypes.Values) + { + ReadPermission rp = sb.Settings.ReadPermission; + bool canWrite = (rp == ReadPermission.Observers) || + (rp == ReadPermission.ExcludeOwner && !connIsOwner) || + (rp == ReadPermission.OwnerOnly && connIsOwner); + + if (!canWrite) + continue; + + int startWriterPosition = writer.Position; + sb.WriteFull(writer); + if (writer.Position != startWriterPosition) + written++; + } + + //If any where written. + if (positionAfterReserve != writer.Position) + { + int insertPosition = (positionAfterReserve - reservedBytes); + writer.InsertUInt8Unpacked(ComponentIndex, insertPosition++); + writer.InsertUInt8Unpacked(written, insertPosition); + } + else + { + writer.Remove(reservedBytes); + } + } + + + /// + /// Reads a SyncType for spawn. + /// + internal void ReadSyncTypesForSpawn(PooledReader reader) + { + byte written = reader.ReadUInt8Unpacked(); + for (int i = 0; i < written; i++) + { + byte syncTypeId = reader.ReadUInt8Unpacked(); + + if (_syncTypes.TryGetValueIL2CPP(syncTypeId, out SyncBase sb)) + sb.Read(reader, asServer: false); + else + NetworkManager.LogWarning($"SyncType not found for index {syncTypeId} on {transform.name}, component {GetType().FullName}. Remainder of packet may become corrupt."); + } + } + + + /// + /// Resets all SyncTypes for this NetworkBehaviour for server or client. + /// + internal void ResetState_SyncTypes(bool asServer) + { + if (_syncTypes != null) + { + foreach (SyncBase item in _syncTypes.Values) + item.ResetState(asServer); + } + + if (_syncTypeWriters != null) + { + foreach (SyncTypeWriter syncTypeWriter in _syncTypeWriters.Values) + syncTypeWriter.Reset(); + } + + if (asServer) + SyncTypeDirty = false; + } + + private void SyncTypes_OnDestroy() + { + CollectionCaches.StoreAndDefault(ref _syncTypes); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs.meta new file mode 100644 index 0000000..5abf9cf --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e56d5389eb07aa040b8a9ec8b0d7c597 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.SyncTypes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs new file mode 100644 index 0000000..1b8c414 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs @@ -0,0 +1,262 @@ +using FishNet.CodeGenerating; +using FishNet.Documenting; +using FishNet.Managing.Transporting; +using FishNet.Serializing.Helping; +using FishNet.Utility; +using System.Runtime.CompilerServices; +using FishNet.Managing; +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. + /// + [ExcludeSerialization] + public abstract partial class NetworkBehaviour : MonoBehaviour + { + #region Public. + /// + /// True if this NetworkBehaviour is initialized for the network. + /// + public bool IsSpawned => _networkObjectCache.IsSpawned; + + /// + /// + /// + [SerializeField, HideInInspector] + private byte _componentIndexCache = NetworkBehaviour.UNSET_NETWORKBEHAVIOUR_ID; + /// + /// 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 + /// + /// Cache of the TransportManager. + /// + private TransportManager _transportManagerCache; + /// + /// + /// + [SerializeField, HideInInspector] + private NetworkObject _networkObjectCache; + + /// + /// NetworkObject this behaviour is for. + /// + public NetworkObject NetworkObject => _networkObjectCache; + #endregion + + #region Private. + /// + /// True if initialized at some point asServer. + /// + private bool _initializedOnceServer; +#pragma warning disable CS0414 + /// + /// True if initialized at some point not asServer. + /// + private bool _initializedOnceClient; +#pragma warning restore CS0414 + #endregion + + #region Consts. + /// + /// Maximum number of allowed added NetworkBehaviours. + /// + public const byte MAXIMUM_NETWORKBEHAVIOURS = (UNSET_NETWORKBEHAVIOUR_ID - 1); + /// + /// Id for when a NetworkBehaviour is not valid. + /// + public const byte UNSET_NETWORKBEHAVIOUR_ID = byte.MaxValue; + #endregion + + /// + /// Outputs data about this NetworkBehaviour to string. + /// + /// + public override string ToString() + { + return $"Name [{gameObject.name}] ComponentId [{ComponentIndex}] NetworkObject Name [{_networkObjectCache.name}] NetworkObject Id [{_networkObjectCache.ObjectId}]"; + } + + [MakePublic] + internal virtual void NetworkInitialize___Early() { } + [MakePublic] + internal virtual void NetworkInitialize___Late() { } + + + /// + /// Preinitializes this script for the network. + /// + internal void InitializeEarly(NetworkObject nob, bool asServer) + { + _transportManagerCache = nob.TransportManager; + SyncTypes_Preinitialize(asServer); + + if (asServer) + { + InitializeRpcLinks(); + _initializedOnceServer = true; + } + else + { + if (!_initializedOnceClient && nob.EnablePrediction && _usesPrediction) + nob.RegisterPredictionBehaviourOnce(this); + + _initializedOnceClient = true; + } + } + + internal void Deinitialize(bool asServer) + { + ResetState_SyncTypes(asServer); + } + + /// + /// Called by the NetworkObject when this object is destroyed. + /// + internal void NetworkBehaviour_OnDestroy() + { + SyncTypes_OnDestroy(); + } + + /// + /// 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. + /// + [MakePublic] + [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. + /// + public virtual void ResetState(bool asServer) + { + ResetState_SyncTypes(asServer); + ResetState_Prediction(asServer); + 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(); + NetworkManagerExtensions.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/NetworkBehaviour.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs.meta new file mode 100644 index 0000000..76c6352 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d2230f9cdb1ffc9489b53875c963342d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/NetworkBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs new file mode 100644 index 0000000..d96484b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs @@ -0,0 +1,31 @@ +using FishNet.Object.Helping; +using FishNet.Transporting; + +namespace FishNet.Object +{ + + + internal struct RpcLinkType + { + /// + /// Hash for the Rpc. + /// + public readonly uint RpcHash; + /// + /// PacketId used for the Rpc type when not using links. + /// + public readonly PacketId RpcPacketId; + /// + /// PacketId sent for the RpcLink. + /// + public readonly ushort LinkPacketId; + + public RpcLinkType(uint rpcHash, PacketId packetId, ushort linkPacketId) + { + RpcHash = rpcHash; + RpcPacketId = packetId; + LinkPacketId = linkPacketId; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs.meta new file mode 100644 index 0000000..4966676 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4fa68ca6a21d08f42980dcf68f984d53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/RpcLinkType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs b/Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs new file mode 100644 index 0000000..5b1433d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs @@ -0,0 +1,12 @@ +// namespace FishNet.Object //Remove V5 +// { +// +// internal enum SyncTypeWriteType +// { +// Observers = 0, +// Owner = 1, +// All = 2, +// } +// +// +// } \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs.meta b/Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs.meta new file mode 100644 index 0000000..eaa6cd9 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e6406cc7d5fe47c44a26298145f54b00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkBehaviour/SyncTypeWriteType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject.meta b/Assets/FishNet/Runtime/Object/NetworkObject.meta new file mode 100644 index 0000000..9313821 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a70645f2a323cb648b815b5b79b56419 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs new file mode 100644 index 0000000..cdeb677 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs @@ -0,0 +1,31 @@ +using FishNet.Broadcast; +using FishNet.Managing; +using FishNet.Transporting; +using UnityEngine; + +namespace FishNet.Object +{ + public 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.LogWarning($"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/NetworkObject.Broadcast.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs.meta new file mode 100644 index 0000000..1049373 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 55d793117b52da549affcc9ec30b05c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Broadcast.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs new file mode 100644 index 0000000..bc3cf26 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs @@ -0,0 +1,182 @@ +using FishNet.Connection; +using System.Runtime.CompilerServices; +using FishNet.Serializing; +using UnityEngine; + +namespace FishNet.Object +{ + public partial class NetworkObject : MonoBehaviour + { + #region Private. + /// + /// True if OnStartServer was called. + /// + private bool _onStartServerCalled; + /// + /// True if OnStartClient was called. + /// + private bool _onStartClientCalled; + #endregion + + // ReSharper disable Unity.PerformanceAnalysis + /// + /// Called after all data is synchronized with this NetworkObject. + /// + private void InvokeStartCallbacks(bool asServer, bool invokeSyncTypeCallbacks) + { + /* Note: When invoking OnOwnership here previous owner will + * always be an empty connection, since the object is just + * now initializing. */ + + //Invoke OnStartNetwork. + bool invokeOnNetwork = (asServer || IsServerOnlyStarted || IsClientOnlyInitialized); + if (invokeOnNetwork) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].InvokeOnNetwork_Internal(start: true); + } + + //As server. + if (asServer) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnStartServer_Internal(); + _onStartServerCalled = true; + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnOwnershipServer_Internal(FishNet.Managing.NetworkManager.EmptyConnection); + } + //As client. + else + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnStartClient_Internal(); + _onStartClientCalled = true; + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnOwnershipClient_Internal(FishNet.Managing.NetworkManager.EmptyConnection); + } + + if (invokeSyncTypeCallbacks) + InvokeOnStartSyncTypeCallbacks(true); + + InvokeStartCallbacks_Prediction(asServer); + } + + /// + /// Invokes OnStartXXXX for synctypes, letting them know the NetworkBehaviour start cycle has been completed. + /// + internal void InvokeOnStartSyncTypeCallbacks(bool asServer) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].InvokeSyncTypeOnStartCallbacks(asServer); + } + + /// + /// Invokes OnStopXXXX for synctypes, letting them know the NetworkBehaviour stop cycle is about to start. + /// + internal void InvokeOnStopSyncTypeCallbacks(bool asServer) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].InvokeSyncTypeOnStopCallbacks(asServer); + } + + /// + /// Invokes events to be called after OnServerStart. + /// This is made one method to save instruction calls. + /// + /// + internal void OnSpawnServer(NetworkConnection conn) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].SendBufferedRpcs(conn); + + for (int i = 0; i < NetworkBehaviours.Count; 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.Count; i++) + NetworkBehaviours[i].OnDespawnServer(conn); + } + + // ReSharper disable Unity.PerformanceAnalysis + /// + /// Invokes OnStop callbacks. + /// + internal void InvokeStopCallbacks(bool asServer, bool invokeSyncTypeCallbacks) + { + InvokeStopCallbacks_Prediction(asServer); + + if (invokeSyncTypeCallbacks) + InvokeOnStopSyncTypeCallbacks(asServer); + + if (asServer && _onStartServerCalled) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnStopServer_Internal(); + + if (!_onStartClientCalled) + InvokeOnNetwork(); + + _onStartServerCalled = false; + } + else if (!asServer && _onStartClientCalled) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnStopClient_Internal(); + + /* Only invoke OnNetwork if server start isn't called, otherwise + * that means this is still intialized on the server. This would + * happen if the object despawned for the clientHost but not on the + * server. */ + if (!_onStartServerCalled) + InvokeOnNetwork(); + + _onStartClientCalled = false; + } + + void InvokeOnNetwork() + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].InvokeOnNetwork_Internal(start: false); + } + } + + /// + /// Invokes OnOwnership callbacks when ownership changes. + /// This is not to be called when assigning ownership during a spawn message. + /// + private void InvokeManualOwnershipChange(NetworkConnection prevOwner, bool asServer) + { + if (asServer) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnOwnershipServer_Internal(prevOwner); + + WriteSyncTypesForManualOwnershipChange(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 && !IsServerStarted) && (prevOwner == Owner)); + if (!blockInvoke) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].OnOwnershipClient_Internal(prevOwner); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs.meta new file mode 100644 index 0000000..4872409 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8e9fbf0d6eb10e94d892dd4e817030bc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Callbacks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs new file mode 100644 index 0000000..954793e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs @@ -0,0 +1,296 @@ +using FishNet.Component.Observing; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Observing; +using System; +using System.Collections.Generic; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Object +{ + public partial class NetworkObject : MonoBehaviour + { + #region Public. + /// + /// Called when the clientHost gains or loses visibility of this object. + /// Boolean value will be true if clientHost has visibility. + /// + public event HostVisibilityUpdatedDelegate OnHostVisibilityUpdated; + + /// + /// + /// + /// True if clientHost was known to have visibility of the object prior to this invoking. + /// True if the clientHost now has visibility of the object. + public delegate void HostVisibilityUpdatedDelegate(bool prevVisible, bool nextVisible); + + /// + /// 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. + /// + [HideInInspector] + public HashSet Observers = new(); + #endregion + + #region Internal. + /// + /// Current HashGrid entry this belongs to. + /// + internal GridEntry HashGridEntry; + /// + /// Last tick an observer was added. + /// + internal uint ObserverAddedTick = TimeManager.UNSET_TICK; + #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 List _renderers; + /// + /// True if renderers have been looked up. + /// + private bool _renderersPopulated; + /// + /// Last visibility value for clientHost on this object. + /// + private bool _lastClientHostVisibility; + /// + /// HashGrid for this object. + /// + private HashGrid _hashGrid; + /// + /// Next time this object may update it's position for HashGrid. + /// + private float _nextHashGridUpdateTime; + /// + /// True if this gameObject is static. + /// + private bool _isStatic; + /// + /// Current grid position. + /// + private Vector2Int _hashGridPosition = HashGrid.UnsetGridPosition; + #endregion + + /// + /// Updates Objects positions in the HashGrid for this Networkmanager. + /// + internal void UpdateForNetworkObject(bool force) + { + if (_hashGrid == null) + return; + if (_isStatic) + return; + + float unscaledTime = Time.unscaledTime; + //Not enough time has passed to update. + if (!force && unscaledTime < _nextHashGridUpdateTime) + return; + + const float updateInterval = 1f; + _nextHashGridUpdateTime = unscaledTime + updateInterval; + Vector2Int newPosition = _hashGrid.GetHashGridPosition(this); + if (newPosition != _hashGridPosition) + { + _hashGridPosition = newPosition; + HashGridEntry = _hashGrid.GetGridEntry(newPosition); + } + } + + /// + /// Updates cached renderers used to managing clientHost visibility. + /// + /// True to also update visibility if clientHost. + public void UpdateRenderers(bool updateVisibility = true) + { + InitializeRendererCollection(force: true, updateVisibility); + } + + /// + /// Sets the renderer visibility for clientHost. + /// + /// True if renderers are to be visibile. + /// True to skip blocking checks. + public void SetRenderersVisible(bool visible, bool force = false) + { + if (!force && !NetworkObserver.UpdateHostVisibility) + return; + + UpdateRenderVisibility(visible); + } + + /// + /// Updates visibilites on renders without checks. + /// + /// + private void UpdateRenderVisibility(bool visible) + { + InitializeRendererCollection(force: false, updateVisibility: false); + + List rs = _renderers; + for (int i = 0; i < rs.Count; i++) + { + Renderer r = rs[i]; + if (r == null) + { + _renderers.RemoveAt(i); + i--; + } + else + { + r.enabled = visible; + } + } + + if (OnHostVisibilityUpdated != null) + OnHostVisibilityUpdated.Invoke(_lastClientHostVisibility, visible); + _lastClientHostVisibility = visible; + } + + /// + /// If needed Renderers collection is initialized and populated. + /// + private void InitializeRendererCollection(bool force, bool updateVisibility) + { + if (!force && _renderersPopulated) + return; + + List cache = CollectionCaches.RetrieveList(); + GetComponentsInChildren(includeInactive: true, cache); + + _renderers = new(); + + foreach (Renderer r in cache) + { + if (r.enabled) + _renderers.Add(r); + } + + CollectionCaches.Store(cache); + + /* Intentionally set before event call. This is to prevent + * a potential endless loop should the user make another call + * to this objects renderer API from the event, resulting in + * the population repeating. */ + _renderersPopulated = true; + + if (updateVisibility) + UpdateRenderVisibility(_lastClientHostVisibility); + } + + /// + /// 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; + } + + //Update hashgrid if needed. + UpdateForNetworkObject(!timedOnly); + + 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 (TimeManager != null) + ObserverAddedTick = TimeManager.LocalTick; + + if (OnObserversActive != null) + { + if ((Observers.Count > 0 && startCount == 0) || Observers.Count == 0 && startCount > 0) + OnObserversActive.Invoke(this); + } + } + + /// + /// Resets this object to starting values. + /// + private void ResetState_Observers(bool asServer) + { + //As server or client it's safe to reset this value. + ObserverAddedTick = TimeManager.UNSET_TICK; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs.meta new file mode 100644 index 0000000..6c9e687 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 816dbea70a70ab949a44f485155f0087 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Observers.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs new file mode 100644 index 0000000..919c939 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs @@ -0,0 +1,464 @@ +#define NEW_RECONCILE_TEST +using System; +using FishNet.Component.Prediction; +using FishNet.Component.Transforming; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Object.Prediction; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using FishNet.Connection; +using FishNet.Managing.Server; +using UnityEngine; + +#pragma warning disable CS0618 // Type or member is obsolete + +namespace FishNet.Object +{ + public partial class NetworkObject : MonoBehaviour + { + #region Types. + /// + /// Type of prediction movement being used. + /// + [System.Serializable] + internal enum PredictionType : byte + { + Other = 0, + Rigidbody = 1, + Rigidbody2D = 2 + } + #endregion + + #region Public. + /// + /// True if a reconcile is occuring on any NetworkBehaviour that is on or nested of this NetworkObject. Runtime NetworkBehaviours are not included, such as if you child a NetworkObject to another at runtime. + /// + public bool IsObjectReconciling { get; internal set; } + + /// + /// Graphical smoother to use when using set for owner. + /// + [Obsolete("This field will be removed in v5. Instead reference NetworkTickSmoother on each graphical object used.")] + public TransformTickSmoother PredictionSmoother { get; private set; } + #endregion + + #region Internal. + /// + /// Pauses and unpauses rigidbodies when they do not have data to reconcile to. + /// + public RigidbodyPauser RigidbodyPauser => _rigidbodyPauser; + + private RigidbodyPauser _rigidbodyPauser; + #endregion + + #region Serialized. + /// + /// True if this object uses prediciton methods. + /// + public bool EnablePrediction => _enablePrediction; + + [Tooltip("True if this object uses prediction methods.")] + [SerializeField] + private bool _enablePrediction; + /// + /// What type of component is being used for prediction? If not using rigidbodies set to other. + /// + [Tooltip("What type of component is being used for prediction? If not using rigidbodies set to other.")] + [SerializeField] + private PredictionType _predictionType = PredictionType.Other; + /// + /// Object containing graphics when using prediction. This should be child of the predicted root. + /// + [Tooltip("Object containing graphics when using prediction. This should be child of the predicted root.")] + [SerializeField] + private Transform _graphicalObject; + + /// + /// Gets the current graphical object for prediction. + /// + /// + public Transform GetGraphicalObject() => _graphicalObject; + + /// + /// Sets a new graphical object for prediction. + /// + /// + public void SetGraphicalObject(Transform t) + { + _graphicalObject = t; + InitializeTickSmoother(); + } + + /// + /// True to detach and re-attach the graphical object at runtime when the client initializes/deinitializes the item. + /// This can resolve camera jitter or be helpful objects child of the graphical which do not handle reconiliation well, such as certain animation rigs. + /// Transform is detached after OnStartClient, and reattached before OnStopClient. + /// + [Tooltip("True to detach and re-attach the graphical object at runtime when the client initializes/deinitializes the item. This can resolve camera jitter or be helpful objects child of the graphical which do not handle reconiliation well, such as certain animation rigs. Transform is detached after OnStartClient, and reattached before OnStopClient.")] + [SerializeField] + private bool _detachGraphicalObject; + + /// + /// True to forward replicate and reconcile states to all clients. This is ideal with games where you want all clients and server to run the same inputs. False to only use prediction on the owner, and synchronize to spectators using other means such as a NetworkTransform. + /// + public bool EnableStateForwarding => (_enablePrediction && _enableStateForwarding); + + [Tooltip("True to forward replicate and reconcile states to all clients. This is ideal with games where you want all clients and server to run the same inputs. False to only use prediction on the owner, and synchronize to spectators using other means such as a NetworkTransform.")] + [SerializeField] + private bool _enableStateForwarding = true; + /// + /// NetworkTransform to configure for prediction. Specifying this is optional. + /// + [Tooltip("NetworkTransform to configure for prediction. Specifying this is optional.")] + [SerializeField] + private NetworkTransform _networkTransform; + /// + /// How many ticks to interpolate graphics on objects owned by the client. Typically low as 1 can be used to smooth over the frames between ticks. + /// + [Tooltip("How many ticks to interpolate graphics on objects owned by the client. Typically low as 1 can be used to smooth over the frames between ticks.")] + [Range(1, byte.MaxValue)] + [SerializeField] + private byte _ownerInterpolation = 1; + /// + /// Properties of the graphicalObject to smooth when owned. + /// + [SerializeField] + private TransformPropertiesFlag _ownerSmoothedProperties = (TransformPropertiesFlag)~(-1 << 8); + /// + /// Interpolation amount of adaptive interpolation to use on non-owned objects. Higher levels result in more interpolation. When off spectatorInterpolation is used; when on interpolation based on strength and local client latency is used. + /// + [Tooltip("Interpolation amount of adaptive interpolation to use on non-owned objects. Higher levels result in more interpolation. When off spectatorInterpolation is used; when on interpolation based on strength and local client latency is used.")] + [SerializeField] + private AdaptiveInterpolationType _adaptiveInterpolation = AdaptiveInterpolationType.Low; + /// + /// Properties of the graphicalObject to smooth when the object is spectated. + /// + [SerializeField] + private TransformPropertiesFlag _spectatorSmoothedProperties = (TransformPropertiesFlag)~(-1 << 8); + /// + /// How many ticks to interpolate graphics on objects when not owned by the client. + /// + [Tooltip("How many ticks to interpolate graphics on objects when not owned by the client.")] + [Range(1, byte.MaxValue)] + [SerializeField] + private byte _spectatorInterpolation = 2; + /// + /// True to enable teleport threshhold. + /// + [Tooltip("True to enable teleport threshhold.")] + [SerializeField] + private bool _enableTeleport; + /// + /// Distance the graphical object must move between ticks to teleport the transform properties. + /// + [Tooltip("Distance the graphical object must move between ticks to teleport the transform properties.")] + [Range(0.001f, ushort.MaxValue)] + [SerializeField] + private float _teleportThreshold = 1f; + #endregion + + #region Private. + /// + /// NetworkBehaviours which use prediction. + /// + private List _predictionBehaviours = new(); + #endregion + + private void TimeManager_OnUpdate_Prediction() + { + if (!_enablePrediction) + return; + + if (PredictionSmoother != null) + PredictionSmoother.OnUpdate(); + } + + private void InitializePredictionEarly(NetworkManager manager, bool asServer) + { + if (!_enablePrediction) + return; + + if (!_enableStateForwarding && _networkTransform != null) + _networkTransform.ConfigureForPrediction(_predictionType); + + if (asServer) + return; + + InitializeSmoothers(); + + if (_predictionBehaviours.Count > 0) + { + ChangePredictionSubscriptions(true, manager); + foreach (NetworkBehaviour item in _predictionBehaviours) + item.Preinitialize_Prediction(asServer); + } + } + + private void Deinitialize_Prediction(bool asServer) + { + if (!_enablePrediction) + return; + + DeinitializeSmoothers(); + /* Only the client needs to unsubscribe from these but + * asServer may not invoke as false if the client is suddenly + * dropping their connection. */ + if (_predictionBehaviours.Count > 0) + { + ChangePredictionSubscriptions(subscribe: false, NetworkManager); + foreach (NetworkBehaviour item in _predictionBehaviours) + item.Deinitialize_Prediction(asServer); + } + } + + /// + /// Changes subscriptions to use callbacks for prediction. + /// + private void ChangePredictionSubscriptions(bool subscribe, NetworkManager manager) + { + if (manager == null) + return; + + if (subscribe) + { + manager.PredictionManager.OnPreReconcile += PredictionManager_OnPreReconcile; + manager.PredictionManager.OnReconcile += PredictionManager_OnReconcile; + manager.PredictionManager.OnReplicateReplay += PredictionManager_OnReplicateReplay; + manager.PredictionManager.OnPostReplicateReplay += PredictionManager_OnPostReplicateReplay; + manager.PredictionManager.OnPostReconcile += PredictionManager_OnPostReconcile; + manager.TimeManager.OnPreTick += TimeManager_OnPreTick; + manager.TimeManager.OnPostTick += TimeManager_OnPostTick; + } + else + { + manager.PredictionManager.OnPreReconcile -= PredictionManager_OnPreReconcile; + manager.PredictionManager.OnReconcile -= PredictionManager_OnReconcile; + manager.PredictionManager.OnReplicateReplay -= PredictionManager_OnReplicateReplay; + manager.PredictionManager.OnPostReplicateReplay -= PredictionManager_OnPostReplicateReplay; + manager.PredictionManager.OnPostReconcile -= PredictionManager_OnPostReconcile; + manager.TimeManager.OnPreTick -= TimeManager_OnPreTick; + manager.TimeManager.OnPostTick -= TimeManager_OnPostTick; + } + } + + /// + /// Initializes tick smoothing. + /// + private void InitializeSmoothers() + { + bool usesRb = (_predictionType == PredictionType.Rigidbody); + bool usesRb2d = (_predictionType == PredictionType.Rigidbody2D); + if (usesRb || usesRb2d) + { + _rigidbodyPauser = ResettableObjectCaches.Retrieve(); + RigidbodyType rbType = (usesRb) ? RigidbodyType.Rigidbody : RigidbodyType.Rigidbody2D; + _rigidbodyPauser.UpdateRigidbodies(transform, rbType, true); + } + + if (_graphicalObject == null) + { + NetworkManagerExtensions.Log($"GraphicalObject is null on {gameObject.name}. This may be intentional, and acceptable, if you are smoothing between ticks yourself. Otherwise consider assigning the GraphicalObject field."); + } + else + { + if (PredictionSmoother == null) + PredictionSmoother = ResettableObjectCaches.Retrieve(); + InitializeTickSmoother(); + } + } + + /// + /// Initializes the tick smoother. + /// + private void InitializeTickSmoother() + { + if (PredictionSmoother == null) + return; + float teleportT = (_enableTeleport) ? _teleportThreshold : MoveRates.UNSET_VALUE; + PredictionSmoother.InitializeNetworked(this, _graphicalObject, _detachGraphicalObject, teleportT, (float)TimeManager.TickDelta, _ownerInterpolation, _ownerSmoothedProperties, _spectatorInterpolation, _spectatorSmoothedProperties, _adaptiveInterpolation); + } + + /// + /// Initializes tick smoothing. + /// + private void DeinitializeSmoothers() + { + if (PredictionSmoother != null) + { + PredictionSmoother.Deinitialize(); + ResettableObjectCaches.Store(PredictionSmoother); + PredictionSmoother = null; + ResettableObjectCaches.StoreAndDefault(ref _rigidbodyPauser); + } + } + + private void InvokeStartCallbacks_Prediction(bool asServer) + { + if (_predictionBehaviours.Count == 0) + return; + + if (!asServer) + { + TimeManager.OnUpdate += TimeManager_Update; + if (PredictionSmoother != null) + PredictionSmoother.OnStartClient(); + } + } + + private void InvokeStopCallbacks_Prediction(bool asServer) + { + if (_predictionBehaviours.Count == 0) + return; + + if (!asServer) + { + if (TimeManager != null) + TimeManager.OnUpdate -= TimeManager_Update; + if (PredictionSmoother != null) + PredictionSmoother.OnStopClient(); + } + } + + private void TimeManager_OnPreTick() + { + if (PredictionSmoother != null) + PredictionSmoother.OnPreTick(); + } + + private void PredictionManager_OnPostReplicateReplay(uint clientTick, uint serverTick) + { + if (PredictionSmoother != null) + PredictionSmoother.OnPostReplicateReplay(clientTick); + } + + private void TimeManager_OnPostTick() + { + if (PredictionSmoother != null) + PredictionSmoother.OnPostTick(NetworkManager.TimeManager.LocalTick); + } + + private void PredictionManager_OnPreReconcile(uint clientTick, uint serverTick) + { + if (PredictionSmoother != null) + PredictionSmoother.OnPreReconcile(); + } + + private void PredictionManager_OnReconcile(uint clientReconcileTick, uint serverReconcileTick) + { + /* Tell all prediction behaviours to set/validate their + * reconcile data now. This will use reconciles from the server + * whenever possible, and local reconciles if a server reconcile + * is not available. */ + for (int i = 0; i < _predictionBehaviours.Count; i++) + _predictionBehaviours[i].Reconcile_Client_Start(); + + /* If still not reconciling then pause rigidbody. + * This shouldn't happen unless the user is not calling + * reconcile at all. */ + if (!IsObjectReconciling) + { + if (_rigidbodyPauser != null) + _rigidbodyPauser.Pause(); + } + } + + private void PredictionManager_OnPostReconcile(uint clientReconcileTick, uint serverReconcileTick) + { + for (int i = 0; i < _predictionBehaviours.Count; i++) + _predictionBehaviours[i].Reconcile_Client_End(); + + /* Unpause rigidbody pauser. It's okay to do that here rather + * than per NB, where the pausing occurs, because once here + * the entire object is out of the replay cycle so there's + * no reason to try and unpause per NB. */ + if (_rigidbodyPauser != null) + _rigidbodyPauser.Unpause(); + IsObjectReconciling = false; + } + + private void PredictionManager_OnReplicateReplay(uint clientTick, uint serverTick) + { + uint replayTick = (IsOwner) ? clientTick : serverTick; + for (int i = 0; i < _predictionBehaviours.Count; i++) + _predictionBehaviours[i].Replicate_Replay_Start(replayTick); + } + + /// + /// Registers a NetworkBehaviour that uses prediction with the NetworkObject. + /// This method should only be called once throughout the entire lifetime of this object. + /// + internal void RegisterPredictionBehaviourOnce(NetworkBehaviour nb) + { + _predictionBehaviours.Add(nb); + } + + /// + /// Clears replication queue inserting them into the past replicates history when possible. + /// This should only be called when client only. + /// + internal void EmptyReplicatesQueueIntoHistory() + { + for (int i = 0; i < _predictionBehaviours.Count; i++) + _predictionBehaviours[i].EmptyReplicatesQueueIntoHistory_Start(); + } + + /// + /// Sets the last tick a NetworkBehaviour replicated with. + /// + /// True to set unordered value, false to set ordered. + internal void SetReplicateTick(uint value, bool createdReplicate) + { + if (createdReplicate && Owner.IsValid) + Owner.ReplicateTick.Update(NetworkManager.TimeManager, value, EstimatedTick.OldTickOption.Discard); + } + + /// + /// ResetState for prediction values. + /// + private void ResetState_Prediction(bool asServer) { } + } + + /// + /// Place this component on your NetworkManager object to remove ownership of objects for a disconnecting client. + /// This prevents any owned object from being despawned when the owner disconnects. + /// + public class GlobalPreserveOwnedObjects : MonoBehaviour + { + private void Awake() + { + ServerManager sm = GetComponent(); + sm.Objects.OnPreDestroyClientObjects += Objects_OnPreDestroyClientObjects; + } + + protected virtual void Objects_OnPreDestroyClientObjects(NetworkConnection conn) + { + foreach (NetworkObject networkObject in conn.Objects) + networkObject.RemoveOwnership(); + } + } + + /// + /// Place this component on NetworkObjects you wish to remove ownership on for a disconnecting owner. + /// This prevents the object from being despawned when the owner disconnects. + /// + public class NetworkPreserveOwnedObjects : NetworkBehaviour + { + public override void OnStartServer() + { + ServerManager.Objects.OnPreDestroyClientObjects += OnPreDestroyClientObjects; + } + + public override void OnStopServer() + { + if (ServerManager != null) + ServerManager.Objects.OnPreDestroyClientObjects -= OnPreDestroyClientObjects; + } + + private void OnPreDestroyClientObjects(NetworkConnection conn) + { + if (conn == Owner) + RemoveOwnership(); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs.meta new file mode 100644 index 0000000..9768ef2 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 29401c9ff84500647a0d8718a39f28d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Prediction.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs new file mode 100644 index 0000000..b3e6c58 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs @@ -0,0 +1,408 @@ +using FishNet.CodeAnalysis.Annotations; +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.Serializing.Helping; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Object +{ + public partial class NetworkObject : MonoBehaviour + { + #region Public. + #region Obsoletes + [Obsolete("Use IsClientOnlyInitialized. Note the difference between IsClientOnlyInitialized and IsClientOnlyStarted.")] + public bool IsClientOnly => IsClientOnlyInitialized; + [Obsolete("Use IsServerOnlyInitialized. Note the difference between IsServerOnlyInitialized and IsServerOnlyStarted.")] + public bool IsServerOnly => IsServerOnlyInitialized; + [Obsolete("Use IsHostInitialized. Note the difference between IsHostInitialized and IsHostStarted.")] + public bool IsHost => IsHostInitialized; + [Obsolete("Use IsClientInitialized. Note the difference between IsClientInitialized and IsClientStarted.")] + public bool IsClient => IsClientInitialized; + [Obsolete("Use IsServerInitialized. Note the difference between IsServerInitialized and IsServerStarted.")] + public bool IsServer => IsServerInitialized; + #endregion + + /// + /// True if despawning without object pooling, or if OnDestroy was invoked on this NetworkObject. As clientHost this value becomes true when previous criteria are met and server begins to deinitialize the object. + /// + /// This can be useful for checking if you wish to perform certain actions within OnStopNetwork based on destroying status. + public bool IsDestroying { get; private set; } + + /// + /// Sets IsDestroying to true if DespawnType is not pooled. When DespawnType is not specified default DespawnType is checked. + /// + internal void SetIsDestroying(DespawnType? despawnType = null) + { + if (despawnType.HasValue) + { + if (despawnType.Value == DespawnType.Destroy) + IsDestroying = true; + } + else if (GetDefaultDespawnType() == DespawnType.Destroy) + { + IsDestroying = true; + } + } + + /// + /// 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 if this object has been initialized on the client side. + /// This is set true right before client start callbacks and after stop callbacks. + /// + public bool IsClientInitialized { get; private set; } + /// + /// True if the client is started and authenticated. This will return true on clientHost even if the object has not initialized yet for the client. + /// To check if this object has been initialized for the client use IsClientInitialized. + /// + public bool IsClientStarted => (NetworkManager == null) ? false : NetworkManager.IsClientStarted; + /// + /// True if this object has been initialized only on the server side. + /// This is set true right before server start callbacks and after stop callbacks. + /// + public bool IsClientOnlyInitialized => (!IsServerInitialized && IsClientInitialized); + /// + /// True if only the client is started and authenticated. + /// + public bool IsClientOnlyStarted => (IsClientStarted && !IsServerStarted); + /// + /// True if this object has been initialized on the server side. + /// This is set true right before server start callbacks and after stop callbacks. + /// + public bool IsServerInitialized { get; private set; } + /// + /// True if the server is active. This will return true on clientHost even if the object is being deinitialized on the server. + /// To check if this object has been initialized for the server use IsServerInitialized. + /// + public bool IsServerStarted => (NetworkManager == null) ? false : NetworkManager.IsServerStarted; + /// + /// True if this object has been initialized only on the server side. + /// This is set true right before server start callbacks and after stop callbacks. + /// + public bool IsServerOnlyInitialized => (IsServerInitialized && !IsClientInitialized); + /// + /// True if only the server is started. + /// + public bool IsServerOnlyStarted => (IsServerStarted && !IsClientStarted); + /// + /// True if client and server are started. + /// + public bool IsHostStarted => (IsClientStarted && IsServerStarted); + /// + /// True if this object has been initialized on the server and client side. + /// + public bool IsHostInitialized => (IsClientInitialized && IsServerInitialized); + /// + /// True if client nor server are started. + /// + public bool IsOffline => (!IsClientStarted && !IsServerStarted); + /// + /// True if a reconcile is occuring on the PredictionManager. Note the difference between this and IsBehaviourReconciling. + /// + public bool IsManagerReconciling => PredictionManager.IsReconciling; + + /// + /// True if the local client is currently using a PredictedOwner component on this object to take ownership. + /// + public bool IsTakingOwnership => (PredictedOwner != null && PredictedOwner.TakingOwnership); + + /// + /// True if the local client is the owner of this object. + /// This will only return true if IsClientInitialized is also true. You may check ownership status regardless of client initialized state by using Owner.IsLocalClient. + /// + [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", "")] + 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 (!IsClientInitialized) + return false; + + return Owner.IsLocalClient; + } + } + /// + /// True if IsOwner, or if IsServerInitialized with no Owner. + /// + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "OnStartServer", "")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "OnStartNetwork", " Use (base.Owner.IsLocalClient || (base.IsServerInitialized && !Owner.Isvalid) instead.")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "Awake", "")] + [PreventUsageInside("global::FishNet.Object.NetworkBehaviour", "Start", "")] + public bool IsController => (IsOwner || (IsServerInitialized && !Owner.IsValid)); + + [Obsolete("Use IsController.")] + public bool HasAuthority => IsController; + /// + /// + /// + 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() : 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.Count) + { + 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."; + 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) + { + if (NetworkManager != 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) + { + if (NetworkManager != 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; + if (NetworkManager != null) + NetworkManager.ServerManager.Despawn(nob, despawnType); + } + + /// + /// Spawns an object over the network. Only call from the server. + /// + public void Spawn(GameObject go, NetworkConnection ownerConnection = null, UnityEngine.SceneManagement.Scene scene = default) + { + if (NetworkManager != null) + NetworkManager.ServerManager.Spawn(go, ownerConnection, scene); + } + + /// + /// Spawns an object over the network. Only call from the server. + /// + public void Spawn(NetworkObject nob, NetworkConnection ownerConnection = null, UnityEngine.SceneManagement.Scene scene = default) + { + if (NetworkManager != null) + NetworkManager.ServerManager.Spawn(nob, ownerConnection, scene); + } + + [Obsolete("Use SetLocalOwnership(NetworkConnection, bool).")] + public void SetLocalOwnership(NetworkConnection caller) => SetLocalOwnership(caller, recursive: false); + + /// + /// Takes ownership of this object and child network objects, allowing immediate control. + /// + /// Connection to give ownership to. + public void SetLocalOwnership(NetworkConnection caller, bool recursive) + { + NetworkConnection prevOwner = Owner; + SetOwner(caller); + + int count; + count = NetworkBehaviours.Count; + for (int i = 0; i < count; i++) + NetworkBehaviours[i].OnOwnershipClient_Internal(prevOwner); + + if (recursive) + { + List allNested = GetNetworkObjects(GetNetworkObjectOption.AllNestedRecursive); + + foreach (NetworkObject nob in allNested) + nob.SetLocalOwnership(caller, recursive: true); + + CollectionCaches.Store(allNested); + } + } + + #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); + + /// + /// Tries to registers a new component to this NetworkManager. + /// This will not register the instance if another already exists. + /// + /// Type to register. + /// Reference of the component being registered. + /// True if was able to register, false if an instance is already registered. + public bool TryRegisterInstance(T component) where T : UnityEngine.Component => NetworkManager.TryRegisterInstance(component); + + /// + /// Returns class of type from registered instances. + /// + /// Outputted component. + /// Type to get. + /// True if was able to get instance. + public bool TryGetInstance(out T component) where T : UnityEngine.Component => NetworkManager.TryGetInstance(out component); + + /// + /// Unregisters a component from this NetworkManager. + /// + /// Type to unregister. + public void UnregisterInstance() where T : UnityEngine.Component => NetworkManager.UnregisterInstance(); + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs.meta new file mode 100644 index 0000000..5449f91 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fd30b4b61d50d01499c94a63a6eeb863 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.QOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs new file mode 100644 index 0000000..7bbfd3d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs @@ -0,0 +1 @@ +//Remove in V5 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs.meta new file mode 100644 index 0000000..843a63a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: be0a4b0a32b02f64495ba3b1d22f89c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.ReferenceIds.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs new file mode 100644 index 0000000..0b1d9cd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Object +{ + public 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() + { + //if (NetworkManager != null) + NetworkManager.ClientManager.Objects.RemoveLinkIndexes(_rpcLinkIndexes); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs.meta new file mode 100644 index 0000000..733f862 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8b2f6927cf3ef254d91b89e5f99a92b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.RpcLinks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs new file mode 100644 index 0000000..40861f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs @@ -0,0 +1,238 @@ +//This file contains values serialized in editor or once at runtime. + +using UnityEngine; +using System; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using FishNet.Managing; +using FishNet.Utility.Extension; + + +#if UNITY_EDITOR +using UnityEditor.Experimental.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor; +#endif + +namespace FishNet.Object +{ + public partial class NetworkObject : MonoBehaviour + { + #region Public. + /// + /// Networked PrefabId assigned to this Prefab. + /// + [field: SerializeField, HideInInspector] + public ushort PrefabId { get; internal set; } = NetworkObject.UNSET_PREFABID_VALUE; + + /// + /// Spawn collection to use assigned to this Prefab. + /// + [field: SerializeField, HideInInspector] + public ushort SpawnableCollectionId { get; internal set; } = 0; + + /// + /// Sets SceneId value. This is not synchronized automatically. + /// + /// + public void SetSceneId(ulong sceneId) => SceneId = sceneId; + + /// + /// 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 + + #region Internal. + /// + /// Network Id for this scene object. + /// + [field: SerializeField, HideInInspector] + internal ulong SceneId { get; private set; } + + /// + /// + /// + [SerializeField, HideInInspector] + internal TransformProperties SerializedTransformProperties = new(); + #endregion + + #region Private. + /// + /// Last time sceneIds were built automatically. + /// + [System.NonSerialized] + private static double _lastSceneIdAutomaticRebuildTime; + #endregion + + /// + /// Removes SceneObject state. + /// This may only be called at runtime. + /// + internal void ClearRuntimeSceneObject() + { + if (!Application.isPlaying) + { + NetworkManagerExtensions.LogError($"ClearRuntimeSceneObject may only be called at runtime."); + return; + } + + SceneId = NetworkObject.UNSET_SCENEID_VALUE; + } + + +#if UNITY_EDITOR + private void OnApplicationQuit() + { + _lastSceneIdAutomaticRebuildTime = 0; + } + + /// + /// Tries to generate a SceneIds for NetworkObjects in a scene. + /// + internal static List CreateSceneId(UnityEngine.SceneManagement.Scene scene, bool force, out int changed) + { + changed = 0; + + if (Application.isPlaying) + return new(); + if (!scene.IsValid()) + return new(); + if (!scene.isLoaded) + return new(); + + HashSet setIds = new(); + uint scenePathHash = scene.path.GetStableHashU32(); + List sceneNobs = new(); + + Scenes.GetSceneNetworkObjects(scene, firstOnly: false, errorOnDuplicates: false, ignoreUnsetSceneIds: false, ref sceneNobs); + System.Random rnd = new(); + + //NetworkObjects which need their Ids rebuilt. + List rebuildingNobs = new(); + + foreach (NetworkObject item in sceneNobs) + { + bool canGenerate = (!item.IsSceneObject || !setIds.Add(item.SceneId)); + /* If an Id has not been generated yet or if it + * already exist then rebuild for this object. */ + if (force || canGenerate) + { + item.SceneId = NetworkObject.UNSET_SCENEID_VALUE; + rebuildingNobs.Add(item); + } + } + + foreach (NetworkObject item in rebuildingNobs) + { + ulong nextSceneId = NetworkObject.UNSET_SCENEID_VALUE; + while (nextSceneId == NetworkObject.UNSET_SCENEID_VALUE || setIds.Contains(nextSceneId)) + { + uint rndId = (uint)(rnd.Next(int.MinValue, int.MaxValue) + int.MaxValue); + nextSceneId = CombineHashes(scenePathHash, rndId); + } + + ulong CombineHashes(uint a, uint b) + { + return (b | a); + } + + setIds.Add(nextSceneId); + changed++; + item.SceneId = nextSceneId; + EditorUtility.SetDirty(item); + } + + return sceneNobs; + } + + /// + /// Tries to generate a SceneId. + /// + private void CreateSceneId(bool force) + { + 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 = NetworkObject.UNSET_SCENEID_VALUE; + return; + } + + /* If building then only check if + * scene networkobjects have their sceneIds + * missing. */ + if (BuildPipeline.isBuildingPlayer) + { + //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)) + //If here this is a sceneObject, but sceneId is not set. + if (!IsSceneObject) + throw new InvalidOperationException($"Networked GameObject {gameObject.name} in scene {gameObject.scene.path} is missing a SceneId. Use the Fish-Networking menu -> Utility -> Reserialize NetworkObjects > Reserialize Scenes. 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."); + } + //If not building check to rebuild sceneIds this for object and the scene its in. + else + { + double realtime = EditorApplication.timeSinceStartup; + //Only do this once every Xms to prevent excessive rebiulds. + if (realtime - _lastSceneIdAutomaticRebuildTime < 0.250d) + return; + + //Not in a scene, another prefab check. + //Stored on disk, so is a prefab. Somehow prefabutility missed it. + if (PrefabUtility.IsPartOfPrefabAsset(this) || IsEditingInPrefabMode() || !gameObject.scene.IsValid() || EditorUtility.IsPersistent(this)) + return; + + _lastSceneIdAutomaticRebuildTime = realtime; + + CreateSceneId(gameObject.scene, force, out _); + } + } + + 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; + } + + private void ReferenceIds_Reset() + { + CreateSceneId(force: false); + } +#endif + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs.meta new file mode 100644 index 0000000..dffea39 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2d0b7f2fb91aa9243819ba0c5f783dd5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.Serialized.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs new file mode 100644 index 0000000..0de551e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs @@ -0,0 +1,32 @@ +using System.Runtime.CompilerServices; +using FishNet.Connection; +using FishNet.Object.Synchronizing; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Object +{ + public partial class NetworkObject : MonoBehaviour + { + /// + /// Writes SyncTypes for previous and new owner where permissions apply. + /// + + private void WriteSyncTypesForManualOwnershipChange(NetworkConnection prevOwner) + { + if (prevOwner.IsActive) + WriteForConnection(prevOwner, ReadPermission.ExcludeOwner); + if (Owner.IsActive) + WriteForConnection(Owner, ReadPermission.OwnerOnly); + + void WriteForConnection(NetworkConnection conn, ReadPermission permission) + { + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].WriteSyncTypesForConnection(conn, permission); + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs.meta new file mode 100644 index 0000000..2eeace0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aacec7a84fa0ebe45a6a385a33863782 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.SyncTypes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs new file mode 100644 index 0000000..4721df8 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs @@ -0,0 +1,1431 @@ +using System; +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 FishNet.Component.Ownership; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine.Serialization; +#if UNITY_EDITOR +using UnityEditor; +#endif + +namespace FishNet.Object +{ + public class NetworkObjectIdComparer : IEqualityComparer + { + public bool Equals(NetworkObject valueA, NetworkObject valueB) + { + bool aNull = (valueA is null); + bool bNull = (valueB is null); + //One null, one isn't. + if (aNull != bNull) + return false; + //Both null. + if (aNull && bNull) + return true; + + //If here neither are null. + return (valueA.ObjectId == valueB.ObjectId); + } + + public int GetHashCode(NetworkObject obj) + { + return obj.ObjectId; + } + } + + [DefaultExecutionOrder(short.MinValue + 1)] + [DisallowMultipleComponent] + public partial class NetworkObject : MonoBehaviour, IOrderable + { + #region Public. + /// + /// True if this object is nested. + /// This value is automatically applied for prefabs and scene objects during serialization. However, if changing parents at runtime use NetworkObject.SetParent(). + /// + [field: SerializeField, HideInInspector] + public bool IsNested { get; private set; } + /// + /// True if was set as nested during initialization. + /// + public bool IsInitializedNested => (InitializedParentNetworkBehaviour != null); + + /// + /// 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 a scene object or prefab that is active by default. + /// + [field: SerializeField, HideInInspector] + internal bool WasActiveDuringEdit; + /// + /// This value is used to ensure users have reserialized NetworkObjects to apply WasActiveDuringEdit. //Remove V5. + /// + [field: SerializeField, HideInInspector] + internal bool WasActiveDuringEdit_Set1; + + /// + /// Returns if this object was placed in the scene during edit-time. + /// + /// + public bool IsSceneObject => (SceneId != NetworkObject.UNSET_SCENEID_VALUE); + + /// + /// 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; } = NetworkObject.UNSET_OBJECTID_VALUE; + + /// + /// 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; } + + /// + /// PredictedOwner component on this object. Will be null if not added manually. + /// + [field: SerializeField, HideInInspector] + public PredictedOwner PredictedOwner { get; private set; } + + /// + /// Current Networkbehaviours. + /// + [HideInInspector] + public List NetworkBehaviours; + /// + /// NetworkBehaviour on the root of a NetworkObject parenting this instance. Value will be null if there was no parent during serialization. + /// + /// This API is for internal use and may change at any time. + [HideInInspector] + public NetworkBehaviour InitializedParentNetworkBehaviour; + + /// + /// Nested NetworkObjects that existed during initialization. + /// + /// This API is for internal use and may change at any time. + [HideInInspector] + public List InitializedNestedNetworkObjects = new(); + /// + /// NetworkBehaviour parenting this object when set at runtime using NetworkObject/NetworkBehaviour.SetParent. + /// This is exposed only for low-level use and may change without notice. + /// + [HideInInspector] + public NetworkBehaviour RuntimeParentNetworkBehaviour; + /// + /// NetworkObjects which are made child at runtime using NetworkObject.SetParent. + /// This is exposed only for low-level use and may change without notice. + /// + [HideInInspector] + public List RuntimeChildNetworkBehaviours; + /// + /// NetworkBehaviour parenting this instance. This value prioritizes the runtime value, then initialized value. + /// This is exposed only for low-level use and may change without notice. + /// + internal NetworkBehaviour CurrentParentNetworkBehaviour + { + get + { + if (RuntimeParentNetworkBehaviour != null) + return RuntimeParentNetworkBehaviour; + if (InitializedParentNetworkBehaviour != null) + return InitializedParentNetworkBehaviour; + + return null; + } + } + + /// + /// 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. + /// To check if server or client has been initialized on this object use IsXYZInitialized. + /// + [Obsolete("Use Get/SetIsNetworked.")] + public bool IsNetworked + { + get => GetIsNetworked(); + private set => SetIsNetworked(value); + } + + /// + /// Returns IsNetworked value. + /// + /// + public bool GetIsNetworked() => _isNetworked; + + /// + /// 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 if the object can be spawned at runtime; this is generally false for scene prefabs you do not spawn. + /// + [Obsolete("Use GetIsSpawnable.")] //Remove on V5. + public bool IsSpawnable => _isSpawnable; + + /// + /// Gets the current IsSpawnable value. + /// + /// + public bool GetIsSpawnable() => _isSpawnable; + + /// + /// Sets IsSpawnable value. + /// + /// Next value. + public void SetIsSpawnable(bool value) => _isSpawnable = value; + + [Tooltip("True if the object can be spawned at runtime; this is generally false for scene prefabs you do not spawn.")] + [SerializeField] + private bool _isSpawnable = 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 && !CurrentParentNetworkBehaviour.NetworkObject.IsGlobal) + { + NetworkManager.LogWarning($"Object {gameObject.name} cannot change IsGlobal because it is nested and the parent NetorkObject is not global."); + return; + } + + if (!IsDeinitializing) + { + NetworkManager.LogWarning($"Object {gameObject.name} cannot change IsGlobal as it's already initialized. IsGlobal may only be changed immediately after instantiating."); + return; + } + + if (IsSceneObject) + { + NetworkManager.LogWarning($"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 int GetInitializeOrder() => Order; + + /// + /// This is for internal use. Returns the order to initialize the object. + /// + public int Order + { + get + { + /* Returns a value to add onto initialization order + * based on how nested the object is. This is a fairly + * cheap and quick way to ensure nested objects + * always initialize after ones in the hierarchy. */ + /* Assuming a modifier base of 100. + * A 0 + * B 100 + * C 200 + * D 300 + * E 100 + * F 200 + */ + int GetOrderModifier() + { + //Ensure at least 1 multiplier so initialization order isnt multiplied by 0. + int multiplier = 1; + + //Add one multiplier per every nested. + NetworkBehaviour parentNb = CurrentParentNetworkBehaviour; + while (parentNb != null) + { + multiplier += 1; + parentNb = parentNb.NetworkObject.CurrentParentNetworkBehaviour; + } + + //InitializeOrder max value + 1. + const int modifierBase = (sbyte.MaxValue + 1); + + return (multiplier * modifierBase); + } + + return (_initializeOrder + GetOrderModifier()); + } + } + + [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.")] + [Range(sbyte.MinValue, sbyte.MaxValue)] + [SerializeField] + private sbyte _initializeOrder = 0; + /// + /// True to keep this object spawned when the owner disconnects. + /// + internal bool PreventDespawnOnDisconnect => _preventDespawnOnDisconnect; + [Tooltip("True to keep this object spawned when the owner disconnects.")] + [SerializeField] + private bool _preventDespawnOnDisconnect; + /// + /// 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; + /// + /// Becomes true once initialized values are set. + /// + private bool _initializedValusSet; + /// + /// Sets that InitializedValues have not yet been set. This can be used to force objects to reinitialize which may have changed since the prefab was initialized, such as placed scene objects. + /// + internal void UnsetInitializedValuesSet() => _initializedValusSet = false; + #endregion + + #region Const. + /// + /// Value used when the ObjectId has not been set. + /// + public const int UNSET_SCENEID_VALUE = 0; + /// + /// 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 + + /// + /// Outputs data about this NetworkObject to string. + /// + /// + public override string ToString() + { + string hashCode = (gameObject == null) ? $"NetworkObject HashCode [{GetHashCode()}]" : $"GameObject HashCode [{gameObject.GetHashCode()}]"; + return $"Name [{gameObject.name}] ObjectId [{ObjectId}] OwnerId [{OwnerId}] {hashCode}"; + } + + protected virtual void Awake() + { + _isStatic = gameObject.isStatic; + + /* If networkBehaviours are not yet initialized then do so now. + * After initializing at least 1 networkBehaviour will always exist + * as emptyNetworkBehaviour is added automatically when none are present. */ + if (!_initializedValusSet) + { + bool isNested = false; + //Make sure there are no networkObjects above this since initializing will trickle down. + Transform parent = transform.parent; + while (parent != null) + { + if (parent.TryGetComponent(out _)) + { + isNested = true; + break; + } + + parent = parent.parent; + } + + //If not nested then init + if (!isNested) + SetInitializedValues(parentNob: null, force: false); + } + + SetChildDespawnedState(); + } + + protected virtual void Start() + { + TryStartDeactivation(); + } + + private void OnDisable() + { + /* If deinitializing and an owner exist + * then remove object from owner. */ + if (IsDeinitializing && Owner.IsValid) + { + Owner.RemoveObject(this); + } + //Nothing is started and is a sceneObject. + else if (!IsServerStarted && !IsClientStarted && IsSceneObject) + { + ResetState(asServer: true); + ResetState(asServer: false); + } + } + + private void OnDestroy() + { + SetIsDestroying(DespawnType.Destroy); + + //The object never initialized for use. + if (!_initializedValusSet) + return; + + if (NetworkObserver != null) + NetworkObserver.Deinitialize(destroyed: true); + + if (NetworkManager != null) + { + //Server. + Deinitialize_Prediction(asServer: true); + NetworkManager.ServerManager.Objects.NetworkObjectDestroyed(this, asServer: true); + InvokeStopCallbacks(asServer: true, invokeSyncTypeCallbacks: true); + + //Client. + Deinitialize_Prediction(asServer: false); + NetworkManager.ClientManager.Objects.NetworkObjectDestroyed(this, asServer: false); + InvokeStopCallbacks(asServer: false, invokeSyncTypeCallbacks: true); + } + + /* 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(); + if (NetworkBehaviours.Count > 0) + { + NetworkBehaviour thisNb = NetworkBehaviours[0]; + /* A null check must also be run on the RuntimeChildNbs because the collection is stored + * when an object is destroyed, and if the other object OnDestroy runs before this one deinitializes/destroys + * then the collection will be null. */ + if (RuntimeParentNetworkBehaviour != null && RuntimeParentNetworkBehaviour.NetworkObject.RuntimeChildNetworkBehaviours != null) + RuntimeParentNetworkBehaviour.NetworkObject.RuntimeChildNetworkBehaviours.Remove(thisNb); + } + + IsDeinitializing = true; + + SetDeinitializedStatus(); + + NetworkBehaviour_OnDestroy(); + + ResetState(asServer: true); + ResetState(asServer: false); + + StoreCollections(); + + void NetworkBehaviour_OnDestroy() + { + foreach (NetworkBehaviour nb in NetworkBehaviours) + nb.NetworkBehaviour_OnDestroy(); + } + } + + /// + /// Initializes NetworkBehaviours if they are disabled. + /// + private void InitializeNetworkBehavioursIfDisabled() + { + if (_disabledNetworkBehavioursInitialized) + return; + _disabledNetworkBehavioursInitialized = true; + + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].InitializeIfDisabled(); + } + + /// + /// Returns a cached collection containing NetworkObjects belonging to this object. + /// + internal List GetNetworkObjects(GetNetworkObjectOption option) + { + List cache = CollectionCaches.RetrieveList(); + + if (option.FastContains(GetNetworkObjectOption.Self)) + cache.Add(this); + + //Becomes true if to include any nested. + bool includesNested = false; + + if (option.FastContains(GetNetworkObjectOption.InitializedNested)) + { + cache.AddRange(InitializedNestedNetworkObjects); + includesNested = true; + } + + if (option.FastContains(GetNetworkObjectOption.RuntimeNested)) + { + foreach (NetworkBehaviour nb in RuntimeChildNetworkBehaviours) + cache.Add(nb.NetworkObject); + + includesNested = true; + } + + if (includesNested && option.FastContains(GetNetworkObjectOption.Recursive)) + { + /* Remove include self from options otherwise + * each nested entry would get added twice. */ + option &= ~GetNetworkObjectOption.Self; + + int count = cache.Count; + for (int i = 0; i < count; i++) + { + List recursiveCache = cache[i].GetNetworkObjects(option); + cache.AddRange(recursiveCache); + CollectionCaches.Store(recursiveCache); + } + } + + return cache; + } + + /// + /// Makes children of this NetworkObject global if this object is global. + /// + private void SetChildGlobalState() + { + if (!IsGlobal) + return; + + for (int i = 0; i < InitializedNestedNetworkObjects.Count; i++) + InitializedNestedNetworkObjects[i].SetIsGlobal(true); + } + + /// + /// Sets Despawned on child NetworkObjects if they are not enabled. + /// + private void SetChildDespawnedState() + { + NetworkObject nob; + for (int i = 0; i < InitializedNestedNetworkObjects.Count; i++) + { + nob = InitializedNestedNetworkObjects[i]; + if (!nob.gameObject.activeSelf) + nob.State = NetworkObjectState.Despawned; + } + } + + internal void TryStartDeactivation() + { + if (!GetIsNetworked()) + return; + + //Global. + if (IsGlobal && !IsSceneObject && !IsNested) + DontDestroyOnLoad(gameObject); + + if (NetworkManager == null || (!NetworkManager.IsClientStarted && !NetworkManager.IsServerStarted)) + { + //ActiveDuringEdit is only used for scene objects. + if (IsSceneObject) + WasActiveDuringEdit = true; + gameObject.SetActive(false); + } + } + + /// + /// Sets IsClient or IsServer to isActive. + /// + internal void SetInitializedStatus(bool isInitialized, bool asServer) + { + if (asServer) + IsServerInitialized = isInitialized; + else + IsClientInitialized = isInitialized; + } + + /// + /// Sets IsServerInitialized and IsClientInitialized as false; + /// + private void SetDeinitializedStatus() + { + IsServerInitialized = false; + IsClientInitialized = false; + } + + /// + /// Preinitializes this object for the network. + /// + /// + //public static event Action DebugOnInitialize; //QUICK-TEST Uncomment this + + internal void InitializeEarly(NetworkManager networkManager, int objectId, NetworkConnection owner, bool asServer) + { + //Only initialize this bit once even if clientHost. + if (!networkManager.DoubleLogic(asServer)) + { + //DebugOnInitialize?.Invoke(); //QUICK-TEST Uncomment this + + 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); + + if (ObjectId != NetworkObject.UNSET_OBJECTID_VALUE) + NetworkManager.LogError($"Object was initialized twice without being reset. Object {this.ToString()}"); + + 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.Count; i++) + NetworkBehaviours[i].InitializeEarly(this, asServer); + + /* NetworkObserver uses some information from + * NetworkBehaviour so it must be preinitialized + * after NetworkBehaviours are. */ + if (asServer) + { + if (networkManager.TryGetInstance(out _hashGrid)) + { + _hashGridPosition = _hashGrid.GetHashGridPosition(this); + HashGridEntry = _hashGrid.GetGridEntry(this); + } + + NetworkObserver.Initialize(this); + } + + _networkObserverInitiliazed = true; + + InitializePredictionEarly(networkManager, asServer); + //Add to connections objects. Collection is a hashset so this can be called twice for clientHost. + if (owner != null) + owner.AddObject(this); + } + + private void TimeManager_Update() + { + TimeManager_OnUpdate_Prediction(); + } + + /// + /// Sets this NetworkObject as a child of another at runtime. + /// + /// NetworkBehaviour to use as root. Use null to remove parenting. + public void SetParent(NetworkBehaviour nb) + { + if (!CanChangeParent(true)) + return; + if (IsInvalidParent(nb)) + return; + + UpdateParent(nb); + } + + /// + /// Sets this NetworkObject as a child of another at runtime. + /// + /// NetworkObject to use as root. Use null to remove parenting. + public void SetParent(NetworkObject nob) + { + if (!CanChangeParent(true)) + return; + if (nob == null) + { + UnsetParent(); + return; + } + + //No networkbehaviour. + if (nob.NetworkBehaviours.Count == 0) + { + NetworkManager.LogWarning($"{nob.name} is not a valid parent because it does not have any NetworkBehaviours. Consider adding {nameof(EmptyNetworkBehaviour)} to {nob.name} to resolve this problem."); + return; + } + + NetworkBehaviour newParent = nob.NetworkBehaviours[0]; + UpdateParent(newParent); + } + + /// + /// Unsets this NetworkObject's parent at runtime. + /// + public void UnsetParent() + { + UpdateParent(newParent: null); + } + + /// + /// Updates which NetworkBehaviour to use as a parent. + /// + /// + private void UpdateParent(NetworkBehaviour newParent) + { + NetworkBehaviour thisNb; + + if (NetworkBehaviours.Count == 0) + { + NetworkManager.LogWarning($"{gameObject.name} cannot have it's parent updated because it does not have any NetworkBehaviours. Consider adding {nameof(EmptyNetworkBehaviour)} to {gameObject.name} to resolve this problem."); + return; + } + else + { + //Always use the first to make life easier on everyone. + thisNb = NetworkBehaviours[0]; + } + + //If current is set then remove from as child. + if (RuntimeParentNetworkBehaviour != null) + RuntimeParentNetworkBehaviour.NetworkObject.RuntimeChildNetworkBehaviours.Remove(thisNb); + + //If no new parent, then parent is being removed. + if (newParent == null) + { + RuntimeParentNetworkBehaviour = null; + transform.SetParent(null); + } + //Being set to something. + else + { + RuntimeParentNetworkBehaviour = newParent; + newParent.NetworkObject.RuntimeChildNetworkBehaviours.Add(thisNb); + transform.SetParent(newParent.transform); + } + + /* Rebuild observers since root changed. + * + * This only occurs if this nob is network spawned. + * If not spawned the rebuild will occur after the + * user calls Spawn on the nob/object. */ + if (NetworkManager != null) + NetworkManager.ServerManager.Objects.RebuildObservers(this); + } + + /// + /// Returns if this NetworkObject can change it's parent. + /// + private bool CanChangeParent(bool logFailure) + { + if (IsSceneObject) + return true; + //No limitations on nobs without initialized parents. + if (InitializedParentNetworkBehaviour == null) + return true; + + if (logFailure) + NetworkManager.LogWarning($"{this.ToString()} cannot have it's parent changed because it's nested. Only nested scene objects may change their parent runtime."); + + return false; + } + + /// + /// True if the NetworkObject specified cannot be used as a parent. + /// + /// + /// + private bool IsInvalidParent(NetworkBehaviour nb) + { + /* Scene objects could face destruction if the user + * childs them to an instantiated object that gets despawned. + * If that occurs, the user is at fault. However a destroyed + * scene object should be fine, it just won't spawn later given + * it's been destroyed. Allow scene objects to change parents freely. */ + if (IsSceneObject) + return false; + + //Setting to already current runtime parent. No need to make a change. + if (nb == RuntimeParentNetworkBehaviour) + return true; + //Trying to parent a non-global to a global. + if (nb.NetworkObject.IsGlobal && !IsGlobal) + { + NetworkManager.LogWarning($"{nb.NetworkObject.name} is a global NetworkObject but {gameObject.name} is not. Only global NetworkObjects can be set as a child of another global NetworkObject."); + return true; + } + + //Setting to self. + if (nb.NetworkObject == this) + { + NetworkManager.LogWarning($"{gameObject.name} cannot be set as a child of itself."); + return true; + } + + return false; + } + + /// + /// Adds a NetworkBehaviour and serializes it's components. + /// + internal T AddAndSerialize() where T : NetworkBehaviour //runtimeNB, might need to be public for users. + { + int startingLength = NetworkBehaviours.Count; + T result = gameObject.AddComponent(); + + //Add to network behaviours. + NetworkBehaviours.Add(result); + //Serialize values and return. + result.SerializeComponents(this, (byte)startingLength); + return result; + } + + /// + /// Sets values as they are during initialization, such as componentId, NetworkBehaviour Ids, and more. + /// Starts with a 0 componentId. + /// + internal void SetInitializedValues(NetworkObject parentNob, bool force = false) + { + byte componentId = 0; + SetInitializedValues(parentNob, ref componentId, force); + } + + /// + /// Sets values as they are during initialization, such as componentId, NetworkBehaviour Ids, and more. + /// + /// ComponentId to start from for the NetworkObject. + internal void SetInitializedValues(NetworkObject parentNob, ref byte componentId, bool force = false) + { + if (!ApplicationState.IsPlaying()) + { + NetworkManager.LogError($"Method {nameof(SetInitializedValues)} should only be called at runtime."); + return; + } + + /* If NetworkBehaviours is null then all collections are. + * Set values for each collection. */ + if (force || !_initializedValusSet) + { + /* This only runs when playing, so it's safe to return existing to the pool. */ + StoreCollections(); + + RetrieveCollections(); + + _initializedValusSet = true; + } + + SerializeTransformProperties(); + SetIsNestedThroughTraversal(); + /* 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 (componentId == 0) + { + /* It's not possible to be nested while also having a componentIndex of 0. + * This would mean that the networkObject is being initialized outside of a + * recursive check. We only want to initialize recursively, or when not nested. */ + if (IsNested) + return; + + if (GetComponentsInChildren(true).Length > NetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURS) + { + NetworkManagerExtensions.LogError($"The number of child NetworkObjects on {gameObject.name} exceeds the maximum of {NetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURS}."); + return; + } + } + + NetworkBehaviours.Clear(); + + if (TryGetComponent(out PredictedSpawn ps)) + PredictedSpawn = ps; + if (TryGetComponent(out PredictedOwner po)) + PredictedOwner = po; + + ComponentIndex = componentId; + + /* Since the parent being passed in should have already + * added an empty nb if one didn't exist it's safe to + * pull the first nb. If this value is null then something went + * wrong. */ + if (parentNob != null) + { + /* Try to add an emptyNetworkBehaviour to this objects parent + * if one does not already exist. This is so this networkObject can + * identify it's parent properly. */ + AddEmptyNetworkBehaviour(parentNob, transform.parent, true); + + if (!transform.parent.TryGetComponent(out NetworkBehaviour parentNb)) + NetworkManagerExtensions.LogError($"A NetworkBehaviour is expected to exist on {parentNob.name} but does not."); + else + InitializedParentNetworkBehaviour = parentNb; + } + + //Transforms which can be searched for networkbehaviours. + List transformCache = CollectionCaches.RetrieveList(); + InitializedNestedNetworkObjects.Clear(); + + transformCache.Add(transform); + for (int z = 0; z < transformCache.Count; z++) + { + Transform currentT = transformCache[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) + InitializedNestedNetworkObjects.Add(childNob); + } + else + { + transformCache.Add(t); + } + } + } + + //Iterate all cached transforms and get networkbehaviours. + List nbCache = CollectionCaches.RetrieveList(); + // + List nbCache2 = CollectionCaches.RetrieveList(); + for (int i = 0; i < transformCache.Count; i++) + { + nbCache2.Clear(); + transformCache[i].GetNetworkBehavioursNonAlloc(ref nbCache2); + nbCache.AddRange(nbCache2); + } + + /* If there's no NBs then add an empty one. + * All NetworkObjects must have at least 1 NetworkBehaviour + * to allow nesting. */ + if (nbCache.Count == 0) + { + NetworkBehaviour addedNb = AddEmptyNetworkBehaviour(this, transform, false); + if (addedNb != null) + nbCache.Add(addedNb); + } + + //Copy to array. + int nbCount = nbCache.Count; + // + for (int i = 0; i < nbCount; i++) + { + NetworkBehaviour nb = nbCache[i]; + NetworkBehaviours.Add(nb); + nb.SerializeComponents(this, (byte)i); + } + + CollectionCaches.Store(transformCache); + CollectionCaches.Store(nbCache); + CollectionCaches.Store(nbCache2); + + //Tell children nobs to update their NetworkBehaviours. + foreach (NetworkObject item in InitializedNestedNetworkObjects) + { + componentId++; + item.SetInitializedValues(this, ref componentId, force); + } + + //Update global states to that of this one. + SetChildGlobalState(); + } + + /// + /// Adds EmptyNetworkBehaviour a target if it has no NetworkBehaviours. Updates a NetworkObject to contain the added behaviour. + /// + /// If true an added NetworkBehaviour will be adeded to NetworkBehaviours, and initialized. + /// Added NetworkBehaviour, or first NetworkBehaviour on the target if adding was not required. + private NetworkBehaviour AddEmptyNetworkBehaviour(NetworkObject nob, Transform target, bool addToNetworkBehaviours) + { + NetworkBehaviour result; + //Add to target if it does not have a NB yet. + if (!target.TryGetComponent(out result)) + { + //Already at maximum. + if (nob.NetworkBehaviours.Count == NetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURS) + { + NetworkManager.LogError($"NetworkObject {this.ToString()} already has a maximum of {NetworkBehaviour.MAXIMUM_NETWORKBEHAVIOURS}. {nameof(EmptyNetworkBehaviour)} cannot be added. Nested spawning will likely fail for this object."); + return null; + } + + result = target.gameObject.AddComponent(); + if (addToNetworkBehaviours) + { + nob.NetworkBehaviours.Add(result); + result.SerializeComponents(nob, (byte)(nob.NetworkBehaviours.Count - 1)); + } + } + + return result; + } + + /// + /// Called after all data is synchronized with this NetworkObject. + /// + internal void Initialize(bool asServer, bool invokeSyncTypeCallbacks) + { + SetInitializedStatus(isInitialized: true, asServer); + InvokeStartCallbacks(asServer, invokeSyncTypeCallbacks); + } + + /// + /// Returns if a deinitialize call can process. + /// + internal bool CanDeinitialize(bool asServer) + { + if (NetworkManager == null) + return false; + else if (asServer && !IsServerInitialized) + return false; + else if (!asServer && !IsClientInitialized) + return false; + + return true; + } + + /// + /// Called to prepare this object to be destroyed or disabled. + /// + internal void Deinitialize(bool asServer) + { + if (!CanDeinitialize(asServer)) + return; + + Deinitialize_Prediction(asServer); + + InvokeStopCallbacks(asServer, invokeSyncTypeCallbacks: true); + for (int i = 0; i < NetworkBehaviours.Count; i++) + NetworkBehaviours[i].Deinitialize(asServer); + + bool asServerOnly = (asServer && !IsClientInitialized); + + if (asServer) + { + if (NetworkObserver != null) + NetworkObserver.Deinitialize(destroyed: false); + IsDeinitializing = true; + } + else + { + //Client only. + bool asClientOnly = !NetworkManager.IsServerStarted; + if (asClientOnly) + IsDeinitializing = true; + + RemoveClientRpcLinkIndexes(); + } + + if (!asServer || asServerOnly) + PredictedSpawner = NetworkManager.EmptyConnection; + + SetInitializedStatus(false, asServer); + + if (asServer) + Observers.Clear(); + } + + /// + /// Resets the state of this NetworkObject. + /// This is used internally and typically with custom object pooling. + /// + public void ResetState(bool asServer) + { + int count = NetworkBehaviours.Count; + for (int i = 0; i < count; i++) + NetworkBehaviours[i].ResetState(asServer); + + ResetState_Prediction(asServer); + ResetState_Observers(asServer); + + /* If nested only unset state if despawned. + * This is to prevent nested NetworkObjects from + * being unset as Spawned when only the root was despawned. */ + if (!IsNested || State == NetworkObjectState.Despawned) + State = NetworkObjectState.Unset; + + // //If nested then set active state to serialized value. + // if (IsNested) + // gameObject.SetActive(WasActiveDuringEdit); + // + SetOwner(NetworkManager.EmptyConnection); + if (NetworkObserver != null) + NetworkObserver.Deinitialize(false); + + //Never clear references -- these are needed for cleanup in unexpected destroys. + // NetworkManager = null; + // ServerManager = null; + // ClientManager = null; + // ObserverManager = null; + // TransportManager = null; + // TimeManager = null; + // SceneManager = null; + // RollbackManager = null; + + //Misc sets. + ObjectId = NetworkObject.UNSET_OBJECTID_VALUE; + } + + /// + /// Removes ownership from all clients. + /// + public void RemoveOwnership(bool includeNested = false) + { + GiveOwnership(null, asServer: true, includeNested); + } + + /// + /// Gives ownership to newOwner. + /// + /// + public void GiveOwnership(NetworkConnection newOwner) => GiveOwnership(newOwner, asServer: true, recursive: false); + + /// + /// Gives ownership to newOwner. + /// + /// + public void GiveOwnership(NetworkConnection newOwner, bool asServer) => GiveOwnership(newOwner, asServer, recursive: false); + + /// + /// Gives ownership to newOwner. + /// + /// + //Remove at --- In V5 make IncludeNested required. + internal void GiveOwnership(NetworkConnection newOwner, bool asServer, bool recursive = false) + { + /* Additional asServer checks. */ + if (asServer) + { + if (!NetworkManager.IsServerStarted) + { + 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) + 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.IsHostStarted) + { + if (activeNewOwner) + newOwner.AddObject(this); + if (prevOwner != newOwner) + prevOwner.RemoveObject(this); + } + + //After changing owners invoke callbacks. + InvokeManualOwnershipChange(prevOwner, asServer); + + //If asServer send updates to clients as needed. + if (asServer) + { + if (activeNewOwner) + ServerManager.Objects.RebuildObservers(this, newOwner, false); + + PooledWriter writer = WriterPool.Retrieve(); + writer.WritePacketIdUnpacked(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); + } + + writer.Store(); + + if (prevOwner.IsActive) + ServerManager.Objects.RebuildObservers(prevOwner); + } + + if (recursive) + { + List allNested = GetNetworkObjects(GetNetworkObjectOption.AllNestedRecursive); + + foreach (NetworkObject nob in allNested) + nob.GiveOwnership(newOwner, asServer, recursive: true); + + CollectionCaches.Store(allNested); + } + } + + /// + /// Initializes a predicted object for client. + /// + internal void InitializePredictedObject_Server(NetworkConnection predictedSpawner) + { + PredictedSpawner = predictedSpawner; + } + + /// + /// Initializes a predicted object for client. + /// + internal void InitializePredictedObject_Client(NetworkManager manager, int objectId, NetworkConnection owner, NetworkConnection predictedSpawner) + { + PredictedSpawner = predictedSpawner; + InitializeEarly(manager, objectId, owner, false); + } + + /// + /// Sets the owner of this object. + /// + /// + /// + private void SetOwner(NetworkConnection owner) + { + Owner = owner; + } + + /// + /// Returns changed properties between this transform and values. + /// + internal TransformPropertiesFlag GetTransformChanges(TransformProperties stp) + { + Transform t = transform; + return GetTransformChanges(t, stp.Position, stp.Rotation, stp.Scale); + } + + /// + /// Returns changed properties between this transform and a prefab. + /// + internal TransformPropertiesFlag GetTransformChanges(GameObject prefab) + { + Transform prefabT = prefab.transform; + return GetTransformChanges(transform, prefabT.localPosition, prefabT.localRotation, prefabT.localScale); + } + + /// + /// Returns changed properties between a transform and values. + /// + private TransformPropertiesFlag GetTransformChanges(Transform t, Vector3 localPosition, Quaternion localRotation, Vector3 localScale) + { + TransformPropertiesFlag tpf = TransformPropertiesFlag.Unset; + if (t.localPosition != localPosition) + tpf |= TransformPropertiesFlag.Position; + if (t.localRotation != localRotation) + tpf |= TransformPropertiesFlag.Rotation; + if (t.localScale != localScale) + tpf |= TransformPropertiesFlag.Scale; + + return tpf; + } + + /// + /// Sets IsNested and returns the result. + /// + /// + internal 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; + } + + /// + /// Serializes TransformProperties to current transform properties. + /// Returns if serialized value changed. + /// + internal void SerializeTransformProperties() + { + SerializedTransformProperties = new(transform.localPosition, transform.localRotation, transform.localScale); + } + + /// + /// Stores collections to caches. + /// + private void StoreCollections() + { + CollectionCaches.StoreAndDefault(ref NetworkBehaviours); + CollectionCaches.StoreAndDefault(ref InitializedNestedNetworkObjects); + CollectionCaches.StoreAndDefault(ref RuntimeChildNetworkBehaviours); + } + + private void RetrieveCollections() + { + NetworkBehaviours = CollectionCaches.RetrieveList(); + InitializedNestedNetworkObjects = CollectionCaches.RetrieveList(); + RuntimeChildNetworkBehaviours = CollectionCaches.RetrieveList(); + } + + #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); + } + + internal void ReserializeEditorSetValues(bool setWasActiveDuringEdit, bool setSceneId) + { + if (ApplicationState.IsPlaying()) + return; + +#if UNITY_EDITOR + if (setWasActiveDuringEdit) + { + bool hasNetworkObjectParent = false; + Transform parent = transform.parent; + while (parent != null) + { + if (parent.TryGetComponent(out _)) + { + hasNetworkObjectParent = true; + break; + } + + parent = parent.parent; + } + + WasActiveDuringEdit = (hasNetworkObjectParent && gameObject.activeSelf) || (!hasNetworkObjectParent && gameObject.activeInHierarchy); + WasActiveDuringEdit_Set1 = true; + } + + if (setSceneId) + CreateSceneId(force: false); +#endif + } + + private void OnValidate() + { + ReserializeEditorSetValues(setWasActiveDuringEdit: true, setSceneId: true); + + if (IsGlobal && IsSceneObject) + NetworkManagerExtensions.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() + { + ReferenceIds_Reset(); + } +#endif + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs.meta new file mode 100644 index 0000000..d522070 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 26b716c41e9b56b4baafaf13a523ba2e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObject.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs new file mode 100644 index 0000000..45d3e9a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs @@ -0,0 +1,80 @@ + +namespace FishNet.Object +{ + /// + /// Action to take when despawning a NetworkObject. + /// + public enum DespawnType : byte + { + Destroy = 0, + Pool = 1, + } + + + /// + /// 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, + } + + /// + /// Options on retrieving nested NetworkObjects. + /// + [System.Flags] + internal enum GetNetworkObjectOption : int + { + /// + /// Include NetworkObject which nested are being returned for. + /// + Self = (1 << 0), + /// + /// Include initialize nested. + /// + InitializedNested = (1 << 1), + /// + /// Include runtime nested. + /// + RuntimeNested = (1 << 2), + /// + /// Recursively iterate nested includes. + /// + /// This only functions if Initialized or Runtime is flagged. + Recursive = (1 << 3), + + /// + /// Uses InitializedNested and RuntimeNested flags. + /// + AllNested = (InitializedNested | RuntimeNested), + /// + /// Uses InitializedNested, RuntimeNested, and Recursive flags. + /// + AllNestedRecursive = (InitializedNested | RuntimeNested | Recursive), + /// + /// Sets all flags. + /// + All = ~0, + } + + + internal static class GetNetworkObjectOptionExtensions + { + /// + /// True if whole contains part. + /// + public static bool FastContains(this GetNetworkObjectOption whole, GetNetworkObjectOption part) => (whole & part) == part; + } +} + diff --git a/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs.meta b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs.meta new file mode 100644 index 0000000..c52321d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 536f137f11dc6654eab9fbe94ca14cd8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/NetworkObject/NetworkObjectData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction.meta b/Assets/FishNet/Runtime/Object/Prediction.meta new file mode 100644 index 0000000..14d8d58 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 46773e0f27a85d643867f04d902fa007 +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..ed6b55c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs @@ -0,0 +1,16 @@ +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 { } + /// + /// 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 { } +} \ 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..174d60f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Attributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b082d36535ce0404d8438bc1b0499e53 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/Attributes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs b/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs new file mode 100644 index 0000000..e0a36f5 --- /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; +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, ReplicateState state, Channel channel); + [APIExclude] + public delegate void ReconcileUserLogicDelegate(T data, 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..3b7fefd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Delegates.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c9904192dacd41a4ba7b29bc3199ec3a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/Delegates.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs new file mode 100644 index 0000000..daaf9a5 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs @@ -0,0 +1,40 @@ + +namespace FishNet.Object.Prediction +{ + public interface IReplicateData + { + /// + /// Local tick when the data was created. + /// + /// + uint GetTick(); + /// + /// Sets the local tick when data was created. + /// + /// + void SetTick(uint value); + /// + /// Allows for any cleanup when the data is being discarded. + /// + void Dispose(); + } + + public interface IReconcileData + { + /// + /// Local tick when the data was created. + /// + /// + uint GetTick(); + /// + /// Sets the local tick when data was created. + /// + /// + void SetTick(uint value); + /// + /// Allows for any cleanup when the data is being discarded. + /// + 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..03da004 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 515754257f85574438408c7f5b268590 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/Interfaces.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs new file mode 100644 index 0000000..e04cfff --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs @@ -0,0 +1,47 @@ +using FishNet.Documenting; +using FishNet.Serializing; +using UnityEngine; + +namespace FishNet.Object.Prediction +{ + /// + /// Used to store reconciles locally. + /// + /// This is for internal use only. + [APIExclude] + public struct LocalReconcile where T : IReconcileData + { + /// + /// Tick for reconcile. + /// + public uint Tick; + /// + /// Writer reconcile was written to. + /// + public PooledWriter Writer; + /// + /// Data inside writer. + /// + public T Data; + + public void Initialize(uint tick, T data) + { + Tick = tick; + Data = data; + Writer = WriterPool.Retrieve(); + Writer.Write(data); + } + + /// + /// Disposes of used data. + /// + public void Dispose() + { + Data.Dispose(); + if (Writer != null) + WriterPool.Store(Writer); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta new file mode 100644 index 0000000..3e8a313 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eb90cfdc07524be40a9d4d11ae7c35e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/LocalReconcile.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs b/Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs new file mode 100644 index 0000000..ed1f0c3 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs @@ -0,0 +1,440 @@ +using GameKit.Dependencies.Utilities; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Object.Prediction +{ + /// + /// Used to make calculations and perform actions in moving transforms over time. + /// + [Preserve] + public struct MoveRates + { + /// + /// Rate at which to move Position. + /// + public float Position; + /// + /// Rate at which to move Rotation. + /// + public float Rotation; + /// + /// Rate at which to move Scale. + /// + public float Scale; + /// + /// Time remaining until the move is complete. + /// + public float TimeRemaining; + + /// + /// Value used when data is not set. + /// + public const float UNSET_VALUE = float.NegativeInfinity; + /// + /// Value used when move rate should be instant. + /// + public const float INSTANT_VALUE = float.PositiveInfinity; + + /// + /// True if any data is set. Once set, this will remain true until ResetState is called. + /// + public bool IsValid { get; private set; } + + public MoveRates(float value) : this() + { + Position = value; + Rotation = value; + Scale = value; + + IsValid = true; + } + + public MoveRates(float position, float rotation) : this() + { + Position = position; + Rotation = rotation; + Scale = INSTANT_VALUE; + + IsValid = true; + } + + public MoveRates(float position, float rotation, float scale) : this() + { + Position = position; + Rotation = rotation; + Scale = scale; + + IsValid = true; + } + + public MoveRates(float position, float rotation, float scale, float timeRemaining) + { + Position = position; + Rotation = rotation; + Scale = scale; + TimeRemaining = timeRemaining; + + IsValid = true; + } + + /// + /// True if a positional move rate is set. + /// + public bool IsPositionSet => (Position != UNSET_VALUE); + /// + /// True if rotation move rate is set. + /// + public bool IsRotationSet => (Rotation != UNSET_VALUE); + /// + /// True if a scale move rate is set. + /// + public bool IsScaleSet => (Scale != UNSET_VALUE); + + /// + /// True if position move rate should be instant. + /// + public bool IsPositionInstantValue => (Position == INSTANT_VALUE); + /// + /// True if rotation move rate should be instant. + /// + public bool IsRotationInstantValue => (Rotation == INSTANT_VALUE); + /// + /// True if scale move rate should be instant. + /// + public bool IsScaleInstantValue => (Scale == INSTANT_VALUE); + + /// + /// Sets all rates to instant. + /// + public void SetInstantRates() => Update(INSTANT_VALUE); + + /// + /// Sets all rates to the same value. + /// + public void Update(float value) => Update(value, value, value); + + /// + /// Sets rates for each property. + /// + public void Update(float position, float rotation, float scale) + { + Position = position; + Rotation = rotation; + Scale = scale; + + IsValid = true; + } + + /// + /// Sets rates for each property. + /// + public void Update(float position, float rotation, float scale, float timeRemaining) + { + Position = position; + Rotation = rotation; + Scale = scale; + TimeRemaining = timeRemaining; + + IsValid = true; + } + + /// + /// Updates to new values. + /// + public void Update(MoveRates moveRates) => Update(moveRates.Position, moveRates.Rotation, moveRates.Scale, moveRates.TimeRemaining); + + /// + /// Updates to new values. + /// + public void Update(MoveRatesCls moveRates) => Update(moveRates.Position, moveRates.Rotation, moveRates.Scale, moveRates.TimeRemaining); + + /// + /// Resets to unset values. + /// + public void ResetState() + { + Update(MoveRates.UNSET_VALUE, MoveRates.UNSET_VALUE, MoveRates.UNSET_VALUE, timeRemaining: 0f); + + IsValid = false; + } + + /// + /// Returns a new MoveRates based on previous values, and a transforms current position. + /// + public static MoveRates GetWorldMoveRates(Transform from, Transform to, float duration, float teleportThreshold) + { + return GetMoveRates(from.position, to.position, from.rotation, to.rotation, from.localScale, to.localScale, duration, teleportThreshold); + } + + /// + /// Returns a new MoveRates based on previous values, and a transforms current position. + /// + public static MoveRates GetLocalMoveRates(Transform from, Transform to, float duration, float teleportThreshold) + { + return GetMoveRates(from.localPosition, to.localPosition, from.localRotation, to.localRotation, from.localScale, to.localScale, duration, teleportThreshold); + } + + /// + /// Returns a new MoveRates based on previous values, and a transforms current position. + /// + public static MoveRates GetWorldMoveRates(TransformProperties prevValues, Transform t, float duration, float teleportThreshold) + { + return GetMoveRates(prevValues.Position, t.position, prevValues.Rotation, t.rotation, prevValues.Scale, t.localScale, duration, teleportThreshold); + } + + /// + /// Returns a new MoveRates based on previous values, and a transforms current position. + /// + public static MoveRates GetLocalMoveRates(TransformProperties prevValues, Transform t, float duration, float teleportThreshold) + { + return GetMoveRates(prevValues.Position, t.localPosition, prevValues.Rotation, t.localRotation, prevValues.Scale, t.localScale, duration, teleportThreshold); + } + + /// + /// Returns a new MoveRates based on previous values, and a transforms current position. + /// + public static MoveRates GetMoveRates(TransformProperties prevValues, TransformProperties nextValues, float duration, float teleportThreshold) + { + return GetMoveRates(prevValues.Position, nextValues.Position, prevValues.Rotation, nextValues.Rotation, prevValues.Scale, nextValues.Scale, duration, teleportThreshold); + } + + /// + /// Returns a new MoveRates based on previous values, and a transforms current position. + /// + public static MoveRates GetMoveRates(Vector3 fromPosition, Vector3 toPosition, Quaternion fromRotation, Quaternion toRotation, Vector3 fromScale, Vector3 toScale, float duration, float teleportThreshold) + { + float rate; + + /* Position. */ + rate = toPosition.GetRate(fromPosition, duration, out float distance); + //Basic teleport check. + if (teleportThreshold != UNSET_VALUE && distance > teleportThreshold) + return new(INSTANT_VALUE, INSTANT_VALUE, INSTANT_VALUE, duration); + + //Smoothing. + float positionRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE); + rate = toRotation.GetRate(fromRotation, duration, out _); + float rotationRate = rate.SetIfUnderTolerance(0.2f, INSTANT_VALUE); + rate = toScale.GetRate(fromScale, duration, out _); + float scaleRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE); + + return new(positionRate, rotationRate, scaleRate, duration); + } + + /// + /// Gets a move rate for two Vector3s. + /// + public static float GetMoveRate(Vector3 fromPosition, Vector3 toPosition, float duration, float teleportThreshold) + { + float rate; + float distance; + + /* Position. */ + rate = toPosition.GetRate(fromPosition, duration, out distance); + //Basic teleport check. + if (teleportThreshold != UNSET_VALUE && distance > teleportThreshold) + { + return INSTANT_VALUE; + } + //Smoothing. + else + { + float positionRate = rate.SetIfUnderTolerance(0.0001f, INSTANT_VALUE); + return positionRate; + } + } + + /// + /// Gets a move rate for two Quaternions. + /// + public static float GetMoveRate(Quaternion fromRotation, Quaternion toRotation, float duration) + { + float rate = toRotation.GetRate(fromRotation, duration, out _); + float rotationRate = rate.SetIfUnderTolerance(0.2f, INSTANT_VALUE); + return rotationRate; + } + + /// + /// Moves transform to target values. + /// + public void Move(Transform movingTransform, TransformProperties goalProperties, float delta, bool useWorldSpace) + { + if (!IsValid) + return; + + Move(movingTransform, TransformPropertiesFlag.Everything, goalProperties.Position, Position, goalProperties.Rotation, Rotation, goalProperties.Scale, Scale, delta, useWorldSpace); + TimeRemaining -= delta; + } + + /// + /// Moves transform to target values. + /// + public void Move(Transform movingTransform, TransformProperties goalProperties, TransformPropertiesFlag movedProperties, float delta, bool useWorldSpace) + { + if (!IsValid) + return; + + Move(movingTransform, movedProperties, goalProperties.Position, Position, goalProperties.Rotation, Rotation, goalProperties.Scale, Scale, delta, useWorldSpace); + TimeRemaining -= delta; + } + + /// + /// Moves transform to target values. + /// + public static void Move(Transform movingTransform, TransformPropertiesFlag movedProperties, Vector3 posGoal, float posRate, Quaternion rotGoal, float rotRate, Vector3 scaleGoal, float scaleRate, float delta, bool useWorldSpace) + { + Transform t = movingTransform; + + bool containsPosition = movedProperties.FastContains(TransformPropertiesFlag.Position); + bool containsRotation = movedProperties.FastContains(TransformPropertiesFlag.Rotation); + bool containsScale = movedProperties.FastContains(TransformPropertiesFlag.Scale); + + //World space. + if (useWorldSpace) + { + if (containsPosition) + { + if (posRate == INSTANT_VALUE) + t.position = posGoal; + else if (posRate == UNSET_VALUE) { } + else + t.position = Vector3.MoveTowards(t.position, posGoal, posRate * delta); + } + + if (containsRotation) + { + if (rotRate == INSTANT_VALUE) + t.rotation = rotGoal; + else if (rotRate == UNSET_VALUE) { } + else + t.rotation = Quaternion.RotateTowards(t.rotation, rotGoal, rotRate * delta); + } + } + //Local space. + else + { + if (containsPosition) + { + if (posRate == INSTANT_VALUE) + t.localPosition = posGoal; + else if (posRate == UNSET_VALUE) { } + else + t.localPosition = Vector3.MoveTowards(t.localPosition, posGoal, posRate * delta); + } + + if (containsRotation) + { + if (rotRate == INSTANT_VALUE) + t.localRotation = rotGoal; + else if (rotRate == UNSET_VALUE) { } + else + t.localRotation = Quaternion.RotateTowards(t.localRotation, rotGoal, rotRate * delta); + } + } + + //Scale always uses local. + if (containsScale) + { + if (scaleRate == INSTANT_VALUE) + t.localScale = scaleGoal; + else if (scaleRate == UNSET_VALUE) { } + else + t.localScale = Vector3.MoveTowards(t.localScale, scaleGoal, scaleRate * delta); + } + } + } + + /// + /// Used to make calculations and perform actions in moving transforms over time. + /// + /// This acts as a wrapper for MoveRates struct. + public class MoveRatesCls : IResettable + { + /// + /// Container of all move rate information. + /// + private MoveRates _moveRates = new(); + + /// + /// Rate at which to move Position. + /// + public float Position => _moveRates.Position; + /// + /// Rate at which to move Rotation. + /// + public float Rotation => _moveRates.Rotation; + /// + /// Rate at which to move Scale. + /// + public float Scale => _moveRates.Scale; + /// + /// Time remaining until the move is complete. + /// + public float TimeRemaining => _moveRates.TimeRemaining; + + /// + /// True if position move rate should be instant. + /// + public bool IsPositionInstantValue => _moveRates.IsPositionInstantValue; + /// + /// True if rotation move rate should be instant. + /// + public bool IsRotationInstantValue => _moveRates.IsRotationInstantValue; + /// + /// True if scale move rate should be instant. + /// + public bool IsScaleInstantValue => _moveRates.IsScaleInstantValue; + + /// + /// True if any data is set. + /// + public bool IsValid => _moveRates.IsValid; + + public MoveRatesCls(float value) => _moveRates = new MoveRates(value); + public MoveRatesCls(float position, float rotation) => _moveRates = new MoveRates(position, rotation); + public MoveRatesCls(float position, float rotation, float scale) => _moveRates = new MoveRates(position, rotation, scale); + public MoveRatesCls(float position, float rotation, float scale, float timeRemaining) => _moveRates = new MoveRates(position, rotation, scale, timeRemaining); + + public MoveRatesCls() => _moveRates.ResetState(); + + /// + /// Sets all rates to instant. + /// + public void SetInstantRates() => _moveRates.SetInstantRates(); + + /// + /// Sets all rates to the same value. + /// + public void Update(float value) => _moveRates.Update(value); + + /// + /// Updates values. + /// + public void Update(float position, float rotation, float scale) => _moveRates.Update(position, rotation, scale); + + /// + /// Updates values. + /// + public void Update(float position, float rotation, float scale, float timeRemaining) => _moveRates.Update(position, rotation, scale, timeRemaining); + + /// + /// Updaes values. + /// + public void Update(MoveRatesCls mr) => _moveRates.Update(mr.Position, mr.Rotation, mr.Scale); + + /// + /// Moves transform to target values. + /// + public void Move(Transform movingTransform, TransformProperties goalProperties, float delta, bool useWorldSpace) => _moveRates.Move(movingTransform, goalProperties, delta, useWorldSpace); + + /// + /// Moves transform to target values. + /// + public void Move(Transform movingTransform, TransformProperties goalProperties, TransformPropertiesFlag movedProperties, float delta, bool useWorldSpace) => _moveRates.Move(movingTransform, goalProperties, movedProperties, delta, useWorldSpace); + + public void ResetState() => _moveRates.ResetState(); + + public void InitializeState() { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs.meta new file mode 100644 index 0000000..da0d1ac --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1bddf57861232884ca21f7e97a6d662d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/MoveRates.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs new file mode 100644 index 0000000..b91794b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs @@ -0,0 +1,441 @@ +using FishNet.CodeGenerating; +using FishNet.Component.Prediction; +using FishNet.Managing; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Object.Prediction +{ + [Preserve] + [DefaultWriter] + public static class PredictionigidbodySerializers + { + [DefaultWriter] + public static void WriteEntryData(this Writer w, PredictionRigidbody.EntryData value) + { + PredictionRigidbody.ForceApplicationType appType = value.Type; + w.WriteUInt8Unpacked((byte)appType); + PredictionRigidbody.AllForceData data = value.Data; + + switch (appType) + { + case PredictionRigidbody.ForceApplicationType.AddTorque: + case PredictionRigidbody.ForceApplicationType.AddForce: + case PredictionRigidbody.ForceApplicationType.AddRelativeTorque: + case PredictionRigidbody.ForceApplicationType.AddRelativeForce: + w.WriteVector3(data.Vector3Force); + w.WriteInt32((byte)data.Mode); + break; + case PredictionRigidbody.ForceApplicationType.AddExplosiveForce: + w.WriteSingle(data.FloatForce); + w.WriteVector3(data.Position); + w.WriteSingle(data.Radius); + w.WriteSingle(data.UpwardsModifier); + w.WriteInt32((byte)data.Mode); + break; + case PredictionRigidbody.ForceApplicationType.AddForceAtPosition: + w.WriteVector3(data.Vector3Force); + w.WriteVector3(data.Position); + w.WriteInt32((byte)data.Mode); + break; + default: + NetworkManagerExtensions.LogError($"ForceApplicationType of {appType} is not supported."); + break; + } + } + + [DefaultReader] + public static PredictionRigidbody.EntryData ReadEntryData(this Reader r) + { + PredictionRigidbody.EntryData fd = new(); + + PredictionRigidbody.ForceApplicationType appType = (PredictionRigidbody.ForceApplicationType)r.ReadUInt8Unpacked(); + fd.Type = appType; + + PredictionRigidbody.AllForceData data = new(); + + switch (appType) + { + case PredictionRigidbody.ForceApplicationType.AddTorque: + case PredictionRigidbody.ForceApplicationType.AddForce: + case PredictionRigidbody.ForceApplicationType.AddRelativeTorque: + case PredictionRigidbody.ForceApplicationType.AddRelativeForce: + data.Vector3Force = r.ReadVector3(); + data.Mode = (ForceMode)r.ReadInt32(); + break; + case PredictionRigidbody.ForceApplicationType.AddExplosiveForce: + data.FloatForce = r.ReadSingle(); + data.Position = r.ReadVector3(); + data.Radius = r.ReadSingle(); + data.UpwardsModifier = r.ReadSingle(); + data.Mode = (ForceMode)r.ReadInt32(); + break; + case PredictionRigidbody.ForceApplicationType.AddForceAtPosition: + data.Vector3Force = r.ReadVector3(); + data.Position = r.ReadVector3(); + data.Mode = (ForceMode)r.ReadInt32(); + break; + default: + NetworkManagerExtensions.LogError($"ForceApplicationType of {appType} is not supported."); + break; + } + + fd.Data = data; + return fd; + } + + [DefaultWriter] + public static void WritePredictionRigidbody(this Writer w, PredictionRigidbody pr) + { + w.Write(pr.Rigidbody.GetState()); + w.WriteList(pr.GetPendingForces()); + } + + [DefaultReader] + public static PredictionRigidbody ReadPredictionRigidbody(this Reader r) + { + List lst = CollectionCaches.RetrieveList(); + + RigidbodyState rs = r.Read(); + r.ReadList(ref lst); + PredictionRigidbody pr = ResettableObjectCaches.Retrieve(); + + pr.SetReconcileData(rs, lst); + return pr; + } + + [DefaultDeltaWriter] + public static bool WriteDeltaEntryData(this Writer w, PredictionRigidbody.EntryData value) + { + w.WriteEntryData(value); + return true; + } + + [DefaultDeltaReader] + public static PredictionRigidbody.EntryData ReadDeltaEntryData(this Reader r) => r.ReadEntryData(); + + [DefaultDeltaWriter] + public static bool WriteDeltaPredictionRigidbody(this Writer w, PredictionRigidbody pr) + { + w.WritePredictionRigidbody(pr); + return true; + } + + [DefaultDeltaReader] + public static PredictionRigidbody ReadDeltaPredictionRigidbody(this Reader r) => r.ReadPredictionRigidbody(); + } + + [UseGlobalCustomSerializer] + [Preserve] + public class PredictionRigidbody : IResettable + { + #region Types. + public struct AllForceData + { + public ForceMode Mode; + public Vector3 Vector3Force; + public Vector3 Position; + public float FloatForce; + public float Radius; + public float UpwardsModifier; + + /// + /// Used for Force and Torque. + /// + public AllForceData(Vector3 force, ForceMode mode) : this() + { + Vector3Force = force; + Mode = mode; + } + + /// + /// Used for Position. + /// + public AllForceData(Vector3 force, Vector3 position, ForceMode mode) : this() + { + Vector3Force = force; + Position = position; + Mode = mode; + } + + /// + /// Used for Explosive. + /// + /// + /// + /// + /// + /// + public AllForceData(float force, Vector3 position, float radius, float upwardsModifier, ForceMode mode) : this() + { + FloatForce = force; + Position = position; + Radius = radius; + UpwardsModifier = upwardsModifier; + Mode = mode; + } + } + + public interface IForceData { } + + //How the force was applied. + [System.Flags] + public enum ForceApplicationType : byte + { + AddForceAtPosition = 1, + AddExplosiveForce = 2, + AddForce = 4, + AddRelativeForce = 8, + AddTorque = 16, + AddRelativeTorque = 32, + } + + [UseGlobalCustomSerializer] + public struct EntryData + { + public ForceApplicationType Type; + public AllForceData Data; + + public EntryData(ForceApplicationType type, AllForceData data) + { + Type = type; + Data = data; + } + + public EntryData(EntryData fd) + { + Type = fd.Type; + Data = fd.Data; + } + } + #endregion + + #region Public. + /// + /// Rigidbody which force is applied. + /// + public Rigidbody Rigidbody { get; private set; } + /// + /// Returns if there are any pending forces. + /// + public bool HasPendingForces => (_pendingForces != null && _pendingForces.Count > 0); + #endregion + + #region Internal. + /// + /// RigidbodyState set only as reconcile data. + /// + [System.NonSerialized] + internal RigidbodyState RigidbodyState; + #endregion + + #region Private + /// + /// Forces waiting to be applied. + /// + [ExcludeSerialization] + private List _pendingForces; + + /// + /// Returns current pending forces. + /// Modifying this collection could cause undesirable results. + /// + public List GetPendingForces() => _pendingForces; + #endregion + + ~PredictionRigidbody() + { + if (_pendingForces != null) + CollectionCaches.StoreAndDefault(ref _pendingForces); + Rigidbody = null; + } + + /// + /// Rigidbody which force is applied. + /// + /// + public void Initialize(Rigidbody rb) + { + Rigidbody = rb; + if (_pendingForces == null) + _pendingForces = CollectionCaches.RetrieveList(); + else + _pendingForces.Clear(); + } + + /// + /// Adds Velocity force to the Rigidbody. + /// + public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force) + { + EntryData fd = new(ForceApplicationType.AddForce, new(force, mode)); + _pendingForces.Add(fd); + } + + public void AddRelativeForce(Vector3 force, ForceMode mode = ForceMode.Force) + { + EntryData fd = new(ForceApplicationType.AddRelativeForce, new(force, mode)); + _pendingForces.Add(fd); + } + + public void AddTorque(Vector3 force, ForceMode mode = ForceMode.Force) + { + EntryData fd = new(ForceApplicationType.AddTorque, new(force, mode)); + _pendingForces.Add(fd); + } + + public void AddRelativeTorque(Vector3 force, ForceMode mode = ForceMode.Force) + { + EntryData fd = new(ForceApplicationType.AddRelativeTorque, new(force, mode)); + _pendingForces.Add(fd); + } + + public void AddExplosiveForce(float force, Vector3 position, float radius, float upwardsModifier = 0f, ForceMode mode = ForceMode.Force) + { + EntryData fd = new(ForceApplicationType.AddExplosiveForce, new(force, position, radius, upwardsModifier, mode)); + _pendingForces.Add(fd); + } + + public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode mode = ForceMode.Force) + { + EntryData fd = new(ForceApplicationType.AddForceAtPosition, new(force, position, mode)); + _pendingForces.Add(fd); + } + + /// + /// Sets velocity while clearing pending forces. + /// Simulate should still be called normally. + /// + public void Velocity(Vector3 force) + { + Rigidbody.linearVelocity = force; + RemoveForces(true); + } + + /// + /// Sets angularVelocity while clearning pending forces. + /// Simulate should still be called normally. + /// + public void AngularVelocity(Vector3 force) + { + Rigidbody.angularVelocity = force; + RemoveForces(false); + } + + /// + /// Applies pending forces to rigidbody in the order they were added. + /// + public void Simulate() + { + foreach (EntryData item in _pendingForces) + { + AllForceData data = item.Data; + switch (item.Type) + { + case ForceApplicationType.AddTorque: + Rigidbody.AddTorque(data.Vector3Force, data.Mode); + break; + case ForceApplicationType.AddForce: + Rigidbody.AddForce(data.Vector3Force, data.Mode); + break; + case ForceApplicationType.AddRelativeTorque: + Rigidbody.AddRelativeTorque(data.Vector3Force, data.Mode); + break; + case ForceApplicationType.AddRelativeForce: + Rigidbody.AddRelativeForce(data.Vector3Force, data.Mode); + break; + case ForceApplicationType.AddExplosiveForce: + Rigidbody.AddExplosionForce(data.FloatForce, data.Position, data.Radius, data.UpwardsModifier, data.Mode); + break; + case ForceApplicationType.AddForceAtPosition: + Rigidbody.AddForceAtPosition(data.Vector3Force, data.Position, data.Mode); + break; + } + } + _pendingForces.Clear(); + } + + /// + /// Manually clears pending forces. + /// + /// True to clear velocities, false to clear angular velocities. + public void ClearPendingForces(bool velocity) + { + RemoveForces(velocity); + } + + /// + /// Clears pending velocity and angular velocity forces. + /// + public void ClearPendingForces() + { + _pendingForces.Clear(); + } + + /// + /// Reconciles to a state. + /// + public void Reconcile(PredictionRigidbody pr) + { + _pendingForces.Clear(); + if (pr._pendingForces != null) + { + foreach (EntryData item in pr._pendingForces) + _pendingForces.Add(new(item)); + } + //Set state. + Rigidbody.SetState(pr.RigidbodyState); + + ResettableObjectCaches.Store(pr); + } + + /// + /// Removes forces from pendingForces. + /// + /// True to remove if velocity, false if to remove angular velocity. + private void RemoveForces(bool velocity) + { + if (_pendingForces.Count > 0) + { + ForceApplicationType velocityApplicationTypes = (ForceApplicationType.AddRelativeForce | ForceApplicationType.AddForce | ForceApplicationType.AddExplosiveForce); + + List newDatas = CollectionCaches.RetrieveList(); + foreach (EntryData item in _pendingForces) + { + if (VelocityApplicationTypesContains(item.Type) == !velocity) + newDatas.Add(item); + } + //Add back to _pendingForces if changed. + if (newDatas.Count != _pendingForces.Count) + { + _pendingForces.Clear(); + foreach (EntryData item in newDatas) + _pendingForces.Add(item); + } + CollectionCaches.Store(newDatas); + + bool VelocityApplicationTypesContains(ForceApplicationType apt) + { + return (velocityApplicationTypes & apt) == apt; + } + } + } + + internal void SetReconcileData(RigidbodyState rs, List lst) + { + RigidbodyState = rs; + _pendingForces = lst; + } + + public void ResetState() + { + CollectionCaches.StoreAndDefault(ref _pendingForces); + Rigidbody = null; + } + + public void InitializeState() { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs.meta new file mode 100644 index 0000000..794ee73 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7c964c0b90f389c4899a8120cc19d8b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs new file mode 100644 index 0000000..99f01a8 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs @@ -0,0 +1,368 @@ +using FishNet.CodeGenerating; +using FishNet.Component.Prediction; +using FishNet.Managing; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Object.Prediction +{ + public static class PredictionRigidbody2DSerializers + { + public static void WriteForceData(this Writer w, PredictionRigidbody2D.EntryData value) + { + PredictionRigidbody2D.ForceApplicationType appType = value.Type; + w.WriteUInt8Unpacked((byte)appType); + PredictionRigidbody2D.AllForceData data = value.Data; + + switch (appType) + { + case PredictionRigidbody2D.ForceApplicationType.AddForce: + case PredictionRigidbody2D.ForceApplicationType.AddRelativeForce: + w.WriteVector3(data.Vector3Force); + w.WriteInt32((byte)data.Mode); + break; + case PredictionRigidbody2D.ForceApplicationType.AddTorque: + w.WriteSingle(data.FloatForce); + w.WriteInt32((byte)data.Mode); + break; + case PredictionRigidbody2D.ForceApplicationType.AddForceAtPosition: + w.WriteVector3(data.Vector3Force); + w.WriteVector3(data.Position); + w.WriteInt32((byte)data.Mode); + break; + default: + NetworkManagerExtensions.LogError($"ForceApplicationType of {appType} is not supported."); + break; + } + } + + public static PredictionRigidbody2D.EntryData ReadForceData(this Reader r) + { + PredictionRigidbody2D.EntryData fd = new(); + + PredictionRigidbody2D.ForceApplicationType appType = (PredictionRigidbody2D.ForceApplicationType)r.ReadUInt8Unpacked(); + fd.Type = appType; + + PredictionRigidbody2D.AllForceData data = new(); + + switch (appType) + { + case PredictionRigidbody2D.ForceApplicationType.AddForce: + case PredictionRigidbody2D.ForceApplicationType.AddRelativeForce: + data.Vector3Force = r.ReadVector3(); + data.Mode = (ForceMode2D)r.ReadUInt8Unpacked(); + return fd; + case PredictionRigidbody2D.ForceApplicationType.AddTorque: + data.FloatForce = r.ReadSingle(); + data.Mode = (ForceMode2D)r.ReadUInt8Unpacked(); + return fd; + case PredictionRigidbody2D.ForceApplicationType.AddForceAtPosition: + data.Vector3Force = r.ReadVector3(); + data.Position = r.ReadVector3(); + data.Mode = (ForceMode2D)r.ReadUInt8Unpacked(); + return fd; + default: + NetworkManagerExtensions.LogError($"ForceApplicationType of {appType} is not supported."); + return fd; + } + } + + public static void WritePredictionRigidbody2D(this Writer w, PredictionRigidbody2D pr) + { + w.Write(pr.Rigidbody2D.GetState()); + w.WriteList(pr.GetPendingForces()); + } + + public static PredictionRigidbody2D ReadPredictionRigidbody2D(this Reader r) + { + List lst = CollectionCaches.RetrieveList(); + Rigidbody2DState rs = r.Read(); + r.ReadList(ref lst); + PredictionRigidbody2D pr = ResettableObjectCaches.Retrieve(); + + pr.SetReconcileData(rs, lst); + pr.SetPendingForces(lst); + return pr; + } + + } + + [UseGlobalCustomSerializer] + [Preserve] + public class PredictionRigidbody2D : IResettable + { + #region Types. + //How the force was applied. + [System.Flags] + public enum ForceApplicationType : byte + { + AddForceAtPosition = 1, + AddForce = 4, + AddRelativeForce = 8, + AddTorque = 16, + } + public struct AllForceData + { + public Vector3 Vector3Force; + public float FloatForce; + public Vector3 Position; + public ForceMode2D Mode; + + public AllForceData(Vector3 force, ForceMode2D mode) : this() + { + Vector3Force = force; + Mode = mode; + } + + + public AllForceData(float force, ForceMode2D mode) : this() + { + FloatForce = force; + Mode = mode; + } + + public AllForceData(Vector3 force, Vector3 position, ForceMode2D mode) : this() + { + Vector3Force = force; + Position = position; + Mode = mode; + } + } + + + [UseGlobalCustomSerializer] + public struct EntryData + { + public ForceApplicationType Type; + public AllForceData Data; + + public EntryData(ForceApplicationType type, AllForceData data) + { + Type = type; + Data = data; + } + public EntryData(EntryData fd) + { + Type = fd.Type; + Data = fd.Data; + } + } + #endregion + + #region Internal. + /// + /// Rigidbody2DState set only as reconcile data. + /// + [System.NonSerialized] + internal Rigidbody2DState Rigidbody2DState; + #endregion + + #region Public. + /// + /// Rigidbody which force is applied. + /// + public Rigidbody2D Rigidbody2D { get; private set; } + /// + /// Returns if there are any pending forces. + /// + public bool HasPendingForces => (_pendingForces != null && _pendingForces.Count > 0); + #endregion + + #region Private + /// + /// Forces waiting to be applied. + /// + [ExcludeSerialization] + private List _pendingForces; + /// + /// Returns current pending forces. + /// Modifying this collection could cause undesirable results. + /// + public List GetPendingForces() => _pendingForces; + #endregion + + ~PredictionRigidbody2D() + { + if (_pendingForces != null) + CollectionCaches.StoreAndDefault(ref _pendingForces); + Rigidbody2D = null; + } + + /// + /// Rigidbody which force is applied. + /// + /// + public void Initialize(Rigidbody2D rb) + { + Rigidbody2D = rb; + if (_pendingForces == null) + _pendingForces = CollectionCaches.RetrieveList(); + else + _pendingForces.Clear(); + } + + /// + /// Adds Velocity force to the Rigidbody. + /// + public void AddForce(Vector3 force, ForceMode2D mode = ForceMode2D.Force) + { + EntryData fd = new(ForceApplicationType.AddForce, + new(force, mode)); + _pendingForces.Add(fd); + } + public void AddRelativeForce(Vector3 force, ForceMode2D mode = ForceMode2D.Force) + { + EntryData fd = new(ForceApplicationType.AddRelativeForce, + new(force, mode)); + _pendingForces.Add(fd); + + } + public void AddTorque(float force, ForceMode2D mode = ForceMode2D.Force) + { + EntryData fd = new(ForceApplicationType.AddTorque, + new(force, mode)); + _pendingForces.Add(fd); + } + public void AddForceAtPosition(Vector3 force, Vector3 position, ForceMode2D mode = ForceMode2D.Force) + { + EntryData fd = new(ForceApplicationType.AddForceAtPosition, + new(force, position, mode)); + _pendingForces.Add(fd); + } + + /// + /// Sets velocity while clearing pending forces. + /// Simulate should still be called normally. + /// + public void Velocity(Vector3 force) + { + Rigidbody2D.linearVelocity = force; + RemoveForces(true); + } + + /// + /// Sets angularVelocity while clearning pending forces. + /// Simulate should still be called normally. + /// + public void AngularVelocity(float force) + { + Rigidbody2D.angularVelocity = force; + RemoveForces(false); + } + + /// + /// Applies pending forces to rigidbody in the order they were added. + /// + public void Simulate() + { + foreach (EntryData item in _pendingForces) + { + AllForceData data = item.Data; + switch (item.Type) + { + case ForceApplicationType.AddTorque: + Rigidbody2D.AddTorque(data.FloatForce, data.Mode); + break; + case ForceApplicationType.AddForce: + Rigidbody2D.AddForce(data.Vector3Force, data.Mode); + break; + case ForceApplicationType.AddRelativeForce: + Rigidbody2D.AddRelativeForce(data.Vector3Force, data.Mode); + break; + case ForceApplicationType.AddForceAtPosition: + Rigidbody2D.AddForceAtPosition(data.Vector3Force, data.Position, data.Mode); + break; + } + } + _pendingForces.Clear(); + } + + /// + /// Manually clears pending forces. + /// + /// True to clear velocities, false to clear angular velocities. + public void ClearPendingForces(bool velocity) + { + RemoveForces(velocity); + } + /// + /// Clears pending velocity and angular velocity forces. + /// + public void ClearPendingForces() + { + _pendingForces.Clear(); + } + + /// + /// Reconciles to a state. + /// + public void Reconcile(PredictionRigidbody2D pr) + { + _pendingForces.Clear(); + if (pr._pendingForces != null) + { + foreach (EntryData item in pr._pendingForces) + _pendingForces.Add(new(item)); + } + Rigidbody2D.SetState(pr.Rigidbody2DState); + + ResettableObjectCaches.Store(pr); + } + + /// + /// Removes forces from pendingForces. + /// + /// True to remove if velocity, false if to remove angular velocity. + private void RemoveForces(bool velocity) + { + if (_pendingForces.Count > 0) + { + bool shouldExist = velocity; + ForceApplicationType velocityApplicationTypes = (ForceApplicationType.AddRelativeForce | ForceApplicationType.AddForce); + + List newDatas = CollectionCaches.RetrieveList(); + foreach (EntryData item in _pendingForces) + { + if (VelocityApplicationTypesContains(item.Type) == !velocity) + newDatas.Add(item); + } + //Add back to _pendingForces if changed. + if (newDatas.Count != _pendingForces.Count) + { + _pendingForces.Clear(); + foreach (EntryData item in newDatas) + _pendingForces.Add(item); + } + CollectionCaches.Store(newDatas); + + bool VelocityApplicationTypesContains(ForceApplicationType apt) + { + return (velocityApplicationTypes & apt) == apt; + } + } + + + } + + internal void SetPendingForces(List lst) => _pendingForces = lst; + + internal void SetReconcileData(Rigidbody2DState rs, List lst) + { + Rigidbody2DState = rs; + _pendingForces = lst; + } + + public void ResetState() + { + CollectionCaches.StoreAndDefault(ref _pendingForces); + Rigidbody2D = null; + } + + public void InitializeState() { } + } + +} + diff --git a/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs.meta new file mode 100644 index 0000000..49fe21d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1b6a1cc418198134180faa3438517b52 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/PredictionRigidbody2D.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs b/Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs new file mode 100644 index 0000000..3b6bff7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs @@ -0,0 +1,102 @@ +using FishNet.Utility; +using System.Runtime.CompilerServices; +using FishNet.CodeGenerating; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] + +namespace FishNet.Object.Prediction +{ + [MakePublic] + internal struct ReplicateDataContainer where T : IReplicateData, new() + { + #region Types + private enum DataCachingType + { + Unset, + ValueType, + IResettableReferenceType, + ReferenceType, + } + #endregion + + /// + /// Replicate data. + /// + public T Data; + /// + /// True if the data was created locally or came through the network as created. + /// + public bool IsCreated; + /// + /// Channel the data came in on. + /// + public readonly Channel Channel; + /// + /// True if populated. + /// + public bool IsValid { get; private set; } + + /// + /// How data should be cached and retrieved when not set. + /// + private static DataCachingType _dataCachingType = DataCachingType.Unset; + + public ReplicateDataContainer(T data, Channel channel) : this(data, channel, tick: 0, isCreated: false) { } + + public ReplicateDataContainer(T data, Channel channel, bool isCreated) : this(data, channel, tick: 0, isCreated) { } + + public ReplicateDataContainer(T data, Channel channel, uint tick, bool isCreated = false) + { + Data = data; + Channel = channel; + IsCreated = isCreated; + IsValid = true; + + SetDataTick(tick); + } + + /// + /// A shortcut to calling Data.SetTick. + /// + public void SetDataTick(uint tick) + { + SetDataIfNull(ref Data); + Data.SetTick(tick); + } + + /// + /// Sets data to new() if is nullable type, and is null. + /// + /// + private void SetDataIfNull(ref T data) + { + //Only figure out data caching type once to save perf. + if (_dataCachingType == DataCachingType.Unset) + { + if (typeof(T).IsValueType) + _dataCachingType = DataCachingType.ValueType; + else if (typeof(IResettable).IsAssignableFrom(typeof(T))) + _dataCachingType = DataCachingType.IResettableReferenceType; + else + _dataCachingType = DataCachingType.ReferenceType; + } + + if (_dataCachingType != DataCachingType.ValueType && data == null) + data = ObjectCaches.Retrieve(); + } + + public void Dispose() + { + if (Data != null) + Data.Dispose(); + + IsValid = false; + } + + public static ReplicateDataContainer GetDefault(uint tick) => new(default(T), Channel.Unreliable, tick); + + public static ReplicateDataContainer GetDefault() => GetDefault(tick: 0); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs.meta new file mode 100644 index 0000000..35b7894 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2e45211dba582c146993b54bf316d8fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/ReplicateDataContainer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs b/Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs new file mode 100644 index 0000000..b65984c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs @@ -0,0 +1,175 @@ +#if !FISHNET_STABLE_REPLICATESTATES +using System; +using FishNet.Utility; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] + +namespace FishNet.Object.Prediction +{ + [System.Flags] + public enum ReplicateState : byte + { + /// + /// The default value of this state. + /// This value should never occur when a replicate runs. + /// + Invalid = 0, + /// + /// Server and clients use this flag. + /// Flag will be set if data tick has run outside a reconcile, such as from user code within OnTick. + /// + Ticked = (1 << 0), //1 + /// + /// Only client will use this flag. + /// Flag is set if data is being run during a reconcile. + /// + Replayed = (1 << 1), //2 + /// + /// Server and client use this flag. + /// Data has been created by the server or client. + /// This indicates that data is known and was intentionally sent. + /// + Created = (1 << 2), //4 + } + + public static class ReplicateStateExtensions + { + /// + /// Returns if value is valid. + /// This should never be false. + /// + public static bool IsValid(this ReplicateState value) => (value != ReplicateState.Invalid); + + /// + /// Returns if value contains ReplicateState.Ticked. + /// + public static bool ContainsTicked(this ReplicateState value) => value.FastContains(ReplicateState.Ticked); + + /// + /// Returns if value contains ReplicateState.Created. + /// + public static bool ContainsCreated(this ReplicateState value) => value.FastContains(ReplicateState.Created); + + /// + /// Returns if value contains ReplicateState.Replayed. + /// + public static bool ContainsReplayed(this ReplicateState value) => value.FastContains(ReplicateState.Replayed); + + [Obsolete("Use ContainsReplayed.")] + public static bool IsReplayed(this ReplicateState value) => value.ContainsReplayed(); + + /// + /// Returns if value is (ReplicateState.Ticked | ReplicateState.Created). + /// + public static bool IsTickedCreated(this ReplicateState value) => (value == (ReplicateState.Ticked | ReplicateState.Created)); + + /// + /// Returns if value equals ReplicateState.Ticked. + /// + public static bool IsTickedNonCreated(this ReplicateState value) => (value == ReplicateState.Ticked); + + /// + /// Returns if value is (ReplicateState.Replayed | ReplicateState.Ticked | ReplicateState.Created). + /// + public static bool IsReplayedCreated(this ReplicateState value) => (value == (ReplicateState.Replayed | ReplicateState.Created)); + + /// + /// Returns if value is ReplicateState.Replayed without ReplicateState.Ticked nor ReplicateState.Created. + /// + public static bool IsFuture(this ReplicateState value) => (value == ReplicateState.Replayed); + + [Obsolete("Use ContainsCreated.")] + public static bool IsCreated(this ReplicateState value) => value.ContainsCreated(); + + /// + /// True if part is containined within whole. + /// + public static bool FastContains(this ReplicateState whole, ReplicateState part) => (whole & part) == part; + } +} +#else +using FishNet.Utility; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.CODEGEN_ASSEMBLY_NAME)] +namespace FishNet.Object +{ + + public enum ReplicateState : byte + { + /// + /// The default value of this state. + /// This value should never occur when a replicate runs. + /// + Invalid = 0, + /// + /// Value is seen on server and clients. + /// Client or server has data on the object for the tick. + /// Clients will only see this value on spectated objects when PredictionManager is using Appended state order. + /// + CurrentCreated = 1, + /// + /// Value is only seen on server when they do not own the object. + /// Server does not have data on this non-owned object for the tick but expected to, such as a state should have arrived but did not. + /// + [System.Obsolete("This is currently not used but may be in a later release. Please read summary for value.")] + CurrentPredicted = 2, + /// + /// Value is only seen on clients when they do not own the object. + /// Client does not have data for the tick but expected to, such as a state should have arrived but did not. + /// Client is currently reconciling. + /// + [System.Obsolete("This is currently not used but may be in a later release. Please read summary for value.")] + ReplayedPredicted = 3, + /// + /// Value is only seen on clients. + /// Client has data on the object for the tick. + /// Client is currently reconciling. + /// + ReplayedCreated = 4, + /// + /// Value is only seen on clients when they do not own the object. + /// Tick is in the future and data cannot yet be known. + /// This can be used to exit replicate early to not process actions, or create actions based on previous datas. + /// + CurrentFuture = 5, + /// + /// Value is only seen on clients when they do not own the object. + /// Tick is in the future and data cannot yet be known. + /// Client is currently reconciling. + /// This can be used to exit replicate early to not process actions, or create actions based on previous datas. + /// + ReplayedFuture = 6, + } + + public static class ReplicateStateExtensions + { + /// + /// Returns if value is valid. + /// This should never be false. + /// + public static bool IsValid(this ReplicateState value) => (value != ReplicateState.Invalid); + /// + /// Returns if value is replayed. + /// +#pragma warning disable CS0618 // Type or member is obsolete + public static bool IsReplayed(this ReplicateState value) => (value == ReplicateState.ReplayedPredicted || value == ReplicateState.ReplayedCreated || value == ReplicateState.ReplayedFuture); +#pragma warning restore CS0618 // Type or member is obsolete + /// + /// Returns if value is user created. + /// + public static bool IsCreated(this ReplicateState value) => (value == ReplicateState.CurrentCreated || value == ReplicateState.ReplayedCreated); + /// + /// Returns if value is predicted. + /// +#pragma warning disable CS0618 // Type or member is obsolete + public static bool IsPredicted(this ReplicateState value) => (value == ReplicateState.ReplayedPredicted); +#pragma warning restore CS0618 // Type or member is obsolete + /// + /// Returns if value is in the future. + /// + public static bool IsFuture(this ReplicateState value) => (value == ReplicateState.CurrentFuture || value == ReplicateState.ReplayedFuture); + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs.meta b/Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs.meta new file mode 100644 index 0000000..1f480b2 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f8deb74673db557489cf93509af3cb21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Prediction/ReplicateState.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing.meta b/Assets/FishNet/Runtime/Object/Synchronizing.meta new file mode 100644 index 0000000..161bfec --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5429986c563a23a4c86f7b6724e41a70 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta.meta new file mode 100644 index 0000000..15b1209 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fa893d9b2e2f2f4459efd0fe00924524 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs new file mode 100644 index 0000000..b291d94 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs @@ -0,0 +1,631 @@ +#if !FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace FishNet.Object.Synchronizing +{ + [System.Serializable] + public class SyncDictionary : 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 Dictionary Collection; + /// + /// 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 Dictionary _initialValues = new(); + /// + /// Changed data which will be sent next tick. + /// + private List _changed = new(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges = new(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges = new(); + /// + /// 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 + + #region Constructors. + public SyncDictionary(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveDictionary(), settings) { } + + public SyncDictionary(Dictionary collection, SyncTypeSettings settings = new()) : base(settings) + { + Collection = (collection == null) ? CollectionCaches.RetrieveDictionary() : collection; + _initialValues = CollectionCaches.RetrieveDictionary(); + _changed = CollectionCaches.RetrieveList(); + _serverOnChanges = CollectionCaches.RetrieveList(); + _clientOnChanges = CollectionCaches.RetrieveList(); + } + #endregion + + #region Deconstructor. + ~SyncDictionary() + { + CollectionCaches.StoreAndDefault(ref Collection); + CollectionCaches.StoreAndDefault(ref _initialValues); + CollectionCaches.StoreAndDefault(ref _changed); + CollectionCaches.StoreAndDefault(ref _serverOnChanges); + CollectionCaches.StoreAndDefault(ref _clientOnChanges); + } + #endregion + + /// + /// 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) + { + return Collection; + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_initialValues == null) _initialValues = new(); + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + + 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] + private void AddOperation(SyncDictionaryOperation operation, TKey key, TValue value) + { + if (!base.IsInitialized) + 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.IsServerStarted); + + if (asServerInvoke) + { + _valuesChanged = true; + if (base.Dirty()) + { + ChangeData change = new(operation, key, value); + _changed.Add(change); + } + } + + InvokeOnChange(operation, key, value, asServerInvoke); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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] + protected internal 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.WriteUInt8Unpacked((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] + protected internal 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.WriteUInt8Unpacked((byte)SyncDictionaryOperation.Add); + writer.Write(item.Key); + writer.Write(item.Value); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + //True to warn if this object was deinitialized on the server. + bool deinitialized = (asClientHost && !base.OnStartServerCalled); + if (deinitialized) + base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation."); + + IDictionary collection = Collection; + + bool fullWrite = reader.ReadBoolean(); + + //Clear collection since it's a full write. + if (canModifyValues && fullWrite) + collection.Clear(); + + int changes = reader.ReadInt32(); + for (int i = 0; i < changes; i++) + { + SyncDictionaryOperation operation = (SyncDictionaryOperation)reader.ReadUInt8Unpacked(); + 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(); + + if (canModifyValues) + collection[key] = value; + } + //Clear. + else if (operation == SyncDictionaryOperation.Clear) + { + if (canModifyValues) + collection.Clear(); + } + //Remove. + else if (operation == SyncDictionaryOperation.Remove) + { + key = reader.Read(); + + if (canModifyValues) + collection.Remove(key); + } + + if (newChangeId) + InvokeOnChange(operation, key, value, false); + } + + //If changes were made invoke complete after all have been read. + if (newChangeId && 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(operation, key, value)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, key, value, asServer); + else + _clientOnChanges.Add(new(operation, key, value)); + } + } + + /// + /// Resets to initialized values. + /// + [APIExclude] + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + + if (base.CanReset(asServer)) + { + _sendAll = false; + _changed.Clear(); + Collection.Clear(); + _valuesChanged = false; + + foreach (KeyValuePair item in _initialValues) + Collection[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. + 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. + 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.IsInitialized) + return; + if (!base.CanNetworkSetValues(log: true)) + return; + + if (base.Dirty()) + _sendAll = true; + } + + /// + /// Dirties an entry by key. + /// + /// Key to dirty. + public void Dirty(TKey key) + { + if (!base.IsInitialized) + 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.IsInitialized) + 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(); + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs.meta new file mode 100644 index 0000000..d7ab674 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 79196d6c6e862da499491d106f66ad72 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncDictionary.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs new file mode 100644 index 0000000..55a3a13 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs @@ -0,0 +1,627 @@ +#if !FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections; +using System.Collections.Generic; + +namespace FishNet.Object.Synchronizing +{ + [System.Serializable] + 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 HashSet Collection; + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + #endregion + + #region Private. + /// + /// ListCache for comparing. + /// + private static List _cache = new(); + /// + /// Values upon initialization. + /// + private HashSet _initialValues; + /// + /// Changed data which will be sent next tick. + /// + private List _changed; + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges; + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges; + /// + /// Comparer to see if entries change when calling public methods. + /// //Not used right now. + /// + private readonly IEqualityComparer _comparer; + /// + /// 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 + + #region Constructors. + public SyncHashSet(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveHashSet(), EqualityComparer.Default, settings) { } + public SyncHashSet(IEqualityComparer comparer, SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveHashSet(), (comparer == null) ? EqualityComparer.Default : comparer, settings) { } + + public SyncHashSet(HashSet collection, IEqualityComparer comparer = null, SyncTypeSettings settings = new()) : base(settings) + { + _comparer = (comparer == null) ? EqualityComparer.Default : comparer; + Collection = (collection == null) ? CollectionCaches.RetrieveHashSet() : collection; + + _initialValues = CollectionCaches.RetrieveHashSet(); + _changed = CollectionCaches.RetrieveList(); + _serverOnChanges = CollectionCaches.RetrieveList(); + _clientOnChanges = CollectionCaches.RetrieveList(); + } + #endregion + + #region Deconstructor. + ~SyncHashSet() + { + CollectionCaches.StoreAndDefault(ref Collection); + CollectionCaches.StoreAndDefault(ref _initialValues); + CollectionCaches.StoreAndDefault(ref _changed); + CollectionCaches.StoreAndDefault(ref _serverOnChanges); + CollectionCaches.StoreAndDefault(ref _clientOnChanges); + } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_initialValues == null) _initialValues = new(); + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + foreach (T item in Collection) + _initialValues.Add(item); + } + + /// + /// Gets the collection being used within this SyncList. + /// + /// + public HashSet GetCollection(bool asServer) + { + return Collection; + } + + /// + /// Adds an operation and invokes locally. + /// + private void AddOperation(SyncHashSetOperation operation, T item) + { + if (!base.IsInitialized) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + if (asServerInvoke) + { + _valuesChanged = true; + if (base.Dirty()) + { + ChangeData change = new(operation, item); + _changed.Add(change); + } + } + + InvokeOnChange(operation, item, asServerInvoke); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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. + protected internal 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.WriteUInt8Unpacked((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. + /// + /// + protected internal 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.WriteUInt8Unpacked((byte)SyncHashSetOperation.Add); + writer.Write(item); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + //True to warn if this object was deinitialized on the server. + bool deinitialized = (asClientHost && !base.OnStartServerCalled); + if (deinitialized) + base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation."); + + ISet collection = Collection; + + bool fullWrite = reader.ReadBoolean(); + + //Clear collection since it's a full write. + if (canModifyValues && fullWrite) + collection.Clear(); + + int changes = reader.ReadInt32(); + for (int i = 0; i < changes; i++) + { + SyncHashSetOperation operation = (SyncHashSetOperation)reader.ReadUInt8Unpacked(); + T next = default; + + //Add. + if (operation == SyncHashSetOperation.Add) + { + next = reader.Read(); + + if (canModifyValues) + collection.Add(next); + } + //Clear. + else if (operation == SyncHashSetOperation.Clear) + { + if (canModifyValues) + collection.Clear(); + } + //Remove. + else if (operation == SyncHashSetOperation.Remove) + { + next = reader.Read(); + + if (canModifyValues) + collection.Remove(next); + } + //Updated. + else if (operation == SyncHashSetOperation.Update) + { + next = reader.Read(); + + if (canModifyValues) + { + collection.Remove(next); + collection.Add(next); + } + } + + if (newChangeId) + InvokeOnChange(operation, next, false); + } + + //If changes were made invoke complete after all have been read. + if (newChangeId && 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(operation, item)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, item, asServer); + else + _clientOnChanges.Add(new(operation, item)); + } + } + + /// + /// Resets to initialized values. + /// + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + + if (base.CanReset(asServer)) + { + _sendAll = false; + _changed.Clear(); + Collection.Clear(); + + foreach (T item in _initialValues) + Collection.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) + 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) + 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) + AddOperation(SyncHashSetOperation.Remove, item); + + return result; + } + + /// + /// Dirties the entire collection forcing a full send. + /// + public void DirtyAll() + { + if (!base.IsInitialized) + return; + if (!base.CanNetworkSetValues(log: 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.IsInitialized) + 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) + { + _cache.AddRange(Collection); + + int count = _cache.Count; + for (int i = 0; i < count; i++) + { + T entry = _cache[i]; + if (!other.Contains(entry)) + Remove(entry); + } + + _cache.Clear(); + } + + 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); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs.meta new file mode 100644 index 0000000..5beeeb0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b8627bf3171f6274790bc7e60e471260 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncHashset.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs new file mode 100644 index 0000000..367e6d6 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs @@ -0,0 +1,746 @@ +#if !FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections; +using System.Collections.Generic; + +namespace FishNet.Object.Synchronizing +{ + [System.Serializable] + 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; + } + } + #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 List Collection; + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + #endregion + + #region Private. + /// + /// Values upon initialization. + /// + private List _initialValues; + /// + /// Comparer to see if entries change when calling public methods. + /// + private readonly IEqualityComparer _comparer; + /// + /// Changed data which will be sent next tick. + /// + private List _changed; + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges; + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges; + /// + /// 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 + + #region Constructors. + public SyncList(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveList(), EqualityComparer.Default, settings) { } + public SyncList(IEqualityComparer comparer, SyncTypeSettings settings = new()) : this(new(), (comparer == null) ? EqualityComparer.Default : comparer, settings) { } + + public SyncList(List collection, IEqualityComparer comparer = null, SyncTypeSettings settings = new()) : base(settings) + { + _comparer = (comparer == null) ? EqualityComparer.Default : comparer; + Collection = (collection == null) ? CollectionCaches.RetrieveList() : collection; + + _initialValues = CollectionCaches.RetrieveList(); + _changed = CollectionCaches.RetrieveList(); + _serverOnChanges = CollectionCaches.RetrieveList(); + _clientOnChanges = CollectionCaches.RetrieveList(); + } + #endregion + + #region Deconstructor. + ~SyncList() + { + CollectionCaches.StoreAndDefault(ref Collection); + CollectionCaches.StoreAndDefault(ref _initialValues); + CollectionCaches.StoreAndDefault(ref _changed); + CollectionCaches.StoreAndDefault(ref _serverOnChanges); + CollectionCaches.StoreAndDefault(ref _clientOnChanges); + } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_initialValues == null) _initialValues = new(); + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + + 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) + { + return Collection; + } + + /// + /// Adds an operation and invokes locally. + /// + /// + /// + /// + /// + private void AddOperation(SyncListOperation operation, int index, T prev, T next) + { + if (!base.IsInitialized) + 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.IsServerStarted); + + /* 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(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. + protected internal 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. + protected internal 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); + + //Number of entries expected. + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteUInt8Unpacked((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. + /// + /// + protected internal 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); + for (int i = 0; i < count; i++) + { + writer.WriteUInt8Unpacked((byte)SyncListOperation.Add); + writer.Write(Collection[i]); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + //True to warn if this object was deinitialized on the server. + bool deinitialized = (asClientHost && !base.OnStartServerCalled); + if (deinitialized) + base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation."); + + List collection = Collection; + + bool fullWrite = reader.ReadBoolean(); + //Clear collection since it's a full write. + if (canModifyValues && fullWrite) + collection.Clear(); + + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncListOperation operation = (SyncListOperation)reader.ReadUInt8Unpacked(); + int index = -1; + T prev = default; + T next = default; + + //Add. + if (operation == SyncListOperation.Add) + { + next = reader.Read(); + + if (canModifyValues) + { + index = collection.Count; + collection.Add(next); + } + } + //Clear. + else if (operation == SyncListOperation.Clear) + { + if (canModifyValues) + collection.Clear(); + } + //Insert. + else if (operation == SyncListOperation.Insert) + { + index = reader.ReadInt32(); + next = reader.Read(); + + if (canModifyValues) + collection.Insert(index, next); + } + //RemoveAt. + else if (operation == SyncListOperation.RemoveAt) + { + index = reader.ReadInt32(); + + if (canModifyValues) + { + prev = collection[index]; + collection.RemoveAt(index); + } + } + //Set + else if (operation == SyncListOperation.Set) + { + index = reader.ReadInt32(); + next = reader.Read(); + + if (canModifyValues) + { + prev = collection[index]; + collection[index] = next; + } + } + + if (newChangeId) + InvokeOnChange(operation, index, prev, next, false); + } + + //If changes were made invoke complete after all have been read. + if (newChangeId && 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(operation, index, prev, next)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, index, prev, next, asServer); + else + _clientOnChanges.Add(new(operation, index, prev, next)); + } + } + + /// + /// Resets to initialized values. + /// + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + + if (base.CanReset(asServer)) + { + _sendAll = false; + _changed.Clear(); + Collection.Clear(); + + foreach (T item in _initialValues) + Collection.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) + 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) + 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(); + 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) + 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) + AddOperation(SyncListOperation.RemoveAt, index, oldItem, default); + } + + /// + /// Removes all values within the collection. + /// + /// + /// + public int RemoveAll(Predicate match) + { + List toRemove = new(); + 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.IsInitialized) + 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. + /// 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; + + T value = Collection[index]; + 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) + AddOperation(SyncListOperation.Set, index, prev, value); + } + } + + /// + /// Returns Enumerator for collection. + /// + /// + public IEnumerator GetEnumerator() => Collection.GetEnumerator(); + + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs.meta new file mode 100644 index 0000000..24a80c5 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9561e48a6cd328040aa2a6de9193e677 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncList.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs new file mode 100644 index 0000000..5b9d1ac --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs @@ -0,0 +1,387 @@ +#if !FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Collections.Generic; + +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(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges = new(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges = new(); + #endregion + + #region Constructors + public SyncStopwatch(SyncTypeSettings settings = new()) : base(settings) { } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + } + + /// + /// 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.IsInitialized) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + if (asServerInvoke) + { + if (base.Dirty()) + { + ChangeData change = new(operation, prev); + _changed.Add(change); + } + } + + OnChange?.Invoke(operation, prev, asServerInvoke); + } + + /// + /// Writes all changed values. + /// + ///True to set the next time data may sync. + protected internal 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.WriteUInt8Unpacked((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. + /// + protected internal 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.WriteUInt8Unpacked((byte)SyncStopwatchOperation.Pause); + } + + /// + /// Writers a start with elapsed time. + /// + /// + private void WriteStartStopwatch(Writer w, float elapsed, bool includeOperationByte) + { + if (includeOperationByte) + w.WriteUInt8Unpacked((byte)SyncStopwatchOperation.Start); + + w.WriteSingle(elapsed); + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncStopwatchOperation op = (SyncStopwatchOperation)reader.ReadUInt8Unpacked(); + if (op == SyncStopwatchOperation.Start) + { + float elapsed = reader.ReadSingle(); + + if (canModifyValues) + Elapsed = elapsed; + + if (newChangeId) + InvokeOnChange(op, elapsed, asServer); + } + else if (op == SyncStopwatchOperation.Pause) + { + if (canModifyValues) + Paused = true; + + if (newChangeId) + InvokeOnChange(op, -1f, asServer); + } + else if (op == SyncStopwatchOperation.PauseUpdated) + { + float prev = reader.ReadSingle(); + + if (canModifyValues) + Paused = true; + + if (newChangeId) + InvokeOnChange(op, prev, asServer); + } + else if (op == SyncStopwatchOperation.Unpause) + { + if (canModifyValues) + Paused = false; + + if (newChangeId) + InvokeOnChange(op, -1f, asServer); + } + else if (op == SyncStopwatchOperation.Stop) + { + if (canModifyValues) + StopStopwatch_Internal(asServer); + + if (newChangeId) + InvokeOnChange(op, -1f, false); + } + else if (op == SyncStopwatchOperation.StopUpdated) + { + float prev = reader.ReadSingle(); + if (canModifyValues) + StopStopwatch_Internal(asServer); + + if (newChangeId) + InvokeOnChange(op, prev, asServer); + } + } + + if (newChangeId && changes > 0) + InvokeOnChange(SyncStopwatchOperation.Complete, -1f, asServer); + } + + /// + /// Stops the Stopwatch and resets. + /// + private void StopStopwatch_Internal(bool asServer) + { + 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(operation, prev)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, prev, asServer); + else + _clientOnChanges.Add(new(operation, prev)); + } + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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(); + } + + /// + /// Adds 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; + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs.meta new file mode 100644 index 0000000..32c41e6 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1aa50198b4dd1bc45a6b7ef0461c5809 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncStopwatch.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs new file mode 100644 index 0000000..c9ceebd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs @@ -0,0 +1,485 @@ +#if !FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System.Collections.Generic; +using UnityEngine; + +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(); + + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges = new(); + + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges = new(); + + /// + /// Last Time.unscaledTime the timer delta was updated. + /// + private float _updateTime; + #endregion + + #region Constructors + public SyncTimer(SyncTypeSettings settings = new()) : base(settings) { } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + } + + /// + /// 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; + SetUpdateTime(); + 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, Remaining, Remaining); + } + + /// + /// 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; + SetUpdateTime(); + AddOperation(SyncTimerOperation.Unpause, Remaining, Remaining); + } + + /// + /// 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.IsInitialized) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + if (asServerInvoke) + { + if (base.Dirty()) + { + ChangeData change = new(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. + protected internal 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.WriteUInt8Unpacked((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. + /// + protected internal 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.WriteUInt8Unpacked((byte)SyncTimerOperation.Pause); + } + + /// + /// Writes a StartTimer operation. + /// + /// + /// + private void WriteStartTimer(Writer w, bool includeOperationByte) + { + if (includeOperationByte) + w.WriteUInt8Unpacked((byte)SyncTimerOperation.Start); + w.WriteSingle(Remaining); + w.WriteSingle(Duration); + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + int changes = reader.ReadInt32(); + //Has previous value if should invoke finished. + float? finishedPrevious = null; + + for (int i = 0; i < changes; i++) + { + SyncTimerOperation op = (SyncTimerOperation)reader.ReadUInt8Unpacked(); + if (op == SyncTimerOperation.Start) + { + float next = reader.ReadSingle(); + float duration = reader.ReadSingle(); + + if (canModifyValues) + { + SetUpdateTime(); + Paused = false; + Remaining = next; + Duration = duration; + } + + if (newChangeId) + { + InvokeOnChange(op, -1f, next, asServer); + /* If next is 0 then that means the timer + * expired on the same tick it was started. + * This can be true depending on when in code + * the server starts the timer. + * + * When 0 also invoke finished. */ + if (next == 0) + finishedPrevious = duration; + } + } + else if (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated || op == SyncTimerOperation.Unpause) + { + if (canModifyValues) + UpdatePauseState(op); + } + else if (op == SyncTimerOperation.Stop) + { + float prev = Remaining; + + if (canModifyValues) + StopTimer_Internal(asServer); + + if (newChangeId) + InvokeOnChange(op, prev, 0f, false); + } + // + else if (op == SyncTimerOperation.StopUpdated) + { + float prev = Remaining; + float next = reader.ReadSingle(); + + if (canModifyValues) + StopTimer_Internal(asServer); + + if (newChangeId) + 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 = Remaining; + float next; + //If updated time as well. + if (op == SyncTimerOperation.PauseUpdated) + { + next = reader.ReadSingle(); + Remaining = next; + } + else + { + next = Remaining; + } + + Paused = newPauseState; + if (!Paused) + SetUpdateTime(); + if (newChangeId) + InvokeOnChange(op, prev, next, asServer); + } + + if (newChangeId && changes > 0) + InvokeOnChange(SyncTimerOperation.Complete, -1f, -1f, false); + if (finishedPrevious.HasValue) + InvokeFinished(finishedPrevious.Value); + } + + /// + /// Stops the timer and resets. + /// + private void StopTimer_Internal(bool asServer) + { + 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(operation, prev, next)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, prev, next, asServer); + else + _clientOnChanges.Add(new(operation, prev, next)); + } + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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(); + } + + /// + /// Sets updateTime to current values. + /// + private void SetUpdateTime() + { + _updateTime = Time.unscaledTime; + } + + /// + /// Removes time passed from Remaining since the last unscaled time using this method. + /// + public void Update() + { + float delta = (Time.unscaledTime - _updateTime); + Update(delta); + } + + /// + /// Removes delta from Remaining for server and client. + /// This also resets unscaledTime delta for Update(). + /// + /// Value to remove from Remaining. + public void Update(float delta) + { + //Not enabled. + if (Remaining <= 0f) + return; + if (Paused) + return; + + SetUpdateTime(); + 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; + InvokeFinished(prev); + } + + /// + /// Invokes SyncTimer finished a previous value. + /// + /// + private void InvokeFinished(float prev) + { + if (base.NetworkManager.IsServerStarted) + OnChange?.Invoke(SyncTimerOperation.Finished, prev, 0f, true); + if (base.NetworkManager.IsClientStarted) + OnChange?.Invoke(SyncTimerOperation.Finished, prev, 0f, false); + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => null; + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs.meta new file mode 100644 index 0000000..6aee5fa --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 46c58fcbaf8624a49834273c8e6eb871 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncTimer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs new file mode 100644 index 0000000..b5933e7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs @@ -0,0 +1,475 @@ +#if !FISHNET_STABLE_SYNCTYPES +using FishNet.CodeGenerating; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Helping; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + internal interface ISyncVar { } + + [APIExclude] + [System.Serializable] + [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)] + public class SyncVar : SyncBase, ISyncVar + { + #region Types. + public struct InterpolationContainer + { + /// + /// Value prior to setting new. + /// + public T LastValue; + /// + /// Tick when LastValue was set. + /// + public float UpdateTime; + + public void Update(T prevValue) + { + LastValue = prevValue; + UpdateTime = Time.unscaledTime; + } + } + + /// + /// 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. + /// + /// Value interpolated between last received and current. + /// + /// True if to ignore interpolated calculations and use the current value. + /// This can be useful if you are able to write this SyncVars values in update. + /// + public T InterpolatedValue(bool useCurrentValue = false) + { + if (useCurrentValue) + return _value; + + float diff = (Time.unscaledTime - _interpolator.UpdateTime); + float percent = Mathf.InverseLerp(0f, base.Settings.SendRate, diff); + + return Interpolate(_interpolator.LastValue, _value, percent); + } + + /// + /// Gets and sets the current value for this SyncVar. + /// + public T Value + { + get => _value; + set => SetValue(value, true); + } + ///// + ///// Sets the current value for this SyncVar while sending it immediately. + ///// + //public T ValueRpc + //{ + // set => SetValue(value, true, true); + //} + ///// + ///// Gets the current value for this SyncVar while marking it dirty. This could be useful to change properties or fields on a reference type SyncVar and have the SyncVar be dirtied after. + ///// + //public T ValueDirty + //{ + // get + // { + // base.Dirty(); + // return _value; + // } + //} + ///// + ///// Gets the current value for this SyncVar while sending it imediately. This could be useful to change properties or fields on a reference type SyncVar and have the SyncVar send after. + ///// + //public T ValueDirtyRpc + //{ + // get + // { + // base.Dirty(true); + // return _value; + // } + //} + /// + /// Called when the SyncDictionary changes. + /// + public event OnChanged OnChange; + + public delegate void OnChanged(T prev, T next, bool asServer); + #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; + /// + /// Current value on the server, or client. + /// + [SerializeField] + private T _value; + /// + /// Holds information about interpolating between values. + /// + private InterpolationContainer _interpolator = new(); + /// + /// True if value was ever set after the SyncType initialized. + /// This is true even if SetInitialValues was called at runtime. + /// + private bool _valueSetAfterInitialized; + #endregion + + #region Constructors. + public SyncVar(SyncTypeSettings settings = new()) : this(default(T), settings) { } + public SyncVar(T initialValue, SyncTypeSettings settings = new()) : base(settings) => SetInitialValues(initialValue); + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + _initialValue = _value; + } + + /// + /// Sets initial values. + /// Initial values are not automatically synchronized, as it is assumed clients and server already have them set to the specified value. + /// When a SyncVar is reset, such as when the object despawns, current values are set to initial values. + /// + public void SetInitialValues(T value) + { + _initialValue = value; + /* Only update current if a value has not been set already. + * A value normally would not be set unless a SyncVar came through + * as the object was enabling, such as if it started in a disabled state + * and was later enabled. */ + if (!_valueSetAfterInitialized) + UpdateValues(value); + + if (base.IsInitialized) + _valueSetAfterInitialized = true; + } + + /// + /// Sets current and previous values. + /// + /// + private void UpdateValues(T next) + { + //If network initialized then update interpolator. + if (base.IsNetworkInitialized) + _interpolator.Update(_value); + + _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. + internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false) + { + /* IsInitialized is only set after the script containing this SyncVar + * has executed our codegen in the beginning of awake, and after awake + * user logic. When not set update the initial values */ + if (!base.IsInitialized) + { + SetInitialValues(nextValue); + return; + } + else + { + _valueSetAfterInitialized = true; + } + + /* 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 = CanInvokeCallbackAsServer(); + + /* 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, asServer: true); + } + else + { + if (Comparers.EqualityCompare(_value, nextValue)) + return; + + T prev = _value; + UpdateValues(nextValue); + InvokeOnChange(prev, _value, asServerInvoke); + } + + TryDirty(asServerInvoke); + } + //Not called by user. + else + { + /* Only perform the equality checks when not host. + * + * In the previous SyncVar version it was okay to call + * this on host because a separate clientHost value was kept for + * the client side, and that was compared against. + * + * In newer SyncVar(this one) a client side copy is + * not kept so when compariing against the current vlaue + * as clientHost, it will always return as matched. + * + * But it's impossible for clientHost to send a value + * and it not have changed, so this check is not needed. */ + + // /* 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 = _value; + // if (Comparers.EqualityCompare(prev, nextValue)) + // return; + + T prev = _value; + /* If also server do not update value. + * Server side has say of the current value. */ + /* Only update value if not server. We do not want + * clientHost overwriting servers current with what + * they just received.*/ + if (!base.NetworkManager.IsServerStarted) + UpdateValues(nextValue); + + InvokeOnChange(prev, nextValue, asServer: false); + } + + + /* 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(); + //base.Dirty(sendRpc); + } + } + + /// + /// Returns interpolated values between previous and current using a percentage. + /// + protected virtual T Interpolate(T previous, T current, float percent) + { + base.NetworkManager.LogError($"Type {typeof(T).FullName} does not support interpolation. Implement a supported type class or create your own. See class FloatSyncVar for an example."); + return default; + } + + /// + /// True if callback can be invoked with asServer true. + /// + /// + private bool AsServerInvoke() => (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + /// + /// Dirties the the syncVar for a full send. + /// + public void DirtyAll() + { + if (!base.IsInitialized) + return; + if (!base.CanNetworkSetValues(log: true)) + return; + + //Also set that values have changed since the user is forcing a sync. + _valueSetAfterInitialized = true; + + base.Dirty(); + /* Invoke even if was unable to dirty. Dirtying only + * becomes true if server is running, but also if there are + * observers. Even if there are not observers we still want + * to invoke for the server side. */ + //todo: this behaviour needs to be done for all synctypes with dirt/dirtyall. + bool asServerInvoke = CanInvokeCallbackAsServer(); + InvokeOnChange(_value, _value, asServerInvoke); + } + + /// + /// 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. + [MakePublic] + protected internal 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. + [MakePublic] + protected internal override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.Write(_value); + } + + /// + /// Writes current value if not initialized value. + /// m> + [MakePublic] + protected internal override void WriteFull(PooledWriter obj0) + { + // /* If a class then skip comparer check. + // * InitialValue and Value will be the same reference. + // * + // * If a value then compare field changes, since the references + // * will not be the same. */ + // //Compare if a value type. + // if (_isValueType) + // { + // if (Comparers.EqualityCompare(_initialValue, _value)) + // return; + // } + // else + // { + // if (!_valueSetAfterInitialized) + // return; + // } + + if (!_valueSetAfterInitialized) + return; + + /* SyncVars only hold latest value, so just + * write current delta. */ + WriteDelta(obj0, false); + } + + /// + /// Reads a SyncVar value. + /// + protected internal override void Read(PooledReader reader, bool asServer) + { + T value = reader.Read(); + + if (!ReadChangeId(reader)) + return; + + SetValue(value, false); + //TODO this needs to separate invokes from setting values so that syncvar can be written like remainder of synctypes. + } + + //SyncVars do not use changeId. + [APIExclude] + protected override bool ReadChangeId(Reader reader) => true; + + //SyncVars do not use changeId. + [APIExclude] + protected override void WriteChangeId(PooledWriter writer) { } + + /// + /// Resets to initialized values. + /// + [MakePublic] + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + /* Only full reset under the following conditions: + * asServer is true. + * Is not network initialized. + * asServer is false, and server is not started. */ + if (base.CanReset(asServer)) + { + _value = _initialValue; + _valueSetAfterInitialized = false; + } + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs.meta new file mode 100644 index 0000000..40cc2da --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d8d8d88365cea6445bd5268ac9ed2a86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/Beta/SyncVar.cs + uploadId: 762203 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..20e461c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2024b0be0cd1cc744a442f3e2e6ba483 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/ICustomSync.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs b/Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs new file mode 100644 index 0000000..feee835 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs @@ -0,0 +1,159 @@ +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class FloatSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(float); + protected override float Interpolate(float previous, float current, float percent) => Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class DoubleSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(double); + protected override double Interpolate(double previous, double current, float percent) + { + float a = (float)previous; + float b = (float)current; + return Mathf.Lerp(a, b, percent); + } + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class SbyteSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(sbyte); + protected override sbyte Interpolate(sbyte previous, sbyte current, float percent) => (sbyte)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class ByteSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(byte); + protected override byte Interpolate(byte previous, byte current, float percent) => (byte)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class ShortSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(short); + protected override short Interpolate(short previous, short current, float percent) => (short)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class UShortSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(ushort); + protected override ushort Interpolate(ushort previous, ushort current, float percent) => (ushort)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class IntSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(int); + protected override int Interpolate(int previous, int current, float percent) => (int)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class UIntSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(uint); + protected override uint Interpolate(uint previous, uint current, float percent) => (uint)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class LongSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(long); + protected override long Interpolate(long previous, long current, float percent) => (long)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class ULongSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(ulong); + protected override ulong Interpolate(ulong previous, ulong current, float percent) => (ulong)Mathf.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class Vector2SyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(Vector2); + protected override Vector2 Interpolate(Vector2 previous, Vector2 current, float percent) => Vector2.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class Vector3SyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(Vector3); + protected override Vector3 Interpolate(Vector3 previous, Vector3 current, float percent) => Vector3.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class Vector4SyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(Vector4); + protected override Vector4 Interpolate(Vector4 previous, Vector4 current, float percent) => Vector4.Lerp(previous, current, percent); + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class Vector2IntSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(Vector2); + protected override Vector2Int Interpolate(Vector2Int previous, Vector2Int current, float percent) + { + int x = (int)Mathf.Lerp(previous.x, current.x, percent); + int y = (int)Mathf.Lerp(previous.y, current.y, percent); + return new(x, y); + } + } + /// + /// Implements features specific for a typed SyncVar. + /// + [System.Serializable] + public class Vector3IntSyncVar : SyncVar, ICustomSync + { + public object GetSerializedType() => typeof(Vector3Int); + protected override Vector3Int Interpolate(Vector3Int previous, Vector3Int current, float percent) + { + int x = (int)Mathf.Lerp(previous.x, current.x, percent); + int y = (int)Mathf.Lerp(previous.y, current.y, percent); + int z = (int)Mathf.Lerp(previous.z, current.z, percent); + return new(x, y, z); + } + } + +} + + diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs.meta new file mode 100644 index 0000000..c46dde5 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 727355d27ffb19747a43beb6299f7b98 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/InterpolatedSyncVars.cs + uploadId: 762203 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..c00395f --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3d177496f9519e246b8e3ef199d83437 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/MissingObjectPacketLength.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs new file mode 100644 index 0000000..408c60a --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs @@ -0,0 +1,21 @@ +namespace FishNet.Object.Synchronizing +{ + /// + /// Which clients may receive synchronization updates. + /// //Remove on V5. Just rename file to ReadPermission.cs, do not remove. + public enum ReadPermission : byte + { + /// + /// All observers will receive updates. + /// + Observers = 0, + /// + /// Only owner will receive updates. + /// + OwnerOnly = 1, + /// + /// Send to all observers except owner. + /// + ExcludeOwner = 2, + } +} 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..edeaaf7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8050ef114e01f74409d8e29b821b6fc0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/ReadPermissions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs new file mode 100644 index 0000000..fd9ad3b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs @@ -0,0 +1,552 @@ +using System; +using FishNet.CodeGenerating; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Serializing; +using FishNet.Transporting; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing.Internal +{ + public class SyncBase + { + #region Public. + /// + /// True if this SyncBase has been initialized on its NetworkBehaviour. + /// Being true does not mean that the NetworkBehaviour has been initialized on the network, but rather that this SyncBase has been configured with the basics to be networked. + /// + public bool IsInitialized { get; private set; } + /// + /// True if the object for which this SyncType is for has been initialized for the network. + /// + public bool IsNetworkInitialized => (IsInitialized && (NetworkBehaviour.IsServerStarted || NetworkBehaviour.IsClientStarted)); + /// + /// True if a SyncObject, false if a SyncVar. + /// + public bool IsSyncObject { get; private set; } + /// + /// The settings for this SyncVar. + /// + [MakePublic] + internal SyncTypeSettings Settings; + /// + /// How often updates may send. + /// + [MakePublic] + internal 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; + /// + /// True if the server side has initialized this SyncType. + /// + public bool OnStartServerCalled { get; private set; } + /// + /// True if the client side has initialized this SyncType. + /// + public bool OnStartClientCalled { get; private set; } + /// + /// Next time this SyncType may send data. + /// This is also the next time a client may send to the server when using client-authoritative SyncTypes. + /// + [MakePublic] + internal uint NextSyncTick = 0; + /// + /// Index within the sync collection. + /// + public uint SyncIndex { get; protected set; } = 0; + /// + /// Channel to send on. + /// + internal Channel Channel => _currentChannel; + + /// + /// Sets a new currentChannel. + /// + /// + internal void SetCurrentChannel(Channel channel) => _currentChannel = channel; + #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; + /// + /// Last changerId read from sender. + /// + private ushort _lastReadChangeId = UNSET_CHANGE_ID; + /// + /// Last changeId that was sent to receivers. + /// + private ushort _lastWrittenChangeId = UNSET_CHANGE_ID; + #endregion + + #region Consts. + /// + /// Value to use when readId is unset. + /// + private const ushort UNSET_CHANGE_ID = 0; + /// + /// Maximum value readId can be before resetting to the beginning. + /// + private const ushort MAXIMUM_CHANGE_ID = ushort.MaxValue; + #endregion + + #region Constructors + public SyncBase() : this(new()) { } + + public SyncBase(SyncTypeSettings settings) + { + Settings = settings; + } + #endregion + + /// + /// Updates settings with new values. + /// + public void UpdateSettings(SyncTypeSettings settings) + { + Settings = settings; + SetTimeToTicks(); + } + + /// + /// Updates settings with new values. + /// + public void UpdatePermissions(WritePermission writePermissions, ReadPermission readPermissions) + { + UpdatePermissions(writePermissions); + UpdatePermissions(readPermissions); + } + + /// + /// Updates settings with new values. + /// + public void UpdatePermissions(WritePermission writePermissions) => Settings.WritePermission = writePermissions; + + /// + /// Updates settings with new values. + /// + public void UpdatePermissions(ReadPermission readPermissions) => Settings.ReadPermission = readPermissions; + + /// + /// Updates settings with new values. + /// + public void UpdateSendRate(float sendRate) + { + Settings.SendRate = sendRate; + SetTimeToTicks(); + } + + /// + /// Updates settings with new values. + /// + public void UpdateSettings(Channel channel) + { + CheckChannel(ref channel); + _currentChannel = channel; + } + + /// + /// Updates settings with new values. + /// + public void UpdateSettings(WritePermission writePermissions, ReadPermission readPermissions, float sendRate, Channel channel) + { + CheckChannel(ref channel); + _currentChannel = channel; + Settings = new(writePermissions, readPermissions, sendRate, channel); + SetTimeToTicks(); + } + + /// + /// Checks channel and corrects if not valid. + /// + /// + private void CheckChannel(ref Channel c) + { + if (c == Channel.Unreliable && IsSyncObject) + { + c = Channel.Reliable; + string warning = $"Channel cannot be unreliable for SyncObjects. Channel has been changed to reliable."; + NetworkManager.LogWarning(warning); + } + } + + /// + /// Initializes this SyncBase before user Awake code. + /// + [MakePublic] + internal void InitializeEarly(NetworkBehaviour nb, uint syncIndex, bool isSyncObject) + { + NetworkBehaviour = nb; + SyncIndex = syncIndex; + IsSyncObject = isSyncObject; + + NetworkBehaviour.RegisterSyncType(this, SyncIndex); + } + + /// + /// Called during InitializeLate in NetworkBehaviours to indicate user Awake code has executed. + /// + [MakePublic] + internal void InitializeLate() + { + Initialized(); + } + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected virtual void Initialized() + { + IsInitialized = true; + } + + /// + /// PreInitializes this for use with the network. + /// + [MakePublic] + protected internal void PreInitialize(NetworkManager networkManager, bool asServer) + { + NetworkManager = networkManager; + + if (Settings.IsDefault()) + { + float sendRate = Mathf.Max(networkManager.ServerManager.GetSyncTypeRate(), (float)networkManager.TimeManager.TickDelta); + Settings = new(sendRate); + } + + SetTimeToTicks(); + } + + /// + /// Sets ticks needed to pass for send rate. + /// + private void SetTimeToTicks() + { + if (NetworkManager == null) + return; + _timeToTicks = NetworkManager.TimeManager.TimeToTicks(Settings.SendRate, TickRounding.RoundUp); + } + + /// + /// Called after OnStartXXXX has occurred for the NetworkBehaviour. + /// + /// True if OnStartServer was called, false if OnStartClient. + [MakePublic] + protected internal virtual void OnStartCallback(bool asServer) + { + if (asServer) + OnStartServerCalled = true; + else + OnStartClientCalled = true; + } + + /// + /// Called before OnStopXXXX has occurred for the NetworkBehaviour. + /// + /// True if OnStopServer was called, false if OnStopClient. + [MakePublic] + protected internal virtual void OnStopCallback(bool asServer) + { + if (asServer) + OnStartServerCalled = false; + else + OnStartClientCalled = false; + } + + /// + /// True if can set values and send them over the network. + /// + /// + /// + protected bool CanNetworkSetValues(bool log = 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 (!IsInitialized) + 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.IsServerStarted) + return true; + /* If here then server is not active and additional + * checks must be performed. */ + bool result = (Settings.WritePermission == WritePermission.ClientUnsynchronized) || (Settings.ReadPermission == ReadPermission.ExcludeOwner && NetworkBehaviour.IsOwner); + if (!result && log) + 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. You can disable this warning by setting WritePermissions to {WritePermission.ClientUnsynchronized.ToString()}."); + } + + /// + /// Dirties this Sync and the NetworkBehaviour. + /// + /// True to send current dirtied values immediately as a RPC. When this occurs values will arrive in the order they are sent and interval is ignored. + protected bool Dirty() //bool sendRpc = false) + { + //if (sendRpc) + // NextSyncTick = 0; + /* 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(); + IsDirty |= canDirty; + + return canDirty; + } + + /// + /// Returns if callbacks can be invoked with asServer ture. + /// This is typically used when the value is changing through user code, causing supplier to be unknown. + /// + /// + protected bool CanInvokeCallbackAsServer() => (!IsNetworkInitialized || NetworkBehaviour.IsServerStarted); + + /// + /// Reads a change Id and returns true if the change is new. + /// + /// This method is currently under evaluation and may change at any time. + protected virtual bool ReadChangeId(Reader reader) + { + if (NetworkManager == null) + { + NetworkManager.LogWarning($"NetworkManager is unexpectedly null during a SyncType read."); + return false; + } + + bool rolledOver = reader.ReadBoolean(); + ushort id = reader.ReadUInt16(); + + //Only check lastReadId if its not unset. + if (_lastReadChangeId != UNSET_CHANGE_ID) + { + /* If not rolledOver then Id should always be larger + * than the last read. If it's not then the data is + * old. + * + * If Id is smaller then rolledOver should be normal, + * as rolling over means to restart the Id from the lowest + * value. */ + if (rolledOver) + { + if (id >= _lastReadChangeId) + return false; + } + else + { + if (id <= _lastReadChangeId) + return false; + } + } + + _lastReadChangeId = id; + return true; + } + + /// + /// Writes the readId for a change. + /// + /// This method is currently under evaluation and may change at any time. + protected virtual void WriteChangeId(PooledWriter writer) + { + bool rollOver; + if (_lastWrittenChangeId >= MAXIMUM_CHANGE_ID) + { + rollOver = true; + _lastWrittenChangeId = UNSET_CHANGE_ID; + } + else + { + rollOver = false; + } + + _lastWrittenChangeId++; + writer.WriteBoolean(rollOver); + writer.WriteUInt16(_lastWrittenChangeId); + } + +#if !FISHNET_STABLE_SYNCTYPES + /// + /// Returns true if values are being read as clientHost. + /// + /// True if reading as server. + /// This method is currently under evaluation and may change at any time. + protected bool IsReadAsClientHost(bool asServer) => (!asServer && NetworkManager.IsServerStarted); + + /// + /// Returns true if values are being read as clientHost. + /// + /// True if reading as server. + /// This method is currently under evaluation and may change at any time. + protected bool CanReset(bool asServer) + { + bool clientStarted = (IsNetworkInitialized && NetworkManager.IsClientStarted); + return (asServer && !clientStarted) || (!asServer && NetworkBehaviour.IsDeinitializing); + } +#else + /// + /// Returns true if values are being read as clientHost. + /// + /// True if reading as server. + /// This method is currently under evaluation and may change at any time. + protected bool IsReadAsClientHost(bool asServer) => (!asServer && (NetworkManager != null && NetworkManager.IsServerStarted)); +#endif + + /// + /// Outputs values which may be helpful on how to process a read operation. + /// + /// True if the changeId read is not old data. + /// True if being read as clientHost. + /// True if can modify values from the read, typically when asServer or not asServer and not clientHost. + /// This method is currently under evaluation and may change at any time. + protected void SetReadArguments(PooledReader reader, bool asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues) + { + newChangeId = ReadChangeId(reader); + asClientHost = IsReadAsClientHost(asServer); + canModifyValues = (newChangeId && !asClientHost); + } + + /// + /// 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 IsNextSyncTimeMet(uint tick) => (IsDirty && tick >= NextSyncTick); + + [Obsolete("Use IsNextSyncTimeMet.")] //Remove on V5 + internal bool SyncTimeMet(uint tick) => IsNextSyncTimeMet(tick); + + /// + /// Writes current value. + /// + /// True to set the next time data may sync. + [MakePublic] + protected internal virtual void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + WriteHeader(writer, resetSyncTick); + } + + /// + /// Writes the header for this SyncType. + /// + protected virtual void WriteHeader(PooledWriter writer, bool resetSyncTick = true) + { + if (resetSyncTick) + NextSyncTick = NetworkManager.TimeManager.LocalTick + _timeToTicks; + + writer.WriteUInt8Unpacked((byte)SyncIndex); + WriteChangeId(writer); + } + + /// + /// Indicates that a full write has occurred. + /// This is called from WriteFull, or can be called manually. + /// + [Obsolete("This method no longer functions. You may remove it from your code.")] //Remove on V5. + protected void FullWritten() { } + + /// + /// Writes all values for the SyncType. + /// + [MakePublic] + protected internal virtual void WriteFull(PooledWriter writer) { } + + /// + /// Sets current value as server or client through deserialization. + /// + [MakePublic] + protected internal virtual void Read(PooledReader reader, bool asServer) { } + + /// + /// Resets initialized values for server and client. + /// + protected internal virtual void ResetState() + { + ResetState(true); + ResetState(false); + } + + /// + /// Resets initialized values for server or client. + /// + [MakePublic] + protected internal virtual void ResetState(bool asServer) + { + if (asServer) + { + NextSyncTick = 0; + SetCurrentChannel(Settings.Channel); + IsDirty = false; + } + + /* This only needs to be reset for clients, since + * it only applies to clients. But if the server is resetting + * that means the object is deinitializing, and won't have any + * client observers anyway. Because of this it's safe to reset + * with asServer true, or false. + * + * This change is made to resolve a bug where asServer:false + * sometimes does not invoke when stopping clientHost while not + * also stopping play mode. */ + _lastReadChangeId = UNSET_CHANGE_ID; + } + } +} \ 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..307e434 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1a6f26e3f8016cc499b3fa99e7368fbc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncBase.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs new file mode 100644 index 0000000..d46e2fb --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs @@ -0,0 +1,642 @@ +#if FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + [System.Serializable] + public class SyncDictionary : 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 Dictionary Collection; + /// + /// Copy of objects on client portion when acting as a host. + /// + [HideInInspector] + public Dictionary ClientHostCollection; + /// + /// 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 Dictionary _initialValues = new(); + /// + /// Changed data which will be sent next tick. + /// + private List _changed = new(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges = new(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges = new(); + /// + /// 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 + + #region Constructors. + public SyncDictionary(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveDictionary(), settings) { } + + public SyncDictionary(Dictionary collection, SyncTypeSettings settings = new()) : base(settings) + { + Collection = (collection == null) ? CollectionCaches.RetrieveDictionary() : collection; + ClientHostCollection = CollectionCaches.RetrieveDictionary(); + + _initialValues = CollectionCaches.RetrieveDictionary(); + _changed = CollectionCaches.RetrieveList(); + _serverOnChanges = CollectionCaches.RetrieveList(); + _clientOnChanges = CollectionCaches.RetrieveList(); + //Add to clienthostcollection. + foreach (KeyValuePair item in collection) + ClientHostCollection[item.Key] = item.Value; + } + #endregion + + #region Deconstructor. + ~SyncDictionary() + { + CollectionCaches.StoreAndDefault(ref Collection); + CollectionCaches.StoreAndDefault(ref ClientHostCollection); + CollectionCaches.StoreAndDefault(ref _initialValues); + CollectionCaches.StoreAndDefault(ref _changed); + CollectionCaches.StoreAndDefault(ref _serverOnChanges); + CollectionCaches.StoreAndDefault(ref _clientOnChanges); + } + #endregion + + /// + /// 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.IsServerStarted); + 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 Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_initialValues == null) _initialValues = new(); + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + + 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] + private void AddOperation(SyncDictionaryOperation operation, TKey key, TValue value) + { + if (!base.IsInitialized) + 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.IsServerStarted); + + if (asServerInvoke) + { + _valuesChanged = true; + if (base.Dirty()) + { + ChangeData change = new(operation, key, value); + _changed.Add(change); + } + } + + InvokeOnChange(operation, key, value, asServerInvoke); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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] + protected internal 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.WriteUInt8Unpacked((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] + protected internal 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.WriteUInt8Unpacked((byte)SyncDictionaryOperation.Add); + writer.Write(item.Key); + writer.Write(item.Value); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + //True to warn if this object was deinitialized on the server. + bool deinitialized = (asClientHost && !base.OnStartServerCalled); + if (deinitialized) + base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation."); + + IDictionary collection = (asClientHost) ? 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.ReadUInt8Unpacked(); + 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(); + + if (!deinitialized) + collection[key] = value; + } + //Clear. + else if (operation == SyncDictionaryOperation.Clear) + { + if (!deinitialized) + collection.Clear(); + } + //Remove. + else if (operation == SyncDictionaryOperation.Remove) + { + key = reader.Read(); + + if (!deinitialized) + collection.Remove(key); + } + + if (newChangeId) + InvokeOnChange(operation, key, value, false); + } + + //If changes were made invoke complete after all have been read. + if (newChangeId && 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(operation, key, value)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, key, value, asServer); + else + _clientOnChanges.Add(new(operation, key, value)); + } + } + + /// + /// Resets to initialized values. + /// + [APIExclude] + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + _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. + 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. + 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.IsInitialized) + 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.IsInitialized) + 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.IsInitialized) + 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(); + } +} +#endif \ No newline at end of file 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..61ba74c --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 54751f912587a854cb61ff80a82087bf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionary.cs + uploadId: 762203 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..fc83fc0 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d5d6ed9db47a8224fa9ed4d2ff54586f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncDictionaryOperation.cs + uploadId: 762203 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..2e52bbb --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 914089f5707003340a68fd6cd718e4c4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncHashSetOperation.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs new file mode 100644 index 0000000..378e409 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs @@ -0,0 +1,657 @@ +#if FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + [System.Serializable] + 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 HashSet Collection; + /// + /// Copy of objects on client portion when acting as a host. + /// + [HideInInspector] + public HashSet ClientHostCollection; + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + #endregion + + #region Private. + /// + /// ListCache for comparing. + /// + private static List _cache = new(); + /// + /// Values upon initialization. + /// + private HashSet _initialValues; + /// + /// Changed data which will be sent next tick. + /// + private List _changed; + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges; + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges; + /// + /// Comparer to see if entries change when calling public methods. + /// //Not used right now. + /// + private readonly IEqualityComparer _comparer; + /// + /// 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 + + #region Constructors. + public SyncHashSet(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveHashSet(), EqualityComparer.Default, settings) { } + public SyncHashSet(IEqualityComparer comparer, SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveHashSet(), (comparer == null) ? EqualityComparer.Default : comparer, settings) { } + + public SyncHashSet(HashSet collection, IEqualityComparer comparer = null, SyncTypeSettings settings = new()) : base(settings) + { + _comparer = (comparer == null) ? EqualityComparer.Default : comparer; + Collection = (collection == null) ? CollectionCaches.RetrieveHashSet() : collection; + ClientHostCollection = CollectionCaches.RetrieveHashSet(); + + _initialValues = CollectionCaches.RetrieveHashSet(); + _changed = CollectionCaches.RetrieveList(); + _serverOnChanges = CollectionCaches.RetrieveList(); + _clientOnChanges = CollectionCaches.RetrieveList(); + + //Add each in collection to clienthostcollection. + foreach (T item in collection) + ClientHostCollection.Add(item); + } + #endregion + + #region Deconstructor. + ~SyncHashSet() + { + CollectionCaches.StoreAndDefault(ref Collection); + CollectionCaches.StoreAndDefault(ref ClientHostCollection); + CollectionCaches.StoreAndDefault(ref _initialValues); + CollectionCaches.StoreAndDefault(ref _changed); + CollectionCaches.StoreAndDefault(ref _serverOnChanges); + CollectionCaches.StoreAndDefault(ref _clientOnChanges); + } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_initialValues == null) _initialValues = new(); + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + 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.IsServerStarted); + ISet collection = (asClientAndHost) ? ClientHostCollection : Collection; + return (collection as HashSet); + } + + /// + /// Adds an operation and invokes locally. + /// + private void AddOperation(SyncHashSetOperation operation, T item) + { + if (!base.IsInitialized) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + if (asServerInvoke) + { + _valuesChanged = true; + if (base.Dirty()) + { + ChangeData change = new(operation, item); + _changed.Add(change); + } + } + + InvokeOnChange(operation, item, asServerInvoke); + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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. + protected internal 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.WriteUInt8Unpacked((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. + /// + /// + protected internal 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.WriteUInt8Unpacked((byte)SyncHashSetOperation.Add); + writer.Write(item); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + //True to warn if this object was deinitialized on the server. + bool deinitialized = (asClientHost && !base.OnStartServerCalled); + if (deinitialized) + base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation."); + + ISet collection = (asClientHost) ? 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.ReadUInt8Unpacked(); + T next = default; + + //Add. + if (operation == SyncHashSetOperation.Add) + { + next = reader.Read(); + if (!deinitialized) + collection.Add(next); + } + //Clear. + else if (operation == SyncHashSetOperation.Clear) + { + if (!deinitialized) + collection.Clear(); + } + //Remove. + else if (operation == SyncHashSetOperation.Remove) + { + next = reader.Read(); + if (!deinitialized) + collection.Remove(next); + } + //Updated. + else if (operation == SyncHashSetOperation.Update) + { + next = reader.Read(); + if (!deinitialized) + { + collection.Remove(next); + collection.Add(next); + } + } + + if (newChangeId) + InvokeOnChange(operation, next, false); + } + + //If changes were made invoke complete after all have been read. + if (newChangeId && 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(operation, item)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, item, asServer); + else + _clientOnChanges.Add(new(operation, item)); + } + } + + /// + /// Resets to initialized values. + /// + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + _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.IsInitialized) + 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.IsInitialized) + 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) + { + _cache.AddRange(collection); + + int count = _cache.Count; + for (int i = 0; i < count; i++) + { + T entry = _cache[i]; + if (!other.Contains(entry)) + Remove(entry); + } + } + + _cache.Clear(); + } + + 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); + } + } +} +#endif \ No newline at end of file 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..2a3abe1 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 624322b9d999d4b43a560134460955c6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncHashset.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs new file mode 100644 index 0000000..cc5d220 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs @@ -0,0 +1,775 @@ +#if FISHNET_STABLE_SYNCTYPES +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + [System.Serializable] + 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; + } + } + #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 List Collection; + /// + /// Copy of objects on client portion when acting as a host. + /// + [HideInInspector] + public List ClientHostCollection; + /// + /// Number of objects in the collection. + /// + public int Count => Collection.Count; + #endregion + + #region Private. + /// + /// Values upon initialization. + /// + private List _initialValues; + /// + /// Comparer to see if entries change when calling public methods. + /// + private readonly IEqualityComparer _comparer; + /// + /// Changed data which will be sent next tick. + /// + private List _changed; + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges; + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges; + /// + /// 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 + + #region Constructors. + public SyncList(SyncTypeSettings settings = new()) : this(CollectionCaches.RetrieveList(), EqualityComparer.Default, settings) { } + public SyncList(IEqualityComparer comparer, SyncTypeSettings settings = new()) : this(new(), (comparer == null) ? EqualityComparer.Default : comparer, settings) { } + public SyncList(List collection, IEqualityComparer comparer = null, SyncTypeSettings settings = new()) : base(settings) + { + _comparer = (comparer == null) ? EqualityComparer.Default : comparer; + Collection = (collection == null) ? CollectionCaches.RetrieveList() : collection; + ClientHostCollection = CollectionCaches.RetrieveList(); + + _initialValues = CollectionCaches.RetrieveList(); + _changed = CollectionCaches.RetrieveList(); + _serverOnChanges = CollectionCaches.RetrieveList(); + _clientOnChanges = CollectionCaches.RetrieveList(); + + //Add each in collection to clienthostcollection. + foreach (T item in collection) + ClientHostCollection.Add(item); + } + #endregion + + #region Deconstructor. + ~SyncList() + { + CollectionCaches.StoreAndDefault(ref Collection); + CollectionCaches.StoreAndDefault(ref ClientHostCollection); + CollectionCaches.StoreAndDefault(ref _initialValues); + CollectionCaches.StoreAndDefault(ref _changed); + CollectionCaches.StoreAndDefault(ref _serverOnChanges); + CollectionCaches.StoreAndDefault(ref _clientOnChanges); + } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_initialValues == null) _initialValues = new(); + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + + 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.IsServerStarted); + List collection = (asClientAndHost) ? ClientHostCollection : Collection; + return (collection as List); + } + + /// + /// Adds an operation and invokes locally. + /// + /// + /// + /// + /// + + private void AddOperation(SyncListOperation operation, int index, T prev, T next) + { + if (!base.IsInitialized) + 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.IsServerStarted); + + /* 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(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. + protected internal 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. + protected internal 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); + + //Number of entries expected. + writer.WriteInt32(_changed.Count); + + for (int i = 0; i < _changed.Count; i++) + { + ChangeData change = _changed[i]; + writer.WriteUInt8Unpacked((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. + /// + /// + protected internal 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); + for (int i = 0; i < count; i++) + { + writer.WriteUInt8Unpacked((byte)SyncListOperation.Add); + writer.Write(Collection[i]); + } + } + + /// + /// Reads and sets the current values for server or client. + /// + + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + //True to warn if this object was deinitialized on the server. + bool deinitialized = (asClientHost && !base.OnStartServerCalled); + if (deinitialized) + base.NetworkManager.LogWarning($"SyncType {GetType().Name} received a Read but was deinitialized on the server. Client callback values may be incorrect. This is a ClientHost limitation."); + + List collection = (asClientHost) ? 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.ReadUInt8Unpacked(); + int index = -1; + T prev = default; + T next = default; + + //Add. + if (operation == SyncListOperation.Add) + { + next = reader.Read(); + + if (newChangeId) + { + index = collection.Count; + collection.Add(next); + } + } + //Clear. + else if (operation == SyncListOperation.Clear) + { + if (newChangeId) + collection.Clear(); + } + //Insert. + else if (operation == SyncListOperation.Insert) + { + index = reader.ReadInt32(); + next = reader.Read(); + + if (newChangeId) + collection.Insert(index, next); + } + //RemoveAt. + else if (operation == SyncListOperation.RemoveAt) + { + index = reader.ReadInt32(); + + if (newChangeId) + { + prev = collection[index]; + collection.RemoveAt(index); + } + } + //Set + else if (operation == SyncListOperation.Set) + { + index = reader.ReadInt32(); + next = reader.Read(); + + if (newChangeId) + { + prev = collection[index]; + collection[index] = next; + } + } + + if (newChangeId) + InvokeOnChange(operation, index, prev, next, false); + } + + //If changes were made invoke complete after all have been read. + if (newChangeId && 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(operation, index, prev, next)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, index, prev, next, asServer); + else + _clientOnChanges.Add(new(operation, index, prev, next)); + } + } + + /// + /// Resets to initialized values. + /// + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + _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(); + 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(); + 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.IsInitialized) + 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. + /// 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 IEnumerator GetEnumerator() => Collection.GetEnumerator(); + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + [APIExclude] + IEnumerator IEnumerable.GetEnumerator() => Collection.GetEnumerator(); + + } +} +#endif \ No newline at end of file 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..5eaf0fd --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2f3a4c0d0a34e5142be66143d732c079 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncList.cs + uploadId: 762203 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..8c4e8fa --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4fa53fc807605df4997f0b63a6570bcf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncListOperation.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs new file mode 100644 index 0000000..ada11d7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs @@ -0,0 +1,390 @@ +#if FISHNET_STABLE_SYNCTYPES +using FishNet.CodeGenerating; +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(); + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges = new(); + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges = new(); + #endregion + + #region Constructors + public SyncStopwatch(SyncTypeSettings settings = new()) : base(settings) { } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + } + + /// + /// 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.IsInitialized) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + if (asServerInvoke) + { + if (base.Dirty()) + { + ChangeData change = new(operation, prev); + _changed.Add(change); + } + } + + OnChange?.Invoke(operation, prev, asServerInvoke); + } + + /// + /// Writes all changed values. + /// + ///True to set the next time data may sync. + protected internal 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.WriteUInt8Unpacked((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. + /// + protected internal 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.WriteUInt8Unpacked((byte)SyncStopwatchOperation.Pause); + } + + /// + /// Writers a start with elapsed time. + /// + /// + private void WriteStartStopwatch(Writer w, float elapsed, bool includeOperationByte) + { + if (includeOperationByte) + w.WriteUInt8Unpacked((byte)SyncStopwatchOperation.Start); + + w.WriteSingle(elapsed); + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncStopwatchOperation op = (SyncStopwatchOperation)reader.ReadUInt8Unpacked(); + if (op == SyncStopwatchOperation.Start) + { + float elapsed = reader.ReadSingle(); + + if (canModifyValues) + Elapsed = elapsed; + + if (newChangeId) + InvokeOnChange(op, elapsed, asServer); + } + else if (op == SyncStopwatchOperation.Pause) + { + if (canModifyValues) + Paused = true; + + if (newChangeId) + InvokeOnChange(op, -1f, asServer); + } + else if (op == SyncStopwatchOperation.PauseUpdated) + { + float prev = reader.ReadSingle(); + + if (newChangeId) + Paused = true; + + if (newChangeId) + InvokeOnChange(op, prev, asServer); + } + else if (op == SyncStopwatchOperation.Unpause) + { + if (canModifyValues) + Paused = false; + + if (newChangeId) + InvokeOnChange(op, -1f, asServer); + } + else if (op == SyncStopwatchOperation.Stop) + { + if (canModifyValues) + StopStopwatch_Internal(asServer); + + if (newChangeId) + InvokeOnChange(op, -1f, false); + } + else if (op == SyncStopwatchOperation.StopUpdated) + { + float prev = reader.ReadSingle(); + + if (canModifyValues) + StopStopwatch_Internal(asServer); + + if (newChangeId) + InvokeOnChange(op, prev, asServer); + } + } + + if (newChangeId && changes > 0) + InvokeOnChange(SyncStopwatchOperation.Complete, -1f, asServer); + } + + /// + /// Stops the Stopwatch and resets. + /// + private void StopStopwatch_Internal(bool asServer) + { + 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(operation, prev)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, prev, asServer); + else + _clientOnChanges.Add(new(operation, prev)); + } + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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(); + } + + /// + /// Adds 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; + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs.meta new file mode 100644 index 0000000..276d434 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5b06067fb219c724785a23c4f15e9a2a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatch.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs new file mode 100644 index 0000000..9ebd17b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/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/Object/Synchronizing/SyncStopwatchOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs.meta new file mode 100644 index 0000000..41aedc7 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3ef101e3b1527224ca96ef61c238adbb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncStopwatchOperation.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs new file mode 100644 index 0000000..3e66e11 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs @@ -0,0 +1,462 @@ +#if FISHNET_STABLE_SYNCTYPES +using FishNet.CodeGenerating; +using FishNet.Documenting; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +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(); + + /// + /// Server OnChange events waiting for start callbacks. + /// + private List _serverOnChanges = new(); + + /// + /// Client OnChange events waiting for start callbacks. + /// + private List _clientOnChanges = new(); + + /// + /// Last Time.unscaledTime the timer delta was updated. + /// + private float _updateTime; + #endregion + + #region Constructors + public SyncTimer(SyncTypeSettings settings = new()) : base(settings) { } + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + + //Initialize collections if needed. OdinInspector can cause them to become deinitialized. +#if ODIN_INSPECTOR + if (_changed == null) _changed = new(); + if (_serverOnChanges == null) _serverOnChanges = new(); + if (_clientOnChanges == null) _clientOnChanges = new(); +#endif + } + + /// + /// 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; + SetUpdateTime(); + 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, Remaining, Remaining); + } + + /// + /// 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; + SetUpdateTime(); + AddOperation(SyncTimerOperation.Unpause, Remaining, Remaining); + } + + /// + /// Stops and resets the timer. + /// + public void StopTimer(bool sendRemaining = false) + { + if (Remaining <= 0f) + return; + if (!base.CanNetworkSetValues(log: 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.IsInitialized) + return; + + bool asServerInvoke = (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + if (asServerInvoke) + { + if (base.Dirty()) + { + ChangeData change = new(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. + protected internal 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.WriteUInt8Unpacked((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. + /// + protected internal 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.WriteUInt8Unpacked((byte)SyncTimerOperation.Pause); + } + + /// + /// Writes a StartTimer operation. + /// + /// + /// + private void WriteStartTimer(Writer w, bool includeOperationByte) + { + if (includeOperationByte) + w.WriteUInt8Unpacked((byte)SyncTimerOperation.Start); + w.WriteSingle(Remaining); + w.WriteSingle(Duration); + } + + /// + /// Reads and sets the current values for server or client. + /// + [APIExclude] + protected internal override void Read(PooledReader reader, bool asServer) + { + base.SetReadArguments(reader, asServer, out bool newChangeId, out bool asClientHost, out bool canModifyValues); + + int changes = reader.ReadInt32(); + + for (int i = 0; i < changes; i++) + { + SyncTimerOperation op = (SyncTimerOperation)reader.ReadUInt8Unpacked(); + if (op == SyncTimerOperation.Start) + { + float next = reader.ReadSingle(); + float duration = reader.ReadSingle(); + + if (canModifyValues) + { + Paused = false; + Remaining = next; + Duration = duration; + } + + if (newChangeId) + InvokeOnChange(op, -1f, next, asServer); + } + else if (op == SyncTimerOperation.Pause || op == SyncTimerOperation.PauseUpdated || op == SyncTimerOperation.Unpause) + { + if (canModifyValues) + UpdatePauseState(op); + } + else if (op == SyncTimerOperation.Stop) + { + float prev = Remaining; + + if (canModifyValues) + StopTimer_Internal(asServer); + + if (newChangeId) + InvokeOnChange(op, prev, 0f, false); + } + // + else if (op == SyncTimerOperation.StopUpdated) + { + float prev = Remaining; + float next = reader.ReadSingle(); + + if (canModifyValues) + StopTimer_Internal(asServer); + + if (newChangeId) + 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 = Remaining; + float next; + //If updated time as well. + if (op == SyncTimerOperation.PauseUpdated) + { + next = reader.ReadSingle(); + Remaining = next; + } + else + { + next = Remaining; + } + + Paused = newPauseState; + InvokeOnChange(op, prev, next, asServer); + } + + if (newChangeId && changes > 0) + InvokeOnChange(SyncTimerOperation.Complete, -1f, -1f, false); + } + + /// + /// Stops the timer and resets. + /// + private void StopTimer_Internal(bool asServer) + { + 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(operation, prev, next)); + } + else + { + if (base.NetworkBehaviour.OnStartClientCalled) + OnChange?.Invoke(operation, prev, next, asServer); + else + _clientOnChanges.Add(new(operation, prev, next)); + } + } + + /// + /// Called after OnStartXXXX has occurred. + /// + /// True if OnStartServer was called, false if OnStartClient. + protected internal 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(); + } + + /// + /// Sets updateTime to current values. + /// + private void SetUpdateTime() + { + _updateTime = Time.unscaledTime; + } + + /// + /// Removes time passed from Remaining since the last unscaled time using this method. + /// + public void Update() + { + float delta = (Time.unscaledTime - _updateTime); + Update(delta); + } + + /// + /// Removes delta from Remaining for server and client. + /// This also resets unscaledTime delta for Update(). + /// + /// Value to remove from Remaining. + public void Update(float delta) + { + //Not enabled. + if (Remaining <= 0f) + return; + if (Paused) + return; + + SetUpdateTime(); + 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.IsServerStarted) + OnChange?.Invoke(SyncTimerOperation.Finished, prev, 0f, true); + if (base.NetworkManager.IsClientStarted) + OnChange?.Invoke(SyncTimerOperation.Finished, prev, 0f, false); + } + + /// + /// Return the serialized type. + /// + /// + public object GetSerializedType() => null; + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs.meta new file mode 100644 index 0000000..fe62d89 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6abdbcaa07147024fbe99c2813126910 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncTimer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs @@ -0,0 +1,38 @@ +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, + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs.meta new file mode 100644 index 0000000..c552d05 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c7732c822303d3c4387a4af44467e791 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncTimerOperation.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs new file mode 100644 index 0000000..e8d0be2 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs @@ -0,0 +1,82 @@ +using FishNet.Transporting; + +namespace FishNet.Object.Synchronizing +{ + /// + /// Settings which can be passed into SyncTypes. + /// + [System.Serializable] + public struct SyncTypeSettings + { + public WritePermission WritePermission; + public ReadPermission ReadPermission; + public float SendRate; + public Channel Channel; + + internal bool IsDefault() + { + return (WritePermission == WritePermission.ServerOnly && ReadPermission == ReadPermission.Observers + && SendRate == 0f && Channel == (Channel)0); + } + + //Work around for C# parameterless struct limitation. + public SyncTypeSettings(float sendRate = 0.1f) + { + WritePermission = WritePermission.ServerOnly; + ReadPermission = ReadPermission.Observers; + SendRate = sendRate; + Channel = Channel.Reliable; + } + + public SyncTypeSettings(float sendRate, Channel channel) + { + WritePermission = WritePermission.ServerOnly; + ReadPermission = ReadPermission.Observers; + SendRate = sendRate; + Channel = channel; + } + + public SyncTypeSettings(Channel channel) + { + WritePermission = WritePermission.ServerOnly; + ReadPermission = ReadPermission.Observers; + SendRate = 0.1f; + Channel = channel; + } + + public SyncTypeSettings(WritePermission writePermissions) + { + + WritePermission = writePermissions; + ReadPermission = ReadPermission.Observers; + SendRate = 0.1f; + Channel = Channel.Reliable; + } + + public SyncTypeSettings(ReadPermission readPermissions) + { + WritePermission = WritePermission.ServerOnly; + ReadPermission = readPermissions; + SendRate = 0.1f; + Channel = Channel.Reliable; + } + + public SyncTypeSettings(WritePermission writePermissions, ReadPermission readPermissions) + { + + WritePermission = writePermissions; + ReadPermission = readPermissions; + SendRate = 0.1f; + Channel = Channel.Reliable; + } + + public SyncTypeSettings(WritePermission writePermissions, ReadPermission readPermissions, float sendRate, Channel channel) + { + + WritePermission = writePermissions; + ReadPermission = readPermissions; + SendRate = sendRate; + Channel = channel; + } + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs.meta new file mode 100644 index 0000000..037eb25 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9ae08e01a0057a84b84ed5f228724839 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeSetting.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs new file mode 100644 index 0000000..0f5d43b --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs @@ -0,0 +1,15 @@ +namespace FishNet.Object.Synchronizing +{ + [System.Flags] + internal enum SyncTypeWriteFlag + { + Unset = 0, + IgnoreInterval = 1, + ForceReliable = 2, + } + + internal static class SyncTypeWriteFlagExtensions + { + public static bool FastContains(this SyncTypeWriteFlag whole, SyncTypeWriteFlag part) => (whole & part) == part; + } +} diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs.meta b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs.meta new file mode 100644 index 0000000..ae85706 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3ee97e78eaa779d46a91541c9babe061 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncTypeWriteFlag.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs new file mode 100644 index 0000000..8895863 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs @@ -0,0 +1,463 @@ +#if FISHNET_STABLE_SYNCTYPES +using FishNet.CodeGenerating; +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object.Helping; +using FishNet.Object.Synchronizing.Internal; +using FishNet.Serializing; +using FishNet.Serializing.Helping; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using UnityEngine; + +namespace FishNet.Object.Synchronizing +{ + + internal interface ISyncVar { } + + [APIExclude] + [System.Serializable] + [StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)] + public class SyncVar : SyncBase, ISyncVar + { + #region Types. + public struct InterpolationContainer + { + /// + /// Value prior to setting new. + /// + public T LastValue; + /// + /// Tick when LastValue was set. + /// + public float UpdateTime; + + public void Update(T prevValue) + { + LastValue = prevValue; + UpdateTime = Time.unscaledTime; + } + } + + /// + /// 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. + /// + /// Value interpolated between last received and current. + /// + /// True if to ignore interpolated calculations and use the current value. + /// This can be useful if you are able to write this SyncVars values in update. + /// + public T InterpolatedValue(bool useCurrentValue = false) + { + if (useCurrentValue) + return _value; + + float diff = (Time.unscaledTime - _interpolator.UpdateTime); + float percent = Mathf.InverseLerp(0f, base.Settings.SendRate, diff); + + return Interpolate(_interpolator.LastValue, _value, percent); + } + + /// + /// Gets and sets the current value for this SyncVar. + /// + public T Value + { + get => _value; + set => SetValue(value, true); + } + ///// + ///// Sets the current value for this SyncVar while sending it immediately. + ///// + //public T ValueRpc + //{ + // set => SetValue(value, true, true); + //} + ///// + ///// Gets the current value for this SyncVar while marking it dirty. This could be useful to change properties or fields on a reference type SyncVar and have the SyncVar be dirtied after. + ///// + //public T ValueDirty + //{ + // get + // { + // base.Dirty(); + // return _value; + // } + //} + ///// + ///// Gets the current value for this SyncVar while sending it imediately. This could be useful to change properties or fields on a reference type SyncVar and have the SyncVar send after. + ///// + //public T ValueDirtyRpc + //{ + // get + // { + // base.Dirty(true); + // return _value; + // } + //} + /// + /// Called when the SyncDictionary changes. + /// + public event OnChanged OnChange; + public delegate void OnChanged(T prev, T next, bool asServer); + #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. + /// + [SerializeField] + private T _value; + /// + /// Holds information about interpolating between values. + /// + private InterpolationContainer _interpolator = new(); + /// + /// True if T IsValueType. + /// + private bool _isValueType; + /// + /// True if value was ever set after the SyncType initialized. + /// This is true even if SetInitialValues was called at runtime. + /// + private bool _valueSetAfterInitialized; + #endregion + + #region Constructors. + public SyncVar(SyncTypeSettings settings = new()) : this(default(T), settings) { } + public SyncVar(T initialValue, SyncTypeSettings settings = new()) : base(settings) => SetInitialValues(initialValue); + #endregion + + /// + /// Called when the SyncType has been registered, but not yet initialized over the network. + /// + protected override void Initialized() + { + base.Initialized(); + _isValueType = typeof(T).IsValueType; + _initialValue = _value; + } + + /// + /// Sets initial values. + /// Initial values are not automatically synchronized, as it is assumed clients and server already have them set to the specified value. + /// When a SyncVar is reset, such as when the object despawns, current values are set to initial values. + /// + public void SetInitialValues(T value) + { + _initialValue = value; + UpdateValues(value, true); + + if (base.IsInitialized) + _valueSetAfterInitialized = true; + } + /// + /// Sets current and previous values. + /// + /// + private void UpdateValues(T next, bool updateClient) + { + if (updateClient) + _previousClientValue = next; + + //If network initialized then update interpolator. + if (base.IsNetworkInitialized) + _interpolator.Update(_value); + + _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. + + internal void SetValue(T nextValue, bool calledByUser, bool sendRpc = false) + { + /* IsInitialized is only set after the script containing this SyncVar + * has executed our codegen in the beginning of awake, and after awake + * user logic. When not set update the initial values */ + if (!base.IsInitialized) + { + SetInitialValues(nextValue); + return; + } + else + { + _valueSetAfterInitialized = true; + } + + /* 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 = CanInvokeCallbackAsServer(); + + /* 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, false); + //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; + UpdateValues(nextValue, false); + 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.IsServerStarted) + _previousClientValue = nextValue; + /* If server is not started then update both. */ + else + UpdateValues(nextValue, true); + + 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(); + //base.Dirty(sendRpc); + } + } + + /// + /// Returns interpolated values between previous and current using a percentage. + /// + protected virtual T Interpolate(T previous, T current, float percent) + { + base.NetworkManager.LogError($"Type {typeof(T).FullName} does not support interpolation. Implement a supported type class or create your own. See class FloatSyncVar for an example."); + return default; + } + + /// + /// True if callback can be invoked with asServer true. + /// + /// + private bool AsServerInvoke() => (!base.IsNetworkInitialized || base.NetworkBehaviour.IsServerStarted); + + /// + /// Dirties the the syncVar for a full send. + /// + public void DirtyAll() + { + if (!base.IsInitialized) + return; + if (!base.CanNetworkSetValues(true)) + return; + + base.Dirty(); + /* Invoke even if was unable to dirty. Dirtying only + * becomes true if server is running, but also if there are + * observers. Even if there are not observers we still want + * to invoke for the server side. */ + //todo: this behaviour needs to be done for all synctypes with dirt/dirtyall. + bool asServerInvoke = CanInvokeCallbackAsServer(); + InvokeOnChange(_value, _value, asServerInvoke); + } + + /// + /// 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. + + [MakePublic] + protected internal 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. + [MakePublic] + protected internal override void WriteDelta(PooledWriter writer, bool resetSyncTick = true) + { + base.WriteDelta(writer, resetSyncTick); + writer.Write(_value); + } + + /// + /// Writes current value if not initialized value. + /// m> + [MakePublic] + protected internal override void WriteFull(PooledWriter obj0) + { + /* If a class then skip comparer check. + * InitialValue and Value will be the same reference. + * + * If a value then compare field changes, since the references + * will not be the same. */ + //Compare if a value type. + if (_isValueType) + { + if (Comparers.EqualityCompare(_initialValue, _value)) + return; + } + else + { + if (!_valueSetAfterInitialized) + return; + } + /* SyncVars only hold latest value, so just + * write current delta. */ + WriteDelta(obj0, false); + } + + /// + /// Reads a SyncVar value. + /// + protected internal override void Read(PooledReader reader, bool asServer) + { + T value = reader.Read(); + + if (!ReadChangeId(reader)) + return; + + SetValue(value, false); + //TODO this needs to separate invokes from setting values so that syncvar can be written like remainder of synctypes. + } + + //SyncVars do not use changeId. + [APIExclude] + protected override bool ReadChangeId(Reader reader) => true; + + //SyncVars do not use changeId. + [APIExclude] + protected override void WriteChangeId(PooledWriter writer) { } + + /// + /// Resets to initialized values. + /// + [MakePublic] + protected internal override void ResetState(bool asServer) + { + base.ResetState(asServer); + /* Only full reset under the following conditions: + * asServer is true. + * Is not network initialized. + * asServer is false, and server is not started. */ + bool clientStarted = (base.IsNetworkInitialized && base.NetworkManager.IsClientStarted); + if ((asServer && !clientStarted) || (!asServer && base.NetworkBehaviour.IsDeinitializing)) + { + _value = _initialValue; + _previousClientValue = _initialValue; + _valueSetAfterInitialized = false; + } + } + } +} +#endif \ No newline at end of file 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..254cc78 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f319403eec508734a93d723617ab1136 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/SyncVar.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs new file mode 100644 index 0000000..e192a68 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs @@ -0,0 +1,17 @@ +namespace FishNet.Object.Synchronizing +{ + /// + /// Which clients or server may write updates. + /// + public enum WritePermission : byte + { + /// + /// Only the server can change the value of the SyncType. + /// + ServerOnly = 0, + /// + /// Server and clients can change the value of the SyncType. When changed by client the value is not sent to the server. + /// + ClientUnsynchronized = 1, + } +} \ 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..8c84950 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2696d0da2ff02e8499a8351a3021008f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/Synchronizing/WritePermissions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/TransformProperties.cs b/Assets/FishNet/Runtime/Object/TransformProperties.cs new file mode 100644 index 0000000..6ba199e --- /dev/null +++ b/Assets/FishNet/Runtime/Object/TransformProperties.cs @@ -0,0 +1,221 @@ +using System; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Object +{ + + public static class TransformPropertiesExtensions + { + /// + /// Creates direction between two TransformProperties. + /// + /// Value to divide results by. + /// + public static TransformProperties CreateDirections(this TransformProperties prevProperties, TransformProperties nextProperties, uint divisor = 1) + { + //PROSTART + Vector3 position = (nextProperties.Position - prevProperties.Position) / divisor; + + Quaternion rotation = nextProperties.Rotation.Subtract(prevProperties.Rotation); + //If more than 1 tick span then get a portion of the rotation. + if (divisor > 1) + { + float percent = (1f / (float)divisor); + rotation = Quaternion.Lerp(Quaternion.identity, nextProperties.Rotation, percent); + } + + Vector3 scale = (nextProperties.Scale - prevProperties.Scale) / divisor; + + return new(position, rotation, scale); + } + + /// + /// Sets values of TransformPropertiesCls to a transforms world properties. + /// + public static void SetWorldProperties(this TransformPropertiesCls tp, Transform t) + { + tp.Position = t.position; + tp.Rotation = t.rotation; + tp.LocalScale = t.localScale; + } + + /// + /// Sets values of TransformPropertiesCls to a transforms world properties. + /// + public static void SetWorldProperties(this TransformProperties tp, Transform t) + { + tp.Position = t.position; + tp.Rotation = t.rotation; + tp.Scale = t.localScale; + } + + + + } + + [System.Serializable] + public class TransformPropertiesCls : IResettable + { + public Vector3 Position; + public Quaternion Rotation; + public Vector3 LocalScale; + + public TransformPropertiesCls() { } + + public TransformPropertiesCls(Vector3 position, Quaternion rotation, Vector3 localScale) + { + Position = position; + Rotation = rotation; + LocalScale = localScale; + } + + public void InitializeState() { } + + public void ResetState() + { + Update(Vector3.zero, Quaternion.identity, Vector3.zero); + } + + public void Update(Transform t) + { + Update(t.position, t.rotation, t.localScale); + } + + public void Update(TransformPropertiesCls tp) + { + Update(tp.Position, tp.Rotation, tp.LocalScale); + } + + public void Update(TransformProperties tp) + { + Update(tp.Position, tp.Rotation, tp.Scale); + } + + public void Update(Vector3 position, Quaternion rotation) + { + Update(position, rotation, LocalScale); + } + + public void Update(Vector3 position, Quaternion rotation, Vector3 localScale) + { + Position = position; + Rotation = rotation; + LocalScale = localScale; + } + + /// + /// Returns if this TransformProperties equals anothers values. + /// + public bool ValuesEquals(TransformPropertiesCls properties) + { + return (this.Position == properties.Position && this.Rotation == properties.Rotation && this.LocalScale == properties.LocalScale); + } + + /// + /// Returns this classes values as the struct version of TransformProperties. + /// + /// + public TransformProperties ToStruct() + { + TransformProperties result = new(Position, Rotation, LocalScale); + return result; + } + } + + [System.Serializable] + public struct TransformProperties + { + public Vector3 Position; + public Quaternion Rotation; + [Obsolete("Use Scale.")] //Remove V5 + public Vector3 LocalScale => Scale; + public Vector3 Scale; + /// + /// Becomes true when values are set through update or constructor. + /// + public bool IsValid; + + public TransformProperties(Vector3 position, Quaternion rotation, Vector3 localScale) + { + Position = position; + Rotation = rotation; + Scale = localScale; + IsValid = true; + } + + /// + /// Creates a TransformProperties with default position and rotation, with Vector3.one scale. + /// + public static TransformProperties GetTransformDefault() => new(Vector3.zero, Quaternion.identity, Vector3.one); + + public override string ToString() + { + return $"Position: {Position.ToString()}, Rotation {Rotation.ToString()}, Scale {Scale.ToString()}"; + } + + public TransformProperties(Transform t) : this(t.position, t.rotation, t.localScale) { } + + [Obsolete("Use ResetState.")] + public void Reset() => ResetState(); + + public void ResetState() + { + Update(Vector3.zero, Quaternion.identity, Vector3.zero); + IsValid = false; + } + + public void Update(Transform t) + { + Update(t.position, t.rotation, t.localScale); + } + + public void Update(TransformProperties tp) + { + Update(tp.Position, tp.Rotation, tp.Scale); + } + + public void Update(Vector3 position, Quaternion rotation) + { + Update(position, rotation, Scale); + } + + public void Update(Vector3 position, Quaternion rotation, Vector3 localScale) + { + Position = position; + Rotation = rotation; + Scale = localScale; + IsValid = true; + } + + /// + /// Adds another transformProperties onto this. + /// + /// + public void Add(TransformProperties tp) + { + Position += tp.Position; + Rotation *= tp.Rotation; + Scale += tp.Scale; + } + + /// + /// Subtracts another transformProperties from this. + /// + /// + public void Subtract(TransformProperties tp) + { + Position -= tp.Position; + Rotation *= Quaternion.Inverse(tp.Rotation); + Scale -= tp.Scale; + } + + /// + /// Returns if this TransformProperties equals anothers values. + /// + public bool ValuesEquals(TransformProperties properties) + { + return (this.Position == properties.Position && this.Rotation == properties.Rotation && this.Scale == properties.Scale); + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Object/TransformProperties.cs.meta b/Assets/FishNet/Runtime/Object/TransformProperties.cs.meta new file mode 100644 index 0000000..d323a36 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/TransformProperties.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8e4ce2bc25fe8364d8b443f5ac7591ae +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/TransformProperties.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs b/Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs new file mode 100644 index 0000000..8c2fa58 --- /dev/null +++ b/Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs @@ -0,0 +1,26 @@ +using GameKit.Dependencies.Utilities; + +namespace FishNet.Object +{ + [System.Flags] + public enum TransformPropertiesFlag : uint + { + Unset = 0, + Position = (1 << 0), + Rotation = (1 << 1), + Scale = (1 << 2), + Everything = Enums.SHIFT_EVERYTHING_UINT, + } + + public static class TransformPropertiesOptionExtensions + { + /// + /// Returns if enum contains a value. + /// + /// Value checked against. + /// Value checked if whole contains. + /// + public static bool FastContains(this TransformPropertiesFlag whole, TransformPropertiesFlag part) => (whole & part) == part; + } +} + diff --git a/Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs.meta b/Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs.meta new file mode 100644 index 0000000..529b36d --- /dev/null +++ b/Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 67f9d37271d2b6747a71993367895814 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Object/TransformPropertiesFlag.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing.meta b/Assets/FishNet/Runtime/Observing.meta new file mode 100644 index 0000000..e5b485c --- /dev/null +++ b/Assets/FishNet/Runtime/Observing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4fdbbd392bd572f47bb039e44f2b5e21 +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..94e3318 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b3bebf2a796db5c4c8c98b891492e895 +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..865e1e1 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs @@ -0,0 +1,106 @@ +using FishNet.Connection; +using FishNet.Object; +using FishNet.Observing; +using System; +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. + /// + [Obsolete("Use Get/SetMaximumDistance.")] + public float MaximumDistance + { + get => GetMaximumDistance(); + set => SetMaximumDistance(value); + } + + /// + /// Maximum distance a client must be within this object to see it. + /// + /// + public float GetMaximumDistance() => _maximumDistance; + /// + /// Sets the maximum distance value. + /// + /// New value. + public void SetMaximumDistance(float value) + { + _maximumDistance = value; + _sqrMaximumDistance = (_maximumDistance * _maximumDistance); + + float maxDistanceHide = (_maximumDistance * (1f + _hideDistancePercent)); + _sqrHideMaximumDistance = (maxDistanceHide * maxDistanceHide); + } + + /// + /// 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; + #endregion + + #region Private. + /// + /// MaximumDistance squared for faster checks. + /// + private float _sqrMaximumDistance; + /// + /// Distance to hide object at. + /// + private float _sqrHideMaximumDistance; + #endregion + + private void Awake() + { + SetMaximumDistance(_maximumDistance); + } + + /// + /// 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) + { + //If here then checks are being processed. + notProcessed = false; + + float sqrMaximumDistance = (currentlyAdded) ? _sqrHideMaximumDistance : _sqrMaximumDistance; + 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; + } + + /// + /// Type of condition this is. Certain types are handled different, such as Timed which are checked for changes at timed intervals. + /// + /// + public override ObserverConditionType GetConditionType() => ObserverConditionType.Timed; + } +} \ No newline at end of file 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..cd43d24 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7c3e28fa2e37d1d41b4f63c8a0cc2553 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/DistanceCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition.meta b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition.meta new file mode 100644 index 0000000..184f15c --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8e63bfe98ed333f4d9b0e8c81c98c513 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset new file mode 100644 index 0000000..84bd747 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.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: 731898d70776e2341a64ea1f9494299a, type: 3} + m_Name: GridCondition + m_EditorClassIdentifier: + NetworkObject: {fileID: 0} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset.meta b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset.meta new file mode 100644 index 0000000..59cdfa8 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: cc503f7541ebd424c94541e6a767efee +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.asset + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs new file mode 100644 index 0000000..b1f6d02 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs @@ -0,0 +1,35 @@ +using FishNet.Connection; +using FishNet.Observing; +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 grid accuracy to view the object. + /// + [CreateAssetMenu(menuName = "FishNet/Observers/Grid Condition", fileName = "New Grid Condition")] + public class GridCondition : 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) + { + //If here then checks are being processed. + notProcessed = false; + + return connection.HashGridEntry.NearbyEntries.Contains(base.NetworkObject.HashGridEntry); + } + + /// + /// How a condition is handled. + /// + /// + public override ObserverConditionType GetConditionType() => ObserverConditionType.Timed; + } +} diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs.meta new file mode 100644 index 0000000..d75ee57 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 731898d70776e2341a64ea1f9494299a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/GridCondition/GridCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs new file mode 100644 index 0000000..464ba11 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs @@ -0,0 +1,276 @@ +using FishNet.Managing; +using FishNet.Object; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Component.Observing +{ + public class GridEntry + { + /// + /// Position on the grid. + /// + public Vector2Int Position; + /// + /// This grid entry as well those neighboring it. + /// + public HashSet NearbyEntries; + + public GridEntry() + { + } + + public GridEntry(HashSet nearby) + { + NearbyEntries = nearby; + } + + public void SetValues(Vector2Int position, HashSet nearby) + { + Position = position; + NearbyEntries = nearby; + } + + public void SetValues(HashSet nearby) + { + NearbyEntries = nearby; + } + + public void SetValues(Vector2Int position) + { + Position = position; + } + + public void Reset() + { + Position = Vector2Int.zero; + NearbyEntries.Clear(); + } + } + + public class HashGrid : MonoBehaviour + { + #region Types. + + public enum GridAxes : byte + { + XY = 0, + YZ = 1, + XZ = 2, + } + + #endregion + + #region Internal. + + /// + /// Value for when grid position is not set. + /// + internal static Vector2Int UnsetGridPosition = (Vector2Int.one * int.MaxValue); + /// + /// An empty grid entry. + /// + internal static GridEntry EmptyGridEntry = new(new()); + + #endregion + + #region Serialized. + + /// + /// Axes of world space to base the grid on. + /// + [Tooltip("Axes of world space to base the grid on.")] [SerializeField] + private GridAxes _gridAxes = GridAxes.XY; + /// + /// Accuracy of the grid. Objects will be considered nearby if they are within this number of units. Lower values may be more expensive. + /// + [Tooltip("Accuracy of the grid. Objects will be considered nearby if they are within this number of units. Lower values may be more expensive.")] [Range(1, ushort.MaxValue)] [SerializeField] + private ushort _accuracy = 10; + + #endregion + + /// + /// Half of accuracy. + /// + private int _halfAccuracy; + /// + /// Cache of List. + /// + private Stack> _gridEntryHashSetCache = new(); + /// + /// Cache of GridEntrys. + /// + private Stack _gridEntryCache = new(); + /// + /// All grid entries. + /// + private Dictionary _gridEntries = new(); + /// + /// NetworkManager this is used with. + /// + private NetworkManager _networkManager; + + private void Awake() + { + _networkManager = GetComponentInParent(); + + if (_networkManager == null) + { + _networkManager.LogError($"NetworkManager not found on object or within parent of {gameObject.name}. The {GetType().Name} must be placed on or beneath a NetworkManager."); + return; + } + + //Make sure there is only one per networkmanager. + if (!_networkManager.HasInstance()) + { + _halfAccuracy = Mathf.CeilToInt((float)_accuracy / 2f); + _networkManager.RegisterInstance(this); + } + else + { + Destroy(this); + } + } + + /// + /// Sets out values to be used when creating a new GridEntry. + /// + private void OutputNewGridCollections(out GridEntry gridEntry, out HashSet gridEntries) + { + const int cacheCount = 100; + + + if (!_gridEntryHashSetCache.TryPop(out gridEntries)) + { + BuildGridEntryHashSetCache(); + gridEntries = new(); + } + + if (!_gridEntryCache.TryPop(out gridEntry)) + { + BuildGridEntryCache(); + gridEntry = new(); + } + + void BuildGridEntryHashSetCache() + { + for (int i = 0; i < cacheCount; i++) + _gridEntryHashSetCache.Push(new()); + } + + void BuildGridEntryCache() + { + for (int i = 0; i < cacheCount; i++) + _gridEntryCache.Push(new()); + } + } + + /// + /// Creates a GridEntry for position and inserts it into GridEntries. + /// + private GridEntry CreateGridEntry(Vector2Int position) + { + //Make this into a stack that populates a number of entries when empty. also populate with some in awake. + GridEntry newEntry; + HashSet nearby; + OutputNewGridCollections(out newEntry, out nearby); + newEntry.SetValues(position, nearby); + //Add to grid. + _gridEntries[position] = newEntry; + + //Get neighbors. + int endX = (position.x + 1); + int endY = (position.y + 1); + int iterations = 0; + for (int x = (position.x - 1); x <= endX; x++) + { + for (int y = (position.y - 1); y <= endY; y++) + { + iterations++; + if (_gridEntries.TryGetValue(new(x, y), out GridEntry foundEntry)) + { + nearby.Add(foundEntry); + foundEntry.NearbyEntries.Add(newEntry); + } + } + } + + return newEntry; + } + + /// + /// Gets grid positions and neighbors for a NetworkObject. + /// + internal void GetNearbyHashGridPositions(NetworkObject nob, ref HashSet collection) + { + Vector2Int position = GetHashGridPosition(nob); + //Get neighbors. + int endX = (position.x + 1); + int endY = (position.y + 1); + for (int x = (position.x - 1); x < endX; x++) + { + for (int y = (position.y - 1); y < endY; y++) + collection.Add(new(x, y)); + } + } + + /// + /// Gets the grid position to use for a NetworkObjects current position. + /// + internal Vector2Int GetHashGridPosition(NetworkObject nob) + { + Vector3 position = nob.transform.position; + float fX; + float fY; + if (_gridAxes == GridAxes.XY) + { + fX = position.x; + fY = position.y; + } + else if (_gridAxes == GridAxes.XZ) + { + fX = position.x; + fY = position.z; + } + else if (_gridAxes == GridAxes.YZ) + { + fX = position.y; + fY = position.z; + } + else + { + _networkManager.LogError($"GridAxes of {_gridAxes.ToString()} is not handled."); + return default; + } + + return new( + (int)fX / _halfAccuracy + , (int)fY / _halfAccuracy + ); + } + + + /// + /// Gets a GridEntry for a NetworkObject, creating the entry if needed. + /// + + internal GridEntry GetGridEntry(NetworkObject nob) + { + Vector2Int pos = GetHashGridPosition(nob); + return GetGridEntry(pos); + } + + /// + /// Gets a GridEntry for position, creating the entry if needed. + /// + internal GridEntry GetGridEntry(Vector2Int position) + { + GridEntry result; + if (!_gridEntries.TryGetValue(position, out result)) + result = CreateGridEntry(position); + + return result; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs.meta b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs.meta new file mode 100644 index 0000000..767b854 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0a9fc3aafb02eb74fb571c300f846bf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/GridCondition/HashGrid.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs new file mode 100644 index 0000000..9b09c16 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs @@ -0,0 +1,25 @@ +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); + } + + /// + /// How a condition is handled. + /// + /// + public override ObserverConditionType GetConditionType() => ObserverConditionType.Normal; + } +} 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..2eed2f6 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: aa62c0af0c0a4da46b03309dcd3858c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/HostOnlyCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs new file mode 100644 index 0000000..4a0ab24 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs @@ -0,0 +1,770 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Observing; +using GameKit.Dependencies.Utilities; +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 Types. + /// + /// MatchCondition collections used. + /// + public class ConditionCollections + { + public Dictionary> MatchConnections = new(); + public Dictionary> ConnectionMatches = new(); + public Dictionary> MatchObjects = new(); + public Dictionary> ObjectMatches = new(); + } + #endregion + + #region Private. + /// + /// Collections for each NetworkManager instance. + /// + private static Dictionary _collections = new(); + #endregion + + #region Collections. + /// + /// Stores collections for a manager. + /// + /// + internal static void StoreCollections(NetworkManager manager) + { + ConditionCollections cc; + if (!_collections.TryGetValue(manager, out cc)) + return; + + foreach (HashSet item in cc.ObjectMatches.Values) + CollectionCaches.Store(item); + foreach (HashSet item in cc.MatchConnections.Values) + CollectionCaches.Store(item); + foreach (HashSet item in cc.MatchObjects.Values) + CollectionCaches.Store(item); + foreach (HashSet item in cc.ConnectionMatches.Values) + CollectionCaches.Store(item); + + _collections.Remove(manager); + } + /// + /// Gets condition collections for a NetowrkManager. + /// + private static ConditionCollections GetCollections(NetworkManager manager = null) + { + if (manager == null) + manager = InstanceFinder.NetworkManager; + + ConditionCollections cc; + if (!_collections.TryGetValue(manager, out cc)) + { + cc = new(); + _collections[manager] = cc; + } + + return cc; + } + + /// + /// Returns matches and connections in each match. + /// + /// NetworkManager to use. + /// + public static Dictionary> GetMatchConnections(NetworkManager manager = null) + { + ConditionCollections cc = GetCollections(manager); + return cc.MatchConnections; + } + /// + /// Returns connections and the matches they are in. + /// + /// NetworkManager to use. + /// + public static Dictionary> GetConnectionMatches(NetworkManager manager = null) + { + ConditionCollections cc = GetCollections(manager); + return cc.ConnectionMatches; + } + /// + /// Returns matches and objects within each match. + /// + /// NetworkManager to use. + /// + public static Dictionary> GetMatchObjects(NetworkManager manager = null) + { + ConditionCollections cc = GetCollections(manager); + return cc.MatchObjects; + } + /// + /// Returns objects and the matches they are in. + /// + /// NetworkManager to use. + /// + public static Dictionary> GetObjectMatches(NetworkManager manager = null) + { + ConditionCollections cc = GetCollections(manager); + return cc.ObjectMatches; + } + #endregion + + #region Add to match NetworkConnection. + /// + /// Adds a connection to a match. + /// + private static bool AddToMatch(int match, NetworkConnection conn, NetworkManager manager, bool replaceMatch, bool rebuild) + { + Dictionary> matchConnections = GetMatchConnections(manager); + + if (replaceMatch) + RemoveFromMatchesWithoutRebuild(conn, manager); + + /* Get current connections in match. This is where the conn + * will be added to. If does not exist then make new + * collection. */ + HashSet matchConnValues; + if (!matchConnections.TryGetValueIL2CPP(match, out matchConnValues)) + { + matchConnValues = CollectionCaches.RetrieveHashSet(); + matchConnections.Add(match, matchConnValues); + } + + bool r = matchConnValues.Add(conn); + AddToConnectionMatches(conn, match, manager); + if (r && rebuild) + GetServerObjects(manager).RebuildObservers(); + + return r; + } + + + /// + /// 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) + { + AddToMatch(match, conn, manager, replaceMatch, true); + } + + /// + /// Updates a connection within ConnectionMatches to contain match. + /// + private static void AddToConnectionMatches(NetworkConnection conn, int match, NetworkManager manager) + { + Dictionary> connectionMatches = GetConnectionMatches(manager); + + HashSet matches; + if (!connectionMatches.TryGetValueIL2CPP(conn, out matches)) + { + matches = CollectionCaches.RetrieveHashSet(); + connectionMatches[conn] = matches; + } + + matches.Add(match); + } + + /// + /// 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) + { + bool added = false; + foreach (NetworkConnection c in conns) + added |= AddToMatch(match, c, manager, replaceMatch, false); + + if (added) + GetServerObjects(manager).RebuildObservers(); + } + #endregion + + #region Add to match NetworkObject. + /// + /// Adds an object to a match. + /// + private static bool AddToMatch(int match, NetworkObject nob, NetworkManager manager, bool replaceMatch, bool rebuild) + { + Dictionary> matchObjects = GetMatchObjects(manager); + Dictionary> objectMatches = GetObjectMatches(manager); + + if (replaceMatch) + RemoveFromMatchWithoutRebuild(nob, manager); + + HashSet matchObjectsValues; + if (!matchObjects.TryGetValueIL2CPP(match, out matchObjectsValues)) + { + matchObjectsValues = CollectionCaches.RetrieveHashSet(); + matchObjects.Add(match, matchObjectsValues); + } + bool added = matchObjectsValues.Add(nob); + + /* Also add to reverse dictionary. */ + HashSet objectMatchesValues; + if (!objectMatches.TryGetValueIL2CPP(nob, out objectMatchesValues)) + { + objectMatchesValues = CollectionCaches.RetrieveHashSet(); + objectMatches.Add(nob, objectMatchesValues); + } + objectMatchesValues.Add(match); + + if (added && rebuild) + GetServerObjects(manager).RebuildObservers(); + + return added; + } + /// + /// 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) + { + AddToMatch(match, nob, manager, replaceMatch, true); + } + /// + /// 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) + { + //Remove from current matches. + if (replaceMatch) + { + foreach (NetworkObject n in nobs) + RemoveFromMatchWithoutRebuild(n, manager); + } + + bool added = false; + //Add to matches. + foreach (NetworkObject n in nobs) + added |= AddToMatch(match, n, manager, replaceMatch, false); + + if (added) + GetServerObjects(manager).RebuildObservers(); + } + #endregion + + #region TryRemoveKey. + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, int key, HashSet value) + { + bool isEmpty = true; + if (value != null) + { + isEmpty = (value.Count == 0); + if (isEmpty) + CollectionCaches.Store(value); + } + + if (isEmpty) + dict.Remove(key); + } + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, int key) + { + HashSet value; + dict.TryGetValue(key, out value); + TryRemoveKey(dict, key, value); + } + + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, NetworkObject key, HashSet value) + { + bool isEmpty = true; + if (value != null) + { + isEmpty = (value.Count == 0); + if (isEmpty) + CollectionCaches.Store(value); + } + + if (isEmpty) + dict.Remove(key); + } + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, NetworkObject key) + { + HashSet value; + dict.TryGetValueIL2CPP(key, out value); + TryRemoveKey(dict, key, value); + } + + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, int key, HashSet value) + { + bool isEmpty = true; + if (value != null) + { + isEmpty = (value.Count == 0); + if (isEmpty) + CollectionCaches.Store(value); + } + + if (isEmpty) + dict.Remove(key); + } + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, int key) + { + HashSet value; + dict.TryGetValueIL2CPP(key, out value); + TryRemoveKey(dict, key, value); + } + + /// + /// Removes a key if values are empty, and caches values. + /// + private static void TryRemoveKey(Dictionary> dict, NetworkConnection key, HashSet value) + { + bool isEmpty = true; + if (value != null) + { + isEmpty = (value.Count == 0); + if (isEmpty) + CollectionCaches.Store(value); + } + + if (isEmpty) + dict.Remove(key); + } + /// + /// Removes a key and caches collections where needed. + /// + private static void TryRemoveKey(Dictionary> dict, NetworkConnection key) + { + HashSet value; + dict.TryGetValueIL2CPP(key, out value); + TryRemoveKey(dict, key, value); + } + #endregion + + #region Remove from match NetworkConnection. + /// + /// Removes a connection from all matches without rebuilding observers. + /// + /// Connection to remove from matches. + /// NetworkManager connection belongs to. This is not currently used. + internal static bool RemoveFromMatchesWithoutRebuild(NetworkConnection conn, NetworkManager manager) + { + Dictionary> connectionMatches = GetConnectionMatches(manager); + Dictionary> matchConnections = GetMatchConnections(manager); + + bool removed = false; + //If found to be in a match. + if (connectionMatches.TryGetValueIL2CPP(conn, out HashSet connectionMatchesValues)) + { + removed = (connectionMatchesValues.Count > 0); + foreach (int m in connectionMatchesValues) + { + HashSet matchConnsValues; + //If match is found. + if (matchConnections.TryGetValue(m, out matchConnsValues)) + { + matchConnsValues.Remove(conn); + TryRemoveKey(matchConnections, m, matchConnsValues); + } + } + + //Clear matches connection is in. + connectionMatchesValues.Clear(); + //Remove from connectionMatches. + TryRemoveKey(connectionMatches, conn, connectionMatchesValues); + } + + 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 = RemoveFromMatchesWithoutRebuild(conn, manager); + if (removed) + GetServerObjects(manager).RebuildObservers(); + } + /// + /// Removes a connection from a match. + /// + private static bool RemoveFromMatch(int match, NetworkConnection conn, NetworkManager manager, bool rebuild) + { + Dictionary> connectionMatches = GetConnectionMatches(manager); + Dictionary> matchConnections = GetMatchConnections(manager); + + bool removed = false; + HashSet matchConnsValues; + if (matchConnections.TryGetValueIL2CPP(match, out matchConnsValues)) + { + removed |= matchConnsValues.Remove(conn); + HashSet connectionMatchesValues; + if (connectionMatches.TryGetValueIL2CPP(conn, out connectionMatchesValues)) + { + connectionMatchesValues.Remove(match); + TryRemoveKey(connectionMatches, conn, connectionMatchesValues); + } + if (removed && rebuild) + { + TryRemoveKey(matchConnections, match, matchConnsValues); + GetServerObjects(manager).RebuildObservers(); + } + } + + return removed; + } + /// + /// 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. + + public static bool RemoveFromMatch(int match, NetworkConnection conn, NetworkManager manager = null) + { + return RemoveFromMatch(match, conn, manager, true); + } + /// + /// 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. + + public static void RemoveFromMatch(int match, NetworkConnection[] conns, NetworkManager manager) + { + RemoveFromMatch(match, conns.ToList(), 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. + + public static void RemoveFromMatch(int match, List conns, NetworkManager manager) + { + bool removed = false; + foreach (NetworkConnection c in conns) + removed |= RemoveFromMatch(match, c, manager, false); + + if (removed) + GetServerObjects(manager).RebuildObservers(); + } + #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) + { + Dictionary> objectMatches = GetObjectMatches(manager); + Dictionary> matchObjects = GetMatchObjects(manager); + + HashSet objectMatchesValues; + bool removed = false; + //If found to be in a match. + if (objectMatches.TryGetValueIL2CPP(nob, out objectMatchesValues)) + { + removed = (objectMatchesValues.Count > 0); + foreach (int m in objectMatchesValues) + { + //If match is found. + if (matchObjects.TryGetValue(m, out HashSet matchObjectsValues)) + { + matchObjectsValues.Remove(nob); + TryRemoveKey(matchObjects, m, matchObjectsValues); + } + } + + //Since object is being removed from all matches this can be cleared. + objectMatchesValues.Clear(); + TryRemoveKey(objectMatches, nob, objectMatchesValues); + } + + return removed; + } + /// + /// Removes nob from all matches. + /// + /// NetworkObject to remove. + /// NetworkManager to rebuild observers on. If null InstanceFinder.NetworkManager will be used. + public static bool RemoveFromMatch(NetworkObject nob, NetworkManager manager = null) + { + bool removed = RemoveFromMatchWithoutRebuild(nob, manager); + if (removed) + GetServerObjects(manager).RebuildObservers(nob); + + return removed; + } + /// + /// 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. + + public static void RemoveFromMatch(int match, NetworkObject nob, NetworkManager manager = null) + { + Dictionary> matchObjects = GetMatchObjects(manager); + Dictionary> objectMatches = GetObjectMatches(manager); + + HashSet matchObjectsValues; + if (matchObjects.TryGetValueIL2CPP(match, out matchObjectsValues)) + { + bool removed = matchObjectsValues.Remove(nob); + + if (removed) + { + /* Check if nob is still in matches. If not then remove + * nob from ObjectMatches. */ + HashSet objectMatchesValues; + if (objectMatches.TryGetValueIL2CPP(nob, out objectMatchesValues)) + { + objectMatchesValues.Remove(match); + TryRemoveKey(objectMatches, nob, objectMatchesValues); + } + + TryRemoveKey(matchObjects, match, matchObjectsValues); + GetServerObjects(manager).RebuildObservers(nob); + } + } + } + /// + /// 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. + + public static void RemoveFromMatch(int match, NetworkObject[] nobs, NetworkManager manager = null) + { + Dictionary> matchObjects = GetMatchObjects(manager); + Dictionary> objectMatches = GetObjectMatches(manager); + + if (matchObjects.TryGetValueIL2CPP(match, out HashSet matchObjectsValues)) + { + bool removed = false; + for (int i = 0; i < nobs.Length; i++) + { + NetworkObject n = nobs[i]; + removed |= matchObjectsValues.Remove(n); + objectMatches.Remove(n); + } + + if (removed) + { + TryRemoveKey(matchObjects, match, matchObjectsValues); + GetServerObjects(manager).RebuildObservers(nobs); + } + } + } + /// + /// 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. + + public static void RemoveFromMatch(int match, List nobs, NetworkManager manager = null) + { + Dictionary> matchObjects = GetMatchObjects(manager); + Dictionary> objectMatches = GetObjectMatches(manager); + + if (matchObjects.TryGetValueIL2CPP(match, out HashSet matchObjectsValues)) + { + bool removed = false; + for (int i = 0; i < nobs.Count; i++) + { + NetworkObject n = nobs[i]; + removed |= matchObjectsValues.Remove(n); + objectMatches.Remove(n); + } + + if (removed) + { + TryRemoveKey(matchObjects, match, matchObjectsValues); + 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 currentlyAdded, 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) + { + Dictionary> connectionMatches = GetConnectionMatches(base.NetworkObject.NetworkManager); + //Output owner matches. + HashSet ownerMatches; + /* This objects owner is not in a match so treat it like + * a networkobject without an owner. Objects not in matches + * are visible to everyone. */ + if (!connectionMatches.TryGetValueIL2CPP(owner, out ownerMatches)) + { + return true; + } + /* Owner is in a match. See if connection is in any of + * the same matches. */ + else + { + //If conn is not in any matches then they cannot see this object, as it is. + if (!connectionMatches.TryGetValue(connection, out HashSet connMatches)) + { + return false; + } + //See if conn is in any of the same matches. + else + { + foreach (int m in connMatches) + { + if (ownerMatches.Contains(m)) + return true; + } + } + + //Fall through, not found. + return false; + } + } + /* If no owner see if the object is in a match and if so + * then compare that. */ + else + { + Dictionary> objectMatches = GetObjectMatches(base.NetworkObject.NetworkManager); + Dictionary> connectionMatches = GetConnectionMatches(base.NetworkObject.NetworkManager); + + //Object isn't in a match. Is visible with no owner. + HashSet objectMatchesValues; + if (!objectMatches.TryGetValueIL2CPP(base.NetworkObject, out objectMatchesValues)) + return true; + /* See if connection is in any of same matches as the object. + * If connection isn't in a match then it fails as at this point + * object would be, but not conn. */ + if (!connectionMatches.TryGetValueIL2CPP(connection, out HashSet connectionMatchesValues)) + return false; + + //Compare for same matches. + foreach (int cM in connectionMatchesValues) + { + if (objectMatchesValues.Contains(cM)) + return true; + } + + //Fall through, not in any of the matches. + return false; + } + } + + /// + /// Returns which ServerObjects to rebuild observers on. + /// + /// + /// + private static ServerObjects GetServerObjects(NetworkManager manager) + { + return (manager == null) ? InstanceFinder.ServerManager.Objects : manager.ServerManager.Objects; + } + + /// + /// How a condition is handled. + /// + /// + public override ObserverConditionType GetConditionType() => ObserverConditionType.Normal; + } +} 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..0ea8fe4 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5afdd6c2de1c76f4faa6840cc29fda8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/MatchCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs new file mode 100644 index 0000000..2f84c64 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs @@ -0,0 +1,35 @@ +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; + } + + /// + /// How a condition is handled. + /// + /// + public override ObserverConditionType GetConditionType() => ObserverConditionType.Normal; + + } +} 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..73f90e3 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1ca3d8a36a10fd344806a2df999f3eda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/OwnerOnlyCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs new file mode 100644 index 0000000..9fb992a --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs @@ -0,0 +1,39 @@ +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 + { + /// + /// 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 (base.NetworkObject == null || connection == null) + return false; + /* 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); + } + + /// + /// How a condition is handled. + /// + /// + public override ObserverConditionType GetConditionType() => ObserverConditionType.Normal; + + } +} 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..b26bffb --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fab85d1c51ee2c344b7dd914dc262ec4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/SceneCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta new file mode 100644 index 0000000..4efce87 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1f8371074a46da248ba15efa18140ff3 +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..bfdf72c --- /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..91677e4 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 5f33eb0e5b83b5546822cfe42a305657 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/DistanceCondition.asset + uploadId: 762203 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..9012dbc --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 9ff842b44ec59314d9efcecbcdbaac04 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/HostOnlyCondition.asset + uploadId: 762203 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..58306bc --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: efcd7f0dfd341ed4e8671079e91e0544 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/MatchCondition.asset + uploadId: 762203 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..1321a00 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: bcf92670d91dbb74dad77c56b9b8712e +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/OwnerOnlyCondition.asset + uploadId: 762203 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..08a0c7c --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 2033f54fd2794464bae08fa5a55c8996 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/Conditions/ScriptableObjects/SceneCondition.asset + uploadId: 762203 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..4474d30 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: dc90636a96cf14d47812768a9ce3a4d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/HostVisibilityUpdateTypes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs b/Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs new file mode 100644 index 0000000..e087be5 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs @@ -0,0 +1,620 @@ +// using FishNet.Connection; +// using FishNet.Documenting; +// using FishNet.Managing.Server; +// using FishNet.Object; +// using FishNet.Transporting; +// using FishNet.Utility.Performance; +// using GameKit.Dependencies.Utilities; +// using System.Collections.Generic; +// using System.Runtime.CompilerServices; +// using FishNet.Managing.Observing; +// using UnityEngine; +// using UnityEngine.Serialization; +// +// namespace FishNet.Observing +// { +// /// +// /// Controls which clients can see and get messages for an object. +// /// +// [DisallowMultipleComponent] +// [RequireComponent(typeof(NetworkObject))] +// [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.")] +// [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(); +// /// +// /// 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. +// /// +// /// Becomes true if there are timed conditions. +// /// +// private bool _hasTimedConditions; +// /// +// /// Connections which have all non-timed conditions met. +// /// +// private HashSet _nonTimedMet; +// /// +// /// NetworkObject this belongs to. +// /// +// private NetworkObject _networkObject; +// /// +// /// Becomes true when registered with ServerObjects as Timed observers. +// /// +// private bool _registeredAsTimed; +// /// +// /// True if was initialized previously. +// /// +// private bool _initializedPreviously; +// /// +// /// True if currently initialized. +// /// +// private bool _initialized; +// /// +// /// Last ObserverManager hash which initialized conditions. +// /// +// private uint _initializingHash = ObserverManager.UNSET_INITIALIZING_HASH +// /// +// /// True if ParentNetworkObject was visible last iteration. +// /// This value will also be true if there is no ParentNetworkObject. +// /// +// private bool _lastParentVisible; +// /// +// /// ServerManager for this script. +// /// +// private ServerManager _serverManager; +// /// +// /// Becomes true if there are non-timed, normal conditions. +// /// +// private bool _hasNormalConditions; +// /// +// /// ObserverConditions which are referenced or instantiated from ObserverConditions. +// /// +// private List _runtimeObserverConditions; +// #endregion +// +// /// +// /// Deinitializes for reuse or clean up. +// /// +// /// +// internal void Deinitialize(bool destroyed) +// { +// if (!_initialized) +// return; +// +// Debug.Log($"Deinit called on {GetInstanceID()}. Destroyed? {destroyed}"); +// +// _lastParentVisible = false; +// if (_nonTimedMet != null) +// _nonTimedMet.Clear(); +// UnregisterTimedConditions(); +// +// if (_serverManager != null) +// _serverManager.OnRemoteConnectionState -= ServerManager_OnRemoteConnectionState; +// +// if (_initializedPreviously) +// { +// _hasNormalConditions = false; +// +// foreach (ObserverCondition item in _observerConditions) +// { +// item.Deinitialize(destroyed); +// /* Use GetInstanceId to ensure the object is actually +// * instantiated. If Id is negative, then it's instantiated +// * and not a reference to the original object. */ +// if (destroyed && item.GetInstanceID() < 0) +// Destroy(item); +// } +// +// //Clean up lists. +// if (destroyed) +// CollectionCaches.Store(_nonTimedMet); +// } +// +// _serverManager = null; +// _networkObject = null; +// _initialized = false; +// } +// +// /// +// /// Initializes this script for use. +// /// +// internal void Initialize(NetworkObject networkObject) +// { +// if (_initialized) +// return; +// +// Debug.Log($"Init called on {GetInstanceID()}. Initialized previously? {_initializedPreviously}"); +// _networkObject = networkObject; +// _serverManager = _networkObject.ServerManager; +// _serverManager.OnRemoteConnectionState += ServerManager_OnRemoteConnectionState; +// +// if (!_initializedPreviously) +// { +// _initializedPreviously = true; +// bool ignoringManager = (OverrideType == ConditionOverrideType.IgnoreManager); +// +// //Check to override SetHostVisibility. +// if (!ignoringManager) +// UpdateHostVisibility = networkObject.ObserverManager.UpdateHostVisibility; +// +// /* Sort the conditions so that normal conditions are first. +// * This prevents normal conditions from being skipped if a timed +// * condition fails before the normal passed. +// * +// * Example: Let's say an object has a distance and scene condition, with +// * the distance condition being first. Normal conditions are only checked +// * as the change occurs, such as when the scene was loaded. So if the client +// * loaded into the scene and they were not within the distance the condition +// * iterations would skip remaining, which would be the scene condition. As +// * result normal conditions (non timed) would never be met since they are only +// * checked as-needed, in this case during a scene change. +// * +// * By moving normal conditions to the front they will always be checked first +// * and timed can update at intervals per expectancy. This could also be resolved +// * by simply not exiting early when a condition fails but that's going to +// * cost hotpath performance where sorting is only done once. */ +// +// //Initialize collections. +// _nonTimedMet = CollectionCaches.RetrieveHashSet(); +// //Caches for ordering. +// List nonTimedConditions = CollectionCaches.RetrieveList(); +// List timedConditions = CollectionCaches.RetrieveList(); +// +// bool observerFound = false; +// foreach (ObserverCondition condition in _observerConditions) +// { +// if (condition == null) +// continue; +// +// 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. */ +// ObserverCondition ocCopy = Instantiate(condition); +// +// //Condition type. +// ObserverConditionType oct = ocCopy.GetConditionType(); +// if (oct == ObserverConditionType.Timed) +// { +// timedConditions.AddOrdered(ocCopy); +// } +// else +// { +// _hasNormalConditions = true; +// nonTimedConditions.AddOrdered(ocCopy); +// } +// } +// +// //Add to condition collection as ordered now. +// _observerConditions.Clear(); +// //Non timed. +// for (int i = 0; i < nonTimedConditions.Count; i++) +// _observerConditions.Add(nonTimedConditions[i]); +// +// //Timed. +// _timedConditions = CollectionCaches.RetrieveList(); +// foreach (ObserverCondition timedCondition in timedConditions) +// { +// _observerConditions.Add(timedCondition); +// _timedConditions.Add(timedCondition); +// } +// +// //Store caches. +// CollectionCaches.Store(nonTimedConditions); +// CollectionCaches.Store(timedConditions); +// +// //No observers specified, do not need to take further action. +// if (!observerFound) +// return; +// } +// +// //Initialize conditions. +// for (int i = 0; i < _observerConditions.Count; i++) +// _observerConditions[i].Initialize(_networkObject); +// +// _initialized = true; +// +// 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. +// internal ObserverStateChange RebuildObservers(NetworkConnection connection, bool timedOnly) +// { +// bool currentlyAdded = (_networkObject.Observers.Contains(connection)); +// +// //True if all conditions are met. +// bool allConditionsMet = true; +// /* 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.CurrentParentNetworkBehaviour != null) +// parentVisible = _networkObject.CurrentParentNetworkBehaviour.NetworkObject.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 +// { +// //Only need to check beyond this if conditions exist. +// if (_observerConditions.Count > 0) +// { +// /* True if all conditions are timed or +// * if connection has met non timed. */ +// bool startNonTimedMet = (!_hasNormalConditions || _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 = _runtimeObserverConditions; +// for (int i = 0; i < collection.Count; i++) +// { +// ObserverCondition condition = collection[i]; +// +// if (timedOnly && condition.GetConditionType() != ObserverConditionType.Timed) +// continue; +// /* 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; +// Debug.LogWarning($"Condition check {GetInstanceID()} {condition.GetInstanceID()}. Type {condition.GetType()}"); +// bool conditionMet = (!condition.GetIsEnabled() || condition.ConditionMet(connection, currentlyAdded, out notProcessed)); +// +// if (notProcessed) +// conditionMet = currentlyAdded; +// +// //Condition not met. +// if (!conditionMet) +// { +// allConditionsMet = false; +// if (condition.GetConditionType() != ObserverConditionType.Timed) +// nonTimedMet = false; +// break; +// } +// } +// +// //If nonTimedMet changed. +// if (startNonTimedMet != nonTimedMet) +// { +// /* If the collection was iterated without breaks +// * then add to nontimed met. */ +// if (nonTimedMet) +// _nonTimedMet.Add(connection); +// //If there were breaks not all conditions were checked. +// 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 (!_hasTimedConditions) +// return; +// if (_registeredAsTimed) +// return; +// +// _registeredAsTimed = true; +// +// if (_serverManager == null) +// return; +// _serverManager.Objects.AddTimedNetworkObserver(_networkObject); +// } +// +// /// +// /// Unregisters timed conditions. +// /// +// private void UnregisterTimedConditions() +// { +// if (!_hasTimedConditions) +// return; +// if (!_registeredAsTimed) +// return; +// _registeredAsTimed = false; +// +// if (_serverManager == null) +// return; +// _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); +// } +// +// /// +// /// Updates current conditions to supplied values. Conditions are updated if forced or if the manager hash provided differs from what is stored. +// /// +// internal void SetObserverConditions(List conditions, uint observerManagerHash, bool force = false) +// { +// //Already initialized for the observerManager! +// if (observerManagerHash == _initializingHash && !force) +// return; +// if (_overrideType == ConditionOverrideType.IgnoreManager) +// return; +// +// DisposeOfRuntimeConditions(); +// +// //Caches for ordering. +// List nonTimedConditions = CollectionCaches.RetrieveList(); +// List timedConditions = CollectionCaches.RetrieveList(); +// +// //Set new conditions. +// foreach (ObserverCondition oc in conditions) +// { +// if (oc == null) +// continue; +// +// /* 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. */ +// ObserverCondition ocCopy = (oc.IsConstant) ? oc : Instantiate(oc); +// +// //Condition type. +// ObserverConditionType oct = ocCopy.GetConditionType(); +// if (oct == ObserverConditionType.Timed) +// { +// timedConditions.AddOrdered(ocCopy); +// } +// else +// { +// _hasNormalConditions = true; +// nonTimedConditions.AddOrdered(ocCopy); +// } +// } +// +// CollectionCaches.StoreAndDefault(ref _runtimeObserverConditions); +// _runtimeObserverConditions = CollectionCaches.RetrieveList(); +// +// /* Add nonTimed first, as they're always checked first for performance. */ +// for (int i = 0; i < nonTimedConditions.Count; i++) +// _runtimeObserverConditions.Add(nonTimedConditions[i]); +// +// /* Add timed into their own collection as well runtime collection. +// * There are separate collections as timed are checked regularly so +// * this prevents iterating over all conditions in a timed check. */ +// CollectionCaches.StoreAndDefault(ref _timedConditions); +// _timedConditions = CollectionCaches.RetrieveList(); +// foreach (ObserverCondition timedCondition in timedConditions) +// { +// _observerConditions.Add(timedCondition); +// _timedConditions.Add(timedCondition); +// } +// +// //Store caches. +// CollectionCaches.Store(nonTimedConditions); +// CollectionCaches.Store(timedConditions); +// } +// +// /// +// /// Updates current conditions to supplied values. Conditions are updated if forced or if the manager hash provided differs from what is stored. +// /// +// public void UpdateObserverConditions(List conditions, uint observerManagerHash, bool force = false) +// { +// //Already initialized for the observerManager! +// if (observerManagerHash == _initializingHash && !force) +// return; +// +// //Dispose of current instantiated conditions. +// foreach (ObserverCondition oc in _observerConditions) +// { +// //Constant are never initialized. +// if (oc.IsConstant) +// continue; +// /* Not constant, but isn't in instance. +// * Unity tells us only negative Ids are instantiated. */ +// if (oc.GetInstanceID() >= 0) +// continue; +// +// oc.Deinitialize(destroyed: true); +// Destroy(oc); +// } +// +// _observerConditions.Clear(); +// } +// +// /// +// /// Destroys runtime ObserverConditions as needed and clears the collection. +// /// +// private void DisposeOfRuntimeConditions() +// { +// if (_runtimeObserverConditions == null) +// return; +// +// foreach (ObserverCondition oc in _runtimeObserverConditions) +// { +// //Constant are never initialized. +// if (oc.IsConstant) +// continue; +// /* Not constant, but isn't in instance. +// * Unity tells us only negative Ids are instantiated. */ +// if (oc.GetInstanceID() >= 0) +// continue; +// +// oc.Deinitialize(destroyed: true); +// Destroy(oc); +// } +// +// _runtimeObserverConditions.Clear(); +// } +// +// /// +// /// 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; +// } +// } +// } \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs.meta b/Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs.meta new file mode 100644 index 0000000..4529be3 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c8dfd3072b1d3414d8833438b8c2b258 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/NetworkObserver.Rework.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/NetworkObserver.cs b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs new file mode 100644 index 0000000..082f462 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs @@ -0,0 +1,499 @@ +using FishNet.Connection; +using FishNet.Documenting; +using FishNet.Managing.Server; +using FishNet.Object; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using FishNet.Managing; +using UnityEngine; + +namespace FishNet.Observing +{ + /// + /// Controls which clients can see and get messages for an object. + /// + [DisallowMultipleComponent] + [RequireComponent(typeof(NetworkObject))] + [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 Internal. + /// + /// True if the ObserverManager had already added conditions for this component. + /// + internal bool ConditionsSetByObserverManager; + #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.")] + [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(); + /// + /// 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; + /// + /// Connections which have all non-timed conditions met. + /// + private HashSet _nonTimedMet; + /// + /// NetworkObject this belongs to. + /// + private NetworkObject _networkObject; + /// + /// Becomes true when registered with ServerObjects as Timed observers. + /// + private bool _registeredAsTimed; + /// + /// True if was initialized previously. + /// + private bool _conditionsInitializedPreviously; + /// + /// True if currently initialized. + /// + private bool _initialized; + /// + /// True if ParentNetworkObject was visible last iteration. + /// This value will also be true if there is no ParentNetworkObject. + /// + private bool _lastParentVisible; + /// + /// ServerManager for this script. + /// + private ServerManager _serverManager; + /// + /// Becomes true if there are non-timed, normal conditions. + /// + private bool _hasNormalConditions; + #endregion + + /// + /// Deinitializes for reuse or clean up. + /// + /// + internal void Deinitialize(bool destroyed) + { + _lastParentVisible = false; + if (_nonTimedMet != null) + _nonTimedMet.Clear(); + UnregisterTimedConditions(); + + if (_serverManager != null) + _serverManager.OnRemoteConnectionState -= ServerManager_OnRemoteConnectionState; + + if (_conditionsInitializedPreviously) + { + _hasNormalConditions = false; + + foreach (ObserverCondition item in _observerConditions) + { + item.Deinitialize(destroyed); + /* Use GetInstanceId to ensure the object is actually + * instantiated. If Id is negative, then it's instantiated + * and not a reference to the original object. */ + if (destroyed && item.GetInstanceID() < 0) + Destroy(item); + } + + //Clean up lists. + if (destroyed) + { + _observerConditions.Clear(); + CollectionCaches.Store(_timedConditions); + CollectionCaches.Store(_nonTimedMet); + } + } + + _serverManager = null; + _networkObject = null; + _initialized = false; + } + + /// + /// Initializes this script for use. + /// + internal void Initialize(NetworkObject networkObject) + { + if (_initialized) + return; + + _networkObject = networkObject; + _serverManager = _networkObject.ServerManager; + _serverManager.OnRemoteConnectionState += ServerManager_OnRemoteConnectionState; + + bool observerFound = _conditionsInitializedPreviously; + + if (!_conditionsInitializedPreviously) + { + _conditionsInitializedPreviously = true; + bool ignoringManager = (OverrideType == ConditionOverrideType.IgnoreManager); + + //Check to override SetHostVisibility. + if (!ignoringManager) + UpdateHostVisibility = networkObject.ObserverManager.UpdateHostVisibility; + + /* Sort the conditions so that normal conditions are first. + * This prevents normal conditions from being skipped if a timed + * condition fails before the normal passed. + * + * Example: Let's say an object has a distance and scene condition, with + * the distance condition being first. Normal conditions are only checked + * as the change occurs, such as when the scene was loaded. So if the client + * loaded into the scene and they were not within the distance the condition + * iterations would skip remaining, which would be the scene condition. As + * result normal conditions (non timed) would never be met since they are only + * checked as-needed, in this case during a scene change. + * + * By moving normal conditions to the front they will always be checked first + * and timed can update at intervals per expectancy. This could also be resolved + * by simply not exiting early when a condition fails but that's going to + * cost hotpath performance where sorting is only done once. */ + + //Initialize collections. + _nonTimedMet = CollectionCaches.RetrieveHashSet(); + //Caches for ordering. + List nonTimedConditions = CollectionCaches.RetrieveList(); + List timedConditions = CollectionCaches.RetrieveList(); + + foreach (ObserverCondition condition in _observerConditions) + { + if (condition == null) + continue; + + 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. */ + ObserverCondition ocCopy = Instantiate(condition); + + //Condition type. + ObserverConditionType oct = ocCopy.GetConditionType(); + if (oct == ObserverConditionType.Timed) + { + timedConditions.AddOrdered(ocCopy); + } + else + { + _hasNormalConditions = true; + nonTimedConditions.AddOrdered(ocCopy); + } + } + + //Add to condition collection as ordered now. + _observerConditions.Clear(); + //Non timed. + for (int i = 0; i < nonTimedConditions.Count; i++) + _observerConditions.Add(nonTimedConditions[i]); + + //Timed. + _timedConditions = CollectionCaches.RetrieveList(); + foreach (ObserverCondition timedCondition in timedConditions) + { + _observerConditions.Add(timedCondition); + _timedConditions.Add(timedCondition); + } + + //Store caches. + CollectionCaches.Store(nonTimedConditions); + CollectionCaches.Store(timedConditions); + } + + if (observerFound) + { + //Initialize conditions. + for (int i = 0; i < _observerConditions.Count; i++) + _observerConditions[i].Initialize(_networkObject); + + RegisterTimedConditions(); + } + + _initialized = true; + } + + /// + /// 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. + internal ObserverStateChange RebuildObservers(NetworkConnection connection, bool timedOnly) + { + if (!_initialized) + { + string goName = (gameObject == null) ? "Empty" : gameObject.name; + NetworkManagerExtensions.LogError($"{GetType().Name} is not initialized on NetworkObject [{goName}]. RebuildObservers should not be called. If you are able to reproduce this error consistently please report this issue."); + return ObserverStateChange.Unchanged; + } + + bool currentlyAdded = (_networkObject.Observers.Contains(connection)); + + //True if all conditions are met. + bool allConditionsMet = true; + /* 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.CurrentParentNetworkBehaviour != null) + parentVisible = _networkObject.CurrentParentNetworkBehaviour.NetworkObject.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 + { + //Only need to check beyond this if conditions exist. + if (_observerConditions.Count > 0) + { + /* True if all conditions are timed or + * if connection has met non timed. */ + bool startNonTimedMet = (!_hasNormalConditions || _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.GetConditionType() != ObserverConditionType.Timed) + nonTimedMet = false; + break; + } + } + + //If nonTimedMet changed. + if (startNonTimedMet != nonTimedMet) + { + /* If the collection was iterated without breaks + * then add to nontimed met. */ + if (nonTimedMet) + _nonTimedMet.Add(connection); + //If there were breaks not all conditions were checked. + 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 == null || _timedConditions.Count == 0) + return; + if (_registeredAsTimed) + return; + _registeredAsTimed = true; + + if (_serverManager == null) + return; + _serverManager.Objects.AddTimedNetworkObserver(_networkObject); + } + + /// + /// Unregisters timed conditions. + /// + private void UnregisterTimedConditions() + { + if (_timedConditions == null || _timedConditions.Count == 0) + return; + if (!_registeredAsTimed) + return; + _registeredAsTimed = false; + + if (_serverManager == null) + return; + _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; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta new file mode 100644 index 0000000..6751591 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/NetworkObserver.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c71fd7f855ec523429999fc4e14a1928 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/NetworkObserver.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/ObserverCondition.cs b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs new file mode 100644 index 0000000..3727548 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs @@ -0,0 +1,109 @@ +using FishNet.Connection; +using FishNet.Managing.Server; +using FishNet.Object; +using System; +using GameKit.Dependencies.Utilities.Types; +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, IOrderable + { + #region Public. + /// + /// NetworkObject this condition is for. + /// + [HideInInspector] + public NetworkObject NetworkObject; + #endregion + + #region Serialized. + /// + /// Order in which conditions are added to the NetworkObserver. Lower values will added first, resulting in the condition being checked first. Timed conditions will never check before non-timed conditions. + /// + public int Order => _addOrder; + [Tooltip("Order in which conditions are added to the NetworkObserver. Lower values will added first, resulting in the condition being checked first. Timed conditions will never check before non-timed conditions.")] + [SerializeField] + [Range(sbyte.MinValue, sbyte.MaxValue)] + private sbyte _addOrder; + /// + /// Setting this to true can save performance on conditions which do change settings or store data at runtime. + /// This feature does not function yet, but you may set values now for future implementation. + /// + public bool IsConstant => _isConstant; + [Tooltip("Setting this to true can save performance on conditions which do change settings or store data at runtime. This feature does not function yet but you may set values now for future implementation.")] + [SerializeField] + private bool _isConstant; + #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. + /// + /// NetworkObject this condition is initializing for. + public virtual void Initialize(NetworkObject networkObject) + { + NetworkObject = networkObject; + } + + /// + /// Deinitializes this script. + /// + /// True if the object is being destroyed, false if being despawned. An object may deinitialize for despawn, then destroy after. + public virtual void Deinitialize(bool destroyed) + { + NetworkObject = null; + } + + /// + /// 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); + + /// + /// Type of condition this is. Certain types are handled different, such as Timed which are checked for changes at timed intervals. + /// + /// + public abstract ObserverConditionType GetConditionType(); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta new file mode 100644 index 0000000..b580b9a --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverCondition.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d496d4febcb07f4abbdc081eaa99234 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/ObserverCondition.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Observing/ObserverConditionType.cs b/Assets/FishNet/Runtime/Observing/ObserverConditionType.cs new file mode 100644 index 0000000..fa87da4 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverConditionType.cs @@ -0,0 +1,19 @@ + +namespace FishNet.Observing +{ + /// + /// How a condition is handled. + /// This is intentionally not set as flags. + /// + public enum ObserverConditionType : byte + { + /// + /// Condition is checked only when changed. + /// + Normal = 1, + /// + /// Condition requires checks at regular intervals. The intervals are handled internally. + /// + Timed = 2, + } +} diff --git a/Assets/FishNet/Runtime/Observing/ObserverConditionType.cs.meta b/Assets/FishNet/Runtime/Observing/ObserverConditionType.cs.meta new file mode 100644 index 0000000..ddfc8cf --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverConditionType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a0a36dd46b1f5bd4993d5f5b615bb2dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/ObserverConditionType.cs + uploadId: 762203 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..6de76a9 --- /dev/null +++ b/Assets/FishNet/Runtime/Observing/ObserverStateChange.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a4a8dca28b7d84548a918c5c32f684ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Observing/ObserverStateChange.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins.meta b/Assets/FishNet/Runtime/Plugins.meta new file mode 100644 index 0000000..a1adeb7 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 051bc52e2cc851f49a0c1f2babb04074 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/CodeAnalysis.meta b/Assets/FishNet/Runtime/Plugins/CodeAnalysis.meta new file mode 100644 index 0000000..ff85337 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/CodeAnalysis.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aea01893c4a887048868eaa5b37c656a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll b/Assets/FishNet/Runtime/Plugins/CodeAnalysis/FishNet.CodeAnalysis.Analyzers.dll new file mode 100644 index 0000000000000000000000000000000000000000..c01e33044b2dd2d0ab48c0ca5717a60e789f6dc3 GIT binary patch literal 21504 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 Tw0?X4IsT#k|G)m9v%voXXpu@cn0I_F|h* z^(7B*ocYdwJOBC5fBrdpCNodIKspg6@Z7yibQ3+doWgs-sqv^SI&ukb~({bFus7S+WIfmsJrMU&8;nrkkczEbY)|tjyc&Q%vJC*8T5A+HIMocIrHto$Rd6b{ z90*=$8c$ElA^MCbZ5M`|GFrmJzTSt!d;sosp z^T^}4jQQd1W}irsitHeIj8ihiu4G~E-_*iBMaQ)zJw-1nd`isd@1k`?o1$06ue20B zr=5a!r@~(grvIk!yplYl=wZPgzN_7V{az zfBwNKL?-BRhZ_gi>~N2Q`%H&p-SZ(g@RoiC+}9#*Um^$Y8y(9OV*GZbdxa*zy%KSM zRNSi(m(*GJlg_wlaGZ}@yj=PvXpR0sBO^K;)CXxw7qkPIqP@T&dOvW4#uc7cm{&Nh z@SMU=C^Ug7Y6EwZrRX#~5%RE&E(3Rx34Dmwfur3 zo%7qPc_vSr)tb1l=hmMvK;x0`a-Y{X?e@=)rfAy)aV3H6Ie#ACTsZs$5N#gNGiIGre#ZSN;=X* zI6bO)Pp%=j3%*&G(~fV|WOrDHQrZqjC0S|J>t@C7v7JSf{gS-zBPP3Dwt`;6xi#r| zn1)ltsp6D%U6)M{shGr7KwfdZ)vhA;Hi>myl%9|DZ&vo~jcNZ!tD@^HTlE$a(dQ&g zk`kVmws}=i{^m*MJrq_o=qlo+!!Q3pcNKBW?AdhMUB&s96IczYrAfc^rusJOxwq_<{Or61J@g2ZbGHqH?*$lo$$ z>$}VS%h}R4ljmDvxIxrOYgjwJTJ^nNMDp>n-P0gCL%w=r?)unke<^(D&v%Bdw@-hI z5{3{1x7@-~!LJokz(U>6>Cj|$qGnVl%(Y*EL@ z2cnwz$lCEt79P^OI&V20LC>x7QC!xB^S13?nQ7U9)wCr9gVWB6^emh@Y<70n ze>cNqIXLB1Z19#;7YtxH=PeMM=M=qj_Wxe=-{aZKKXFHoTbl2+M$B#KqdRXXT`am@ z3CFx?In?m0t|z5>?^t{HVff(d+y6*IQDLPI60RYAO>GsSj5f*HjK3W&=ZLcS1&>=+ zKMC?8?kS7F2|5pI8edwo;HQC;;a3;EmiYUf@Fo!NZrFb`a+E+Hz9_M0q7p~}uO@l$ zW8oez;nSgIa+T2$b(=0jYeMQnZ&Cm|7hKqGBwm11ftUhVS&-uZ-sH(vHd(AY{5we% z&H+B6c3^A~{uN(5N6aQXx1nVVZdn{XU7r`#{H%@FJ&y6&&O?Y{FYGyUVXUFY z?n>V7+!;h< + /// Disable this feature. + /// + Disabled, + /// + /// Manually specify the dimensions of a bounding box. + /// + Manual, + } + + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs.meta new file mode 100644 index 0000000..32df90d --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 83b62057280ff8e4faf4a7344b9f3bda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.Types.cs + uploadId: 762203 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..891ec63 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using FishNet.Managing; +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Component.ColliderRollback +{ + public partial class ColliderRollback : NetworkBehaviour + { + #region Serialized. + +#pragma warning disable CS0414 + /// + /// How to configure the bounding box check. + /// + [Tooltip("How to configure the bounding box check.")] [SerializeField] + private BoundingBoxType _boundingBox = BoundingBoxType.Disabled; + /// + /// Physics type to generate a bounding box for. + /// + [Tooltip("Physics type to generate a bounding box for.")] [SerializeField] + private RollbackPhysicsType _physicsType = RollbackPhysicsType.Physics; + /// + /// Size for the bounding box. This is only used when BoundingBox is set to Manual. + /// + [Tooltip("Size for the bounding box.. This is only used when BoundingBox is set to Manual.")] [SerializeField] + private Vector3 _boundingBoxSize = new(3f, 3f, 3f); + /// + /// Objects holding colliders which can rollback. + /// + [Tooltip("Objects holding colliders which can rollback.")] [SerializeField] + private GameObject[] _colliderParents = new GameObject[0]; +#pragma warning restore CS0414 + + #endregion + + + } +} \ No newline at end of file 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..1bd5c40 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 01a271dd97d875347b0cea860df29a9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollback.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs new file mode 100644 index 0000000..3c36606 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs @@ -0,0 +1,59 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; +using static FishNet.Component.ColliderRollback.ColliderRollback; + +namespace FishNet.Component.ColliderRollback +{ + + [CustomEditor(typeof(ColliderRollback), true)] + [CanEditMultipleObjects] + public class ColliderRollbackEditor : Editor + { + private SerializedProperty _boundingBox; + private SerializedProperty _physicsType; + private SerializedProperty _boundingBoxSize; + private SerializedProperty _colliderParents; + + + protected virtual void OnEnable() + { + _boundingBox = serializedObject.FindProperty(nameof(_boundingBox)); + _physicsType = serializedObject.FindProperty(nameof(_physicsType)); + _boundingBoxSize = serializedObject.FindProperty(nameof(_boundingBoxSize)); + _colliderParents = serializedObject.FindProperty(nameof(_colliderParents)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + ColliderRollback nob = (ColliderRollback)target; + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(nob), typeof(ColliderRollback), false); + GUI.enabled = true; + + EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + + EditorGUILayout.PropertyField(_boundingBox); + if ((BoundingBoxType)_boundingBox.intValue != BoundingBoxType.Disabled) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_physicsType); + EditorGUILayout.PropertyField(_boundingBoxSize); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_colliderParents); + + EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + + } + +} + + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs.meta new file mode 100644 index 0000000..350ccbd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b0fd7d7c980dbbc49b3ab071b1974d33 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/ColliderRollbackEditor.cs + uploadId: 762203 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..f6b3ecd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs @@ -0,0 +1,224 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Scened; +using FishNet.Managing.Timing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Component.ColliderRollback +{ + public class RollbackManager : MonoBehaviour + { + #region Internal. + + /// + /// Cached value for bounding box layermask. + /// + internal int? BoundingBoxLayerNumber + { + get + { + if (_boundingBoxLayerNumber == null) + { + for (int i = 0; i < 32; i++) + { + if ((1 << i) == BoundingBoxLayer.value) + { + _boundingBoxLayerNumber = i; + break; + } + } + } + + return _boundingBoxLayerNumber; + } + } + + private int? _boundingBoxLayerNumber; + + #endregion + + #region Serialized. + + /// + /// + /// + [Tooltip("Layer to use when creating and checking against bounding boxes. This should be different from any layer used.")] [SerializeField] + private LayerMask _boundingBoxLayer = 0; + + /// + /// Layer to use when creating and checking against bounding boxes. This should be different from any layer used. + /// + internal LayerMask BoundingBoxLayer => _boundingBoxLayer; + + /// + /// + /// + [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 NetworkTransforms or objects being rolled back.")] [Range(0, 250)] [SerializeField] + internal ushort Interpolation = 2; + + #endregion + + + + + + /// + /// Initializes this script for use. + /// + /// + internal void InitializeOnce_Internal(NetworkManager manager) + { + + } + + + + + + + [Obsolete("Use Rollback(Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5 + public void Rollback(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false) + { + + } + + [Obsolete("Use Rollback(Scene, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5 + + public void Rollback(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false) + { + + } + + [Obsolete("Use Rollback(int, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics, bool) instead.")] //Remove on V5 + + public void Rollback(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false) + { + + } + + + [Obsolete("Use Rollback(Scene, Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics2D, bool) instead.")] //Remove on V5 + + public void Rollback(Scene scene, Vector2 origin, Vector2 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false) + { + + } + + [Obsolete("Use Rollback(Vector3, Vector3, float, PreciseTick, RollbackPhysicsType.Physics2D, bool) instead.")] //Remove on V5 + + public void Rollback(Vector2 origin, Vector2 normalizedDirection, float distance, PreciseTick pt, bool asOwnerAndClientHost = false) + { + + } + + + /// + /// Rolls back all colliders. + /// + /// Precise tick received from the client. + /// Type of physics to rollback; this is often what your casts will use. + /// True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost. + public void Rollback(PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false) + { + + } + + /// + /// Rolls back all colliders in a scene. + /// + /// Scene containing colliders. + /// Precise tick received from the client. + /// Type of physics to rollback; this is often what your casts will use. + /// True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost. + + public void Rollback(Scene scene, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false) + { + + } + + /// + /// Rolls back all colliders in a scene. + /// + /// Scene handle containing colliders. + /// Precise tick received from the client. + /// Type of physics to rollback; this is often what your casts will use. + /// True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost. + public void Rollback(int sceneHandle, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false) + { + + } + + /// + /// Rolls back colliders hit by a test cast against bounding boxes. + /// + /// Ray origin. + /// Direction to cast. + /// Distance of cast. + /// Precise tick received from the client. + /// Type of physics to rollback; this is often what your casts will use. + /// True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost. + public void Rollback(Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false) + { + + } + + /// + /// Rolls back colliders hit by a test cast against bounding boxes, in a specific scene. + /// + /// Scene containing colliders. + /// Ray origin. + /// Direction to cast. + /// Distance of cast. + /// Precise tick received from the client. + /// Type of physics to rollback; this is often what your casts will use. + /// True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost. + public void Rollback(Scene scene, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false) + { + + } + + /// + /// Rolls back colliders hit by a test cast against bounding boxes, in a specific scene. + /// + /// Scene handle containing colliders. + /// Ray origin. + /// Direction to cast. + /// Distance of cast. + /// Precise tick received from the client. + /// Type of physics to rollback; this is often what your casts will use. + /// True if IsOwner of the object the raycast is for. This can be ignored and only provides more accurate results for clientHost. + public void Rollback(int sceneHandle, Vector3 origin, Vector3 normalizedDirection, float distance, PreciseTick pt, RollbackPhysicsType physicsType, bool asOwnerAndClientHost = false) + { + + } + + + + /// + /// Returns all ColliderRollback objects back to their original position. + /// + public void Return() + { + + } + + + } +} \ 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..2fa6d1a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b185516acd802904383e2a5f1a666750 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackManager.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs new file mode 100644 index 0000000..a2b2f28 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs @@ -0,0 +1,13 @@ +namespace FishNet.Component.ColliderRollback +{ + /// + /// Which physics to apply after rolling back colliders. + /// + [System.Serializable][System.Flags] + public enum RollbackPhysicsType + { + Physics = 1, + Physics2D = 2, + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs.meta b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs.meta new file mode 100644 index 0000000..99eba43 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 82b31e74d64a0c44d8fa2f3b6b08ebca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/ColliderRollback/Scripts/RollbackPhysicsType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit.meta b/Assets/FishNet/Runtime/Plugins/GameKit.meta new file mode 100644 index 0000000..6996bec --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a72c0fe8d07e9fd49911db527eddbc39 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies.meta new file mode 100644 index 0000000..70b01e5 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cd9a0ca39fab66c448fdc3e25da9d482 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor.meta new file mode 100644 index 0000000..353e08e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e92e9b5fef66ccb4d991a260767c3be4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs new file mode 100644 index 0000000..28b42f1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs @@ -0,0 +1,43 @@ +using UnityEngine; + +namespace Sirenix.OdinInspector +{ +#if !ODIN_INSPECTOR + + public class TabGroupAttribute : PropertyAttribute + { + public string name; + public bool foldEverything; + + public TabGroupAttribute(string name, bool foldEverything = false) + { + this.foldEverything = foldEverything; + this.name = name; + } + } + + public class ShowIfAttribute : PropertyAttribute + { + #region Fields + public string comparedPropertyName { get; private set; } + public object comparedValue { get; private set; } + public DisablingType disablingType { get; private set; } + + public enum DisablingType + { + ReadOnly = 2, + DontDraw = 3 + } + #endregion + + public ShowIfAttribute(string comparedPropertyName, object comparedValue, DisablingType disablingType = DisablingType.DontDraw) + { + this.comparedPropertyName = comparedPropertyName; + this.comparedValue = comparedValue; + this.disablingType = disablingType; + } + } + +#endif + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs.meta new file mode 100644 index 0000000..8abd7a9 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 8d18dbd89f49c7a4888bbc0e330675a9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Editor/PlaceholderAttributes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef new file mode 100644 index 0000000..438b416 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef @@ -0,0 +1,22 @@ +{ + "name": "GameKit.Dependencies", + "rootNamespace": "", + "references": [ + "GUID:6055be8ebefd69e48b49212b09b47b2f" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [ + { + "name": "com.unity.textmeshpro", + "expression": "", + "define": "TEXTMESHPRO" + } + ], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef.meta new file mode 100644 index 0000000..15eae6a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 1d82bdf40e2465b44b34adf79595e74c +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/GameKit.Dependencies.asmdef + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities.meta new file mode 100644 index 0000000..4f5170e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 53002e457d153bf49aad4b2b28d4353c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs new file mode 100644 index 0000000..664afc3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs @@ -0,0 +1,84 @@ +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + + + +namespace GameKit.Dependencies.Utilities +{ +#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 + } + + + /// + /// Returns if the application is quitting for editor or builds. + /// + /// + public static bool IsQuitting() + { +#if UNITY_EDITOR + if ((!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying) || + !EditorApplication.isPlaying) + return true; + else + return false; +#else + return _isQuitting; +#endif + } + + /// + /// Returns if the application is playing for editor or builds. + /// + /// + public static bool IsPlaying() + { +#if UNITY_EDITOR + return EditorApplication.isPlaying; +#else + return Application.isPlaying; +#endif + } + + /// + /// Quits the application for editor or builds. + /// + public static void Quit() + { +#if UNITY_EDITOR + UnityEditor.EditorApplication.isPlaying = false; +#else + Application.Quit(); +#endif + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs.meta new file mode 100644 index 0000000..57d46ae --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 54eb82a57a65e8548b57f5ca2a62bb76 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ApplicationState.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs new file mode 100644 index 0000000..1a4a4e8 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs @@ -0,0 +1,147 @@ +using System.Collections.Generic; +using System.Text; +using GameKit.Dependencies.Utilities.Types; + +namespace GameKit.Dependencies.Utilities +{ + public static class Arrays + { + /// + /// Randomizer used for shuffling. + /// + private static System.Random _random = new(); + /// + /// StringBuilder to save performance. + /// + private static StringBuilder _stringBuilder = new(); + + /// + /// Adds an entry to a list if it does not exist already. + /// + /// True if being added. + public static bool AddUnique(this List list, T value) + { + bool contains = list.Contains((T)value); + if (!contains) + list.Add((T)value); + + return !contains; + } + + /// + /// Cast each item in the collection ToString and returns all values. + /// + /// + public static string ToString(this IEnumerable collection, string delimeter = ", ") + { + if (collection == null) + return string.Empty; + + _stringBuilder.Clear(); + foreach (T item in collection) + _stringBuilder.Append(item.ToString() + delimeter); + + //Remove ending delimeter. + if (_stringBuilder.Length > delimeter.Length) + _stringBuilder.Length -= delimeter.Length; + + return _stringBuilder.ToString(); + } + + /// + /// Removes an object from a list through re-ordering. This breaks the order of the list for a faster remove. + /// + /// + /// + /// + /// + public static bool FastReferenceRemove(this List list, object value) + { + for (int i = 0; i < list.Count; i++) + { + if (object.ReferenceEquals(list[i], value)) + { + FastIndexRemove(list, i); + return true; + } + } + + return false; + } + + /// + /// Removes an index from a list through re-ordering. This breaks the order of the list for a faster remove. + /// + /// + /// + /// + public static void FastIndexRemove(this List list, int index) + { + list[index] = list[list.Count - 1]; + list.RemoveAt(list.Count - 1); + } + + /// + /// Shuffles an array. + /// + /// + /// + public static void Shuffle(this T[] array) + { + int n = array.Length; + for (int i = 0; i < (n - 1); i++) + { + int r = i + _random.Next(n - i); + T t = array[r]; + array[r] = array[i]; + array[i] = t; + } + } + + /// + /// Shuffles a list. + /// + /// + /// + public static void Shuffle(this List lst) + { + int n = lst.Count; + for (int i = 0; i < (n - 1); i++) + { + int r = i + _random.Next(n - i); + T t = lst[r]; + lst[r] = lst[i]; + lst[i] = t; + } + } + + /// + /// Adds an item to a collection, ordering it's position based on itemOrder. Lower values are inserted near the beginning of the collection. + /// + public static void AddOrdered(this List collection, T item) where T : IOrderable + { + int count = collection.Count; + int itemOrder = item.Order; + + /* If no entries or is equal or larger to last + * entry then value can be added onto the end. */ + if (count == 0 || itemOrder >= collection[^1].Order) + { + collection.Add(item); + } + else + { + for (int i = 0; i < count; i++) + { + /* If item being sorted is lower than the one in already added. + * then insert it before the one already added. */ + if (itemOrder <= collection[i].Order) + { + collection.Insert(i, item); + break; + } + } + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs.meta new file mode 100644 index 0000000..8aa97cb --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 1b93eae9ff81b3e4b892128ca4b392ed +timeCreated: 1530140103 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Arrays.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs new file mode 100644 index 0000000..476b83c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs @@ -0,0 +1,15 @@ + +namespace GameKit.Dependencies.Utilities +{ + public static class Booleans + { + /// + /// Converts a boolean to an integer, 1 for true 0 for false. + /// + public static int ToInt(this bool b) + { + return (b) ? 1 : 0; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs.meta new file mode 100644 index 0000000..c3d664c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 602eeac4b016b174f90ae5e85254ac86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bools.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs new file mode 100644 index 0000000..b506231 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs @@ -0,0 +1,69 @@ + +using System.Runtime.CompilerServices; +using System.Text; + +namespace GameKit.Dependencies.Utilities +{ + + /// + /// Various utility classes relating to floats. + /// + public static class Bytes + { + /// + /// Used to encode and decode strings. + /// + private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + + /// + /// Pads an index a specified value. Preferred over typical padding so that pad values used with skins can be easily found in the code. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Pad(this byte value, int padding) => Ints.PadInt(value, padding); + /// + /// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Inclusive maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte RandomInclusiveRange(byte minimum, byte maximum) => (byte)Ints.RandomInclusiveRange(minimum, maximum); + /// + /// Provides a random exclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Exclusive maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte RandomExclusiveRange(byte minimum, byte maximum) => (byte)Ints.RandomExclusiveRange(minimum, maximum); + + /// + /// Returns a clamped int within a specified range. + /// + /// Value to clamp. + /// Minimum value. + /// Maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Clamp(byte value, byte minimum, byte maximum) => (byte)Ints.Clamp(value, minimum, maximum); + + /// + /// Returns whichever value is lower. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static byte Min(byte a, byte b) => (a < b) ? a : b; + + /// + /// Determins if all values passed in are the same. + /// + /// Values to check. + /// True if all values are the same. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ValuesMatch(params byte[] values) => Ints.ValuesMatch((int[])(object)values); + + /// + /// Converts bytes to a string without error checking. + /// + public static string ToString(this byte[] bytes, int offset, int count) => _encoding.GetString(bytes, offset, count); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs.meta new file mode 100644 index 0000000..a154bf5 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 26489022ceafbfe4d85bfd5cccf37303 +timeCreated: 1527268448 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Bytes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs new file mode 100644 index 0000000..0b8ff2b --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs @@ -0,0 +1,93 @@ +using UnityEngine; + + +namespace GameKit.Dependencies.Utilities +{ + /// + /// Ways a CanvasGroup can have it's blocking properties modified. + /// + public enum CanvasGroupBlockingType + { + Unchanged = 0, + DoNotBlock = 1, + Block = 2, + } + + public static class CanvaseGroups + { + + public static void SetBlockingType(this CanvasGroup group, CanvasGroupBlockingType blockingType) + { + if (blockingType == CanvasGroupBlockingType.Unchanged) + return; + + bool block = (blockingType == CanvasGroupBlockingType.Block); + group.blocksRaycasts = block; + group.interactable = block; + } + + /// + /// Sets a CanvasGroup blocking type and alpha. + /// + /// How to handle interactions. + /// Alpha for CanvasGroup. + public static void SetActive(this CanvasGroup group, CanvasGroupBlockingType blockingType, float alpha) + { + group.SetBlockingType(blockingType); + group.alpha = alpha; + } + + /// + /// Sets a canvasGroup active with specified alpha. + /// + public static void SetActive(this CanvasGroup group, float alpha) + { + group.SetActive(true, false); + group.alpha = alpha; + } + + /// + /// Sets a canvasGroup inactive with specified alpha. + /// + public static void SetInactive(this CanvasGroup group, float alpha) + { + group.SetActive(false, false); + group.alpha = alpha; + } + + /// + /// Sets a group active state by changing alpha and interaction toggles. + /// + public static void SetActive(this CanvasGroup group, bool active, bool setAlpha) + { + if (group == null) + return; + + if (setAlpha) + { + if (active) + group.alpha = 1f; + else + group.alpha = 0f; + } + + group.interactable = active; + group.blocksRaycasts = active; + } + + /// + /// Sets a group active state by changing alpha and interaction toggles with a custom alpha. + /// + public static void SetActive(this CanvasGroup group, bool active, float alpha) + { + if (group == null) + return; + + group.alpha = alpha; + + group.interactable = active; + group.blocksRaycasts = active; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs.meta new file mode 100644 index 0000000..d9ab0af --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c0e7937b287d3d24d807a115c1a3a464 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/CanvasGroups.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs new file mode 100644 index 0000000..f43ef0d --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs @@ -0,0 +1,162 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + public static class ColliderExtensions + { + public static void GetBoxOverlapParams(this BoxCollider boxCollider, out Vector3 center, out Vector3 halfExtents) + { + Transform cachedTransform = boxCollider.transform; + + // DO NOT USE UNITY'S VECTOR OPERATIONS IN HOT PATHS, UNITY DOESN'T OPTIMISE THEM + + center = cachedTransform.TransformPoint(boxCollider.center); + + Vector3 lossyScale = cachedTransform.lossyScale; + + Vector3 size = boxCollider.size; + + float x = size.x * 0.5f * lossyScale.x; + float y = size.y * 0.5f * lossyScale.y; + float z = size.z * 0.5f * lossyScale.z; + + halfExtents = new(x, y, z); + } + + public static void GetCapsuleCastParams(this CapsuleCollider capsuleCollider, out Vector3 point1, out Vector3 point2, out float radius) + { + Transform cachedTransform = capsuleCollider.transform; + + Vector3 lossyScale = cachedTransform.lossyScale; + + // Use System.Math instead of UnityEngine.Mathf because it's much faster. + + float absX = Math.Abs(lossyScale.x); + float absY = Math.Abs(lossyScale.y); + float absZ = Math.Abs(lossyScale.z); + + float height; + + Vector3 direction; + + switch (capsuleCollider.direction) + { + case 1: + { + radius = capsuleCollider.radius * Math.Max(absX, absZ); + + height = capsuleCollider.height * absY; + + direction = Vector3.up; + + break; + } + + case 2: + { + radius = capsuleCollider.radius * Math.Max(absX, absY); + + height = capsuleCollider.height * absZ; + + direction = Vector3.forward; + + break; + } + + default: + { + // Falling back to X is Unity's default behaviour. + + radius = capsuleCollider.radius * Math.Max(absY, absZ); + + height = capsuleCollider.height * absX; + + direction = Vector3.right; + + break; + } + } + + Vector3 center = cachedTransform.TransformPoint(capsuleCollider.center); + + Vector3 offset = height < radius * 2.0f ? Vector3.zero : cachedTransform.TransformDirection(direction * (height * 0.5f - radius)); + + // DO NOT USE UNITY'S VECTOR OPERATIONS IN HOT PATHS, UNITY DOESN'T OPTIMISE THEM + + float x1 = center.x + offset.x; + float y1 = center.y + offset.y; + float z1 = center.z + offset.z; + + float x2 = center.x - offset.x; + float y2 = center.y - offset.y; + float z2 = center.z - offset.z; + + point1 = new(x1, y1, z1); + + point2 = new(x2, y2, z2); + } + + public static void GetSphereOverlapParams(this SphereCollider sphereCollider, out Vector3 center, out float radius) + { + Transform cachedTransform = sphereCollider.transform; + + center = cachedTransform.TransformPoint(sphereCollider.center); + + Vector3 lossyScale = cachedTransform.lossyScale; + + // Use System.Math instead of UnityEngine.Mathf because it's much faster. + + float x = Math.Abs(lossyScale.x); + float y = Math.Abs(lossyScale.y); + float z = Math.Abs(lossyScale.z); + + // Two calls of Math.Max are faster than a single Mathf.Max call because Math.Max doesn't allocate memory and doesn't use loops. + + radius = sphereCollider.radius * Math.Max(Math.Max(x, y), z); + } + } + + + public static class Collider2DExtensions + { + public static void GetBox2DOverlapParams(this BoxCollider2D boxCollider, out Vector3 center, out Vector3 halfExtents) + { + Transform cachedTransform = boxCollider.transform; + + // DO NOT USE UNITY'S VECTOR OPERATIONS IN HOT PATHS, UNITY DOESN'T OPTIMISE THEM + + center = cachedTransform.TransformPoint(boxCollider.offset); + + Vector3 lossyScale = cachedTransform.lossyScale; + + Vector3 size = boxCollider.size; + + float x = size.x * 0.5f * lossyScale.x; + float y = size.y * 0.5f * lossyScale.y; + float z = size.z * 0.5f * lossyScale.z; + + halfExtents = new(x, y, z); + } + + public static void GetCircleOverlapParams(this CircleCollider2D circleCollider, out Vector3 center, out float radius) + { + Transform cachedTransform = circleCollider.transform; + Vector3 offset = new(circleCollider.offset.x, circleCollider.offset.y, circleCollider.transform.position.z); + center = cachedTransform.TransformPoint(offset); + + Vector3 lossyScale = cachedTransform.lossyScale; + + // Use System.Math instead of UnityEngine.Mathf because it's much faster. + + float x = Math.Abs(lossyScale.x); + float y = Math.Abs(lossyScale.y); + float z = Math.Abs(lossyScale.z); + + // Two calls of Math.Max are faster than a single Mathf.Max call because Math.Max doesn't allocate memory and doesn't use loops. + + radius = circleCollider.radius * Math.Max(Math.Max(x, y), z); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta new file mode 100644 index 0000000..b5c4122 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 29e69fa855dd3634d9e66313e7748db4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Colliders.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs new file mode 100644 index 0000000..2b1041c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; + +namespace GameKit.Dependencies.Utilities +{ + 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 + } + + /// + /// Returns values as an allocated list. + /// + /// + public static List ValuesToList(this IDictionary dict) + { + List result = new(dict.Count); + dict.ValuesToList(ref result); + return result; + } + + /// + /// Clears a collection and populates it with this dictionaries values. + /// + public static void ValuesToList(this IDictionary dict, ref List result) + { + result.Clear(); + foreach (TValue item in dict.Values) + result.Add(item); + } + + /// + /// Clears a collection and populates it with this dictionaries keys. + /// + public static void KeysToList(this IDictionary dict, ref List result) + { + result.Clear(); + foreach (TKey item in dict.Keys) + result.Add(item); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs.meta new file mode 100644 index 0000000..e9e8e9f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9d31d19bc39eb6041bad18d8eb68ed68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Dictionaries.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs new file mode 100644 index 0000000..8f2bb43 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs @@ -0,0 +1,102 @@ +using System; +using System.IO; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Disks + { + + + /// + /// Writes specified text to a file path. + /// + /// + /// + /// True to format the path to the current platform. + public static void WriteToFile(string text, string path, bool formatPath = true) + { + //If to format the path for the platform. + if (formatPath) + path = FormatPlatformPath(path); + + //Path came back or was passed in as an empty string. + if (path == string.Empty) + { + Debug.LogError("Path cannot be null."); + return; + } + + try + { + //Get directory path. + string directory = Path.GetDirectoryName(path); + //If directory doesn't exist try to create it. + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + //Try to write the file data. + using (FileStream fs = new(path, FileMode.Create)) + { + using (StreamWriter writer = new(fs)) + writer.Write(text); + } + } + catch (Exception ex) + { + Debug.LogError($"An error occured during a file write. Error: {ex.Message} {Environment.NewLine} File path: {path} {Environment.NewLine} Text: {text}"); + } + + /* If within the editor then refresh the asset database so changes + * reflect in the project folder. */ +#if UNITY_EDITOR + UnityEditor.AssetDatabase.Refresh(); +#endif + } + + /// + /// Formats a file path to the current platform. + /// + /// + /// + public static string FormatPlatformPath(string path) + { + //No path specified. + if (path == string.Empty) + { + Debug.LogError("Path cannot be empty."); + return string.Empty; + } + + string convertedPath = string.Empty; + + //Get the directories as an array. + string[] directories = path.Split(Path.DirectorySeparatorChar); + + //Go through each directory. + for (int i = 0; i < directories.Length; i++) + { + /* If only one entry in array then the path + * is in the root of the Resources folder. */ + if (directories.Length == 1) + { + //Append to converted path and break from the loop. + convertedPath = directories[i]; + break; + } + //More than one entry, meaning there are sub paths. + else + { + /* Set converted path to the current + * convertedPath combined with the next directory. */ + convertedPath = Path.Combine(convertedPath, directories[i]); + } + } + + return convertedPath; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs.meta new file mode 100644 index 0000000..3b62383 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a3a909760282d284591c20c873f20837 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Disks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs new file mode 100644 index 0000000..e2cd596 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs @@ -0,0 +1,12 @@ +// #if UNITY_EDITOR +// using System; +// using UnityEditor; +// using UnityEngine; +// +// namespace GameKit.Dependencies.Utilities +// { +// +// } +// +// #endif +//Remove in V5 \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs.meta new file mode 100644 index 0000000..e7e5eb1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: dd42f76391fc1254f82767dbf1a4bc8b +timeCreated: 1525378031 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Editing.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs new file mode 100644 index 0000000..9b9c697 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs @@ -0,0 +1,294 @@ +#if UNITY_EDITOR +using System; +using UnityEditor; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public enum EditorLayoutEnableType + { + Enabled = 0, + Disabled = 1, + DisabledWhilePlaying = 2 + } + + public static class EditorGuiLayoutTools + { + /// + /// Adds a helpbox field. + /// + public static void AddHelpBox(string text, MessageType messageType = MessageType.Info) + { + EditorGUILayout.HelpBox(text, messageType); + } + + /// + /// Adds a property field. + /// + public static void AddPropertyField(SerializedProperty sp, string fieldName, string tooltip = "") + { + if (tooltip == "") + tooltip = sp.tooltip; + + EditorGUILayout.PropertyField(sp, new GUIContent(fieldName, tooltip)); + } + + /// + /// Adds a property field. + /// + public static void AddPropertyField(SerializedProperty sp, GUIContent guiContent) + { + EditorGUILayout.PropertyField(sp, guiContent); + } + + /// + /// Adds a property field. + /// + public static void AddPropertyField(SerializedProperty sp, GUIContent guiContent = null, EditorLayoutEnableType enableType = EditorLayoutEnableType.Enabled, params GUILayoutOption[] options) + { + bool disable = IsDisableLayoutType(enableType); + if (disable) + GUI.enabled = false; + + EditorGUILayout.PropertyField(sp, guiContent, options); + + if (disable) + GUI.enabled = true; + } + + /// + /// Adds a property field. + /// + /// True to have property enabled. + [Obsolete("Use AddPropertyField(SerializedProperty, GUIContent, EditorLayoutEnableType, GUILayoutOption.")] + public static void AddPropertyField(SerializedProperty sp, GUIContent guiContent = null, bool enabled = true, params GUILayoutOption[] options) + { + EditorLayoutEnableType enableType = (enabled) ? EditorLayoutEnableType.Enabled : EditorLayoutEnableType.Disabled; + bool disable = IsDisableLayoutType(enableType); + if (disable) + GUI.enabled = false; + + EditorGUILayout.PropertyField(sp, guiContent, options); + + if (disable) + GUI.enabled = true; + } + + /// + /// Adds an object field. + /// + public static void AddObjectField(string label, MonoScript ms, System.Type type, bool allowSceneObjects, EditorLayoutEnableType enableType = EditorLayoutEnableType.Enabled, params GUILayoutOption[] options) + { + bool disable = IsDisableLayoutType(enableType); + if (disable) + GUI.enabled = false; + + EditorGUILayout.ObjectField("Script:", ms, type, allowSceneObjects, options); + + if (disable) + GUI.enabled = true; + } + + /// + /// Disables GUI if playing. + /// + public static void DisableGUIIfPlaying() + { + if (Application.isPlaying) + GUI.enabled = false; + } + + /// + /// Enables GUI if playing. + /// + public static void EnableGUIIfPlaying() + { + if (Application.isPlaying) + GUI.enabled = true; + } + + /// + /// Returns if a layout field should be disabled. + /// + /// + /// + private static bool IsDisableLayoutType(EditorLayoutEnableType enableType) + { + return (enableType == EditorLayoutEnableType.Disabled || (enableType == EditorLayoutEnableType.DisabledWhilePlaying && Application.isPlaying)); + } + } + + public static class PropertyDrawerToolExtensions + { + /// + /// Returns GetPropertyHeight value based on drawerTool properties. + /// + public static float GetPropertyHeight(this PropertyDrawerTool drawerTool) + { + if (drawerTool == null) + return EditorGUIUtility.singleLineHeight; + + return (EditorGUIUtility.singleLineHeight * drawerTool.LineSpacingMultiplier * drawerTool.PropertiesDrawn); + } + } + + /// + /// Various utility classes relating to floats. + /// + public class PropertyDrawerTool + { + public PropertyDrawerTool() + { + Debug.LogError($"This initializer is not supported. Use the initializer with arguments."); + } + + public PropertyDrawerTool(Rect position, float lineSpacingMultiplier = 1f) + { + Position = position; + LineSpacingMultiplier = lineSpacingMultiplier; + Position = position; + _startingIndent = EditorGUI.indentLevel; + } + + /// + /// Starting position as indicated by the OnGUI method. + /// + /// This value may be modified by user code. + public Rect Position = default; + /// + /// Preferred spacing between each draw. + /// + public float LineSpacingMultiplier; + /// + /// Number of entries drawn by this object. + /// + public int PropertiesDrawn = 0; + + /// + /// Additional position Y of next draw. + /// + private float _additionalPositionY = 0; + /// + /// Indent level during initialization. + /// + private readonly int _startingIndent; + + /// + /// Sets EditorGUI.Indent to the level it were when initializing this class. + /// + public void SetIndentToStarting() => EditorGUI.indentLevel = _startingIndent; + + /// + /// Draws a label. + /// + public void DrawLabel(GUIContent lLabel) => DrawLabel(lLabel, EditorStyles.label.fontStyle, indent: 0); + + /// + /// Draws a label. + /// + public void DrawLabel(GUIContent lLabel, FontStyle styleOverride) => DrawLabel(lLabel, styleOverride, indent: 0); + + /// + /// Draws a label. + /// + public void DrawLabel(GUIContent lLabel, FontStyle styleOverride, int indent) + { + PropertiesDrawn++; + + if (indent != 0) + EditorGUI.indentLevel += indent; + + //Set style. + FontStyle startingStyle = EditorStyles.label.fontStyle; + EditorStyles.label.fontStyle = styleOverride; + + EditorGUI.PrefixLabel(GetRect(), GUIUtility.GetControlID(FocusType.Passive), lLabel); + + EditorStyles.label.fontStyle = startingStyle; + + if (indent != 0) + EditorGUI.indentLevel -= indent; + } + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop) => DrawProperty(prop, lLabel: "", indent: 0); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, string label) => DrawProperty(prop, new GUIContent(label), EditorStyles.label.fontStyle, indent: 0); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, GUIContent content) => DrawProperty(prop, content, EditorStyles.label.fontStyle, indent: 0); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, int indent) => DrawProperty(prop, lLabel: "", indent); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, string lLabel, int indent) => DrawProperty(prop, lLabel, EditorStyles.label.fontStyle, indent); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, GUIContent content, int indent) => DrawProperty(prop, content, EditorStyles.label.fontStyle, indent); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, GUIContent content, FontStyle labelStyle) => DrawProperty(prop, content, labelStyle, indent: 0); + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, string lLabel, FontStyle labelStyle, int indent) + { + GUIContent content = (lLabel == "") ? default : new GUIContent(lLabel); + + DrawProperty(prop, content, labelStyle, indent); + } + + /// + /// Draws a property. + /// + public void DrawProperty(SerializedProperty prop, GUIContent content, FontStyle labelStyle, int indent) + { + PropertiesDrawn++; + + EditorGUI.indentLevel += indent; + + FontStyle startingStyle = EditorStyles.label.fontStyle; + EditorStyles.label.fontStyle = labelStyle; + + EditorGUI.PropertyField(GetRect(), prop, content); + + EditorStyles.label.fontStyle = startingStyle; + + EditorGUI.indentLevel -= indent; + } + + /// + /// Gets the next Rect to draw at. + /// + /// + public Rect GetRect(float? lineSpacingMultiplierOverride = null) + { + float multiplier = lineSpacingMultiplierOverride ?? LineSpacingMultiplier; + + Rect result = new(Position.x, Position.y + _additionalPositionY, Position.width, EditorGUIUtility.singleLineHeight * multiplier); + + _additionalPositionY += EditorGUIUtility.singleLineHeight * multiplier; + + return result; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs.meta new file mode 100644 index 0000000..8df22f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: dc822ae23a249704184fa571f551f9c8 +timeCreated: 1527268448 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/EditorTools.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs new file mode 100644 index 0000000..3e33c2a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs @@ -0,0 +1,133 @@ +using System; + +namespace GameKit.Dependencies.Utilities +{ + + + public static class Enums + { + public const int SHIFT_EVERYTHING_INT = ~0; + public const uint SHIFT_EVERYTHING_UINT = ~0u; +//65535 + /// + /// Determine an enum value from a given string. This can be an expensive function. + /// + /// + /// Text of string. + /// Default value if enum couldn't be found. + /// Enum found or default value if no enum is found. + public static T FromString(string text, T defaultValue) + { + //If string is empty or null return default value. + if (string.IsNullOrEmpty(text)) + return defaultValue; + //If enum isn't defined return default value. + if (!Enum.IsDefined(typeof(T), (string)text)) + return defaultValue; + //Return parsed value. + return (T)Enum.Parse(typeof(T), text, true); + } + + /// + /// Returns if whole(extended enum) has any of the part values. + /// + /// + /// Values to check for within whole. + /// Returns true part is within whole. + public static bool ContainsAllocated(this Enum whole, Enum part) + { + //If not the same type of Enum return false. + /* Commented out for performance. Designer + * should know better than to compare two different + * enums. */ + //if (!SameType(value, target)) + // return false; + + /* Convert enum values to ulong. With so few + * values a uint would be safe, but should + * the options expand ulong is safer. */ + ulong wholeNum = Convert.ToUInt64(whole); + ulong partNum = Convert.ToUInt64(part); + + return ((wholeNum & partNum) != 0); + } + /// + /// Returns if part values contains any of whole(extended enum). + /// + /// + /// + /// Returns true whole is within part. + public static bool ReverseContains(this Enum whole, Enum part) + { + //If not the same type of Enum return false. + /* Commented out for performance. Designer + * should know better than to compare two different + * enums. */ + //if (!SameType(value, target)) + // return false; + + /* Convert enum values to ulong. With so few + * values a uint would be safe, but should + * the options expand ulong is safer. */ + ulong wholeNum = Convert.ToUInt64(whole); + ulong partNum = Convert.ToUInt64(part); + + return ((partNum & wholeNum) != 0); + } + + /// + /// Returns if an enum equals a specified value. + /// + /// + /// + /// + public static bool Equals(this Enum value, Enum target) + { + //If not the same type of Enum return false. + /* Commented out for performance. Designer + * should know better than to compare two different + * enums. */ + //if (!SameType(value, target)) + // return false; + + ulong valueNum = Convert.ToUInt64(value); + ulong wholeNum = Convert.ToUInt64(target); + + return (valueNum == wholeNum); + } + + /// + /// Returns if a is the same Enum as b. + /// + /// + /// + /// + public static bool SameType(Enum a, Enum b) + { + return (a.GetType() == b.GetType()); + } + + /// + /// 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/Plugins/GameKit/Dependencies/Utilities/Enums.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs.meta new file mode 100644 index 0000000..3565355 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: e6c66aec505f9254491b2b126a2d4745 +timeCreated: 1522959833 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Enums.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs new file mode 100644 index 0000000..418b313 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs @@ -0,0 +1,232 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Floats + { + /// + /// Used to randomize float values. + /// + private static System.Random _random = new(); + + /// + /// Sets a source float to value if equal to or greater than tolerance. + /// + /// Float to check against tolerance. + /// Tolerance float must be equal to or greater than to change to value. + /// Value source is set to when breaking tolerance. + public static float SetIfOverTolerance(this float source, float tolerance, float value) + { + if (source >= tolerance) + source = value; + + return source; + } + + /// + /// Sets a source float to value if equal to or less than tolerance. + /// + /// Float to check against tolerance. + /// Tolerance float must be equal to or less than to change to value. + /// Value source is set to when breaking tolerance. + public static float SetIfUnderTolerance(this float source, float tolerance, float value) + { + if (source <= tolerance) + source = value; + + return source; + } + + /// + /// Returns how much time is left on an endTime. Returns -1 if no time is left. + /// + /// + public static float TimeRemainingValue(this float endTime) + { + float remaining = endTime - Time.time; + //None remaining. + if (remaining < 0f) + return -1f; + + return (endTime - Time.time); + } + + /// + /// Returns how much time is left on an endTime. Returns -1 if no time is left. + /// + /// + public static int TimeRemainingValue(this float endTime, bool useFloor = true) + { + float remaining = endTime - Time.time; + //None remaining. + if (remaining < 0f) + return -1; + + float result = (endTime - Time.time); + return (useFloor) ? Mathf.FloorToInt(result) : Mathf.CeilToInt(result); + } + + + /// + /// Returns time remaining as a string using hh:mm:ss. + /// + /// + /// Number of places to return. 1 is seconds, 2 is minutes, 3 is hours. If a placement does not exist it is replaced with 00. + /// True to return an empty string when value is 0 or less. + /// + public static string TimeRemainingText(this float value, byte segments, bool emptyOnZero = false) + { + if (emptyOnZero && value <= 0f) + return string.Empty; + + int timeRounded = Math.Max(Mathf.RoundToInt(value), 0); + TimeSpan t = TimeSpan.FromSeconds(timeRounded); + + int hours = Mathf.FloorToInt(t.Hours); + int minutes = Mathf.FloorToInt(t.Minutes); + int seconds = Mathf.FloorToInt(t.Seconds); + + string timeText; + if (segments == 1) + { + seconds += (minutes * 60); + seconds += (hours * 3600); + timeText = string.Format("{0:D2}", seconds); + } + else if (segments == 2) + { + minutes += (hours * 60); + timeText = string.Format("{0:D2}:{1:D2}", minutes, seconds); + } + else + { + timeText = string.Format("{0:D2}:{1:D2}:{2:D2}", hours, minutes, seconds); + } + + return timeText; + } + + /// + /// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Inclusive maximum value. + /// + public static float RandomInclusiveRange(float minimum, float maximum) + { + double min = Convert.ToDouble(minimum); + double max = Convert.ToDouble(maximum); + + double result = (_random.NextDouble() * (max - min)) + min; + return Convert.ToSingle(result); + } + + /// + /// Returns a random float between 0f and 1f. + /// + /// + public static float Random01() + { + return RandomInclusiveRange(0f, 1f); + } + + /// + /// Returns if a target float is within variance of the source float. + /// + /// + /// + /// + public static bool Near(this float a, float b, float tolerance = 0.01f) + { + return (Mathf.Abs(a - b) <= tolerance); + } + + /// + /// Clamps a float and returns if the float required clamping. + /// + /// + /// + /// + /// + /// + public static float Clamp(float value, float min, float max, ref bool clamped) + { + clamped = (value < min); + if (clamped) + return min; + + clamped = (value > min); + if (clamped) + return max; + + clamped = false; + return value; + } + + /// + /// Returns a float after being adjusted by the specified variance. + /// + /// + /// + /// + public static float Variance(this float source, float variance) + { + float pickedVariance = RandomInclusiveRange(1f - variance, 1f + variance); + return (source * pickedVariance); + } + /// + /// Sets a float value to result after being adjusted by the specified variance. + /// + /// + /// + /// + public static void Variance(this float source, float variance, ref float result) + { + float pickedVariance = RandomInclusiveRange(1f - variance, 1f + variance); + result = (source * pickedVariance); + } + + /// + /// Returns negative-one, zero, or postive-one of a value instead of just negative-one or positive-one. + /// + /// Value to sign. + /// Precise sign. + public static float PreciseSign(float value) + { + if (value == 0f) + return 0f; + else + return (Mathf.Sign(value)); + } + + /// + /// Returns if a float is within a range. + /// + /// Value of float. + /// Minimum of range. + /// Maximum of range. + /// + public static bool InRange(this float source, float rangeMin, float rangeMax) + { + return (source >= rangeMin && source <= rangeMax); + } + + /// + /// Randomly flips a float value. + /// + /// + /// + public static float RandomlyFlip(this float value) + { + if (Ints.RandomInclusiveRange(0, 1) == 0) + return value; + else + return (value *= -1f); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs.meta new file mode 100644 index 0000000..27c2a1f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 4ab517aa5c3b6e34ca20461339adda04 +timeCreated: 1526172456 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Floats.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs new file mode 100644 index 0000000..b00cd4d --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs @@ -0,0 +1,85 @@ +namespace GameKit.Dependencies.Utilities +{ + + 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. + public static ushort GetStableHashU16(this string txt) + { + uint hash32 = txt.GetStableHashU32(); + + 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 GetStableHashU32(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. + public static ulong GetStableHashU64(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; + } + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs.meta new file mode 100644 index 0000000..aea0ad1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7be723c9549bdd041ac1dc8e8c6d2d18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Hashing.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs new file mode 100644 index 0000000..d948173 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs @@ -0,0 +1,61 @@ +using System.Collections.Generic; +using System.IO; + +namespace GameKit.Dependencies.Utilities +{ + public static class IOs + { + + /// + /// Finds all prefab files in a path. + /// + /// Path to begin searching in; this is typically "Assets". + /// Paths to exclude when searching. + /// True to search subpaths. + /// + public static string[] GetDirectoryFiles(string startingPath, HashSet excludedPaths, bool recursive, string extension) + { + //Opportunity to exit early if there are no excluded paths. + if (excludedPaths.Count == 0) + { + string[] strResults = Directory.GetFiles(startingPath, extension, SearchOption.AllDirectories); + return strResults; + } + //starting path is excluded. + if (excludedPaths.Contains(startingPath)) + return new string[0]; + + //Folders remaining to be iterated. + List enumeratedCollection = new() { 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(); + //Build files from folders. + int count = enumeratedCollection.Count; + for (int i = 0; i < count; i++) + { + string[] r = Directory.GetFiles(enumeratedCollection[i], extension, SearchOption.TopDirectoryOnly); + results.AddRange(r); + } + + return results.ToArray(); + } + + } +} diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs.meta new file mode 100644 index 0000000..e358451 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b1994e262a1e963479497289602e4461 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/IOs.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs new file mode 100644 index 0000000..51d2db4 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs @@ -0,0 +1,91 @@ + +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + /// + /// Various utility classes relating to floats. + /// + public static class Ints + { + private static System.Random _random = new(); + + /// + /// Pads an index a specified value. Preferred over typical padding so that pad values used with skins can be easily found in the code. + /// + /// + /// + /// + public static string PadInt(int value, int padding) + { + return value.ToString().PadLeft(padding, '0'); + } + + /// + /// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Inclusive maximum value. + /// + public static int RandomInclusiveRange(int minimum, int maximum) + { + return _random.Next(minimum, maximum + 1); + } + /// + /// Provides a random exclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Exclusive maximum value. + /// + public static int RandomExclusiveRange(int minimum, int maximum) + { + return _random.Next(minimum, maximum); + } + + /// + /// Returns a clamped int within a specified range. + /// + /// Value to clamp. + /// Minimum value. + /// Maximum value. + /// + public static int Clamp(int value, int minimum, int maximum) + { + if (value < minimum) + value = minimum; + else if (value > maximum) + value = maximum; + + return value; + } + + /// + /// Determins if all values passed in are the same. + /// + /// Values to check. + /// True if all values are the same. + public static bool ValuesMatch(params int[] values) + { + if (values.Length == 0) + { + Debug.Log("Ints -> ValuesMatch -> values array is empty."); + return false; + } + + //Assign first value as element in first array. + int firstValue = values[0]; + //Check all values. + for (int i = 1; i < values.Length; i++) + { + //If any value doesn't match first value return false. + if (firstValue != values[i]) + return false; + } + + //If this far all values match. + return true; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs.meta new file mode 100644 index 0000000..514cb91 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: c673118198f5c4b41986d52762828363 +timeCreated: 1527268448 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Ints.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs new file mode 100644 index 0000000..ab5cab0 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Layers + { + /* GetInteractableLayer methods is an implementation from this + * link: https://forum.unity.com/threads/is-there-a-way-to-get-the-layer-collision-matrix.260744/#post-3483886 */ + /// + /// Lookup of interactable layers for each layer. + /// + private static Dictionary _interactablesLayers; + + /// + /// Tries to initializes InteractableLayers. + /// + private static void TryInitializeInteractableLayers() + { + if (_interactablesLayers != null) + return; + + _interactablesLayers = new(); + for (int i = 0; i < 32; i++) + { + int mask = 0; + for (int j = 0; j < 32; j++) + { + if (!Physics.GetIgnoreLayerCollision(i, j)) + { + mask |= 1 << j; + } + } + //Setting without add check is quicker. + _interactablesLayers[i] = mask; + } + } + + /// + /// Returns interactable layers value for layer. + /// + public static int GetInteractableLayersValue(int layer) + { + TryInitializeInteractableLayers(); + return _interactablesLayers[layer]; + } + + /// + /// Returns interactable layers LayerMask for a GameObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static LayerMask GetInteractableLayersMask(int layer) => (LayerMask)GetInteractableLayersValue(layer); + + + /// + /// Returns interactable layers value for a GameObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetInteractableLayersValue(GameObject go) => GetInteractableLayersValue(go.layer); + + /// + /// Returns interactable layers LayerMask for a GameObject. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static LayerMask GetInteractableLayersMask(GameObject go) => (LayerMask)GetInteractableLayersValue(go.layer); + + + /// + /// Converts a layer mask to a layer number. + /// + /// + /// + public static int LayerMaskToLayerNumber(LayerMask mask) + { + return LayerValueToLayerNumber(mask.value); + } + /// + /// Converts a layer value int to a layer int. + /// + /// + /// + public static int LayerValueToLayerNumber(int bitmask) + { + int result = bitmask > 0 ? 0 : 31; + while (bitmask > 1) + { + bitmask = bitmask >> 1; + result++; + } + return result; + } + + /// + /// Returns if a LayerMask contains a specified layer. + /// + /// LayerMask to check for layer in. + /// Layer to check within LayerMask. + /// + public static bool ContainsLayer(LayerMask layerMask, int layer) + { + return (layerMask == (layerMask | (1 << layer))); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs.meta new file mode 100644 index 0000000..76d07e3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 1c18e15e44d21a94d8919f4b6b125a1f +timeCreated: 1522349045 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Layers.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs new file mode 100644 index 0000000..1538dd6 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs @@ -0,0 +1,20 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace GameKit.Dependencies.Utilities +{ + + public static class LayoutGroups + { + /// + /// Returns how many entries can fit into a GridLayoutGroup + /// + public static int EntriesPerWidth(this GridLayoutGroup lg) + { + RectTransform rectTransform = lg.GetComponent(); + return Mathf.CeilToInt(rectTransform.rect.width / lg.cellSize.x); + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs.meta new file mode 100644 index 0000000..d08e44f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e330113395c59ca4dba5de001e010f08 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/LayoutGroups.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs new file mode 100644 index 0000000..e9140fc --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs @@ -0,0 +1,36 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Materials + { + /// + /// Returns the color or tint color property for a material. + /// + /// + /// + public static Color GetColor(this Material material) + { + if (material.HasProperty("_Color")) + return material.color; + else if (material.HasProperty("_TintColor")) + return material.GetColor("_TintColor"); + + return Color.white; + } + + /// + /// Sets the color or tint color property for a material. + /// + /// + public static void SetColor(this Material material, Color color) + { + if (material.HasProperty("_Color")) + material.color = color; + else if (material.HasProperty("_TintColor")) + material.SetColor("_TintColor", color); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs.meta new file mode 100644 index 0000000..2790a40 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 27a618c551d5fdb4ca70bf07e1905580 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Materials.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs new file mode 100644 index 0000000..a82f37f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs @@ -0,0 +1,44 @@ +namespace GameKit.Dependencies.Utilities +{ + public static class Maths + { + /// + /// 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; + } + + /// + /// Returns a clamped byte. + /// + public static byte ClampByte(byte value, byte min, byte 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/Plugins/GameKit/Dependencies/Utilities/Maths.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs.meta new file mode 100644 index 0000000..3d36c8f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 18a583dc22a9a0f4cabec0c4a0219c6e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Maths.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs new file mode 100644 index 0000000..cce4915 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs @@ -0,0 +1,208 @@ +#if NEW_INPUTSYSTEM +using UnityEngine; +using UnityEngine.InputSystem; + +namespace GameKit.Dependencies.Utilities +{ + + public static class NewInput + { + /// + /// Current Keyboard. + /// + public static Keyboard Keyboard => Keyboard.current; + /// + /// Current Mouse. + /// + public static Mouse Mouse => Mouse.current; + + /// + /// Returns if a button is held on any map. + /// + public static bool GetButtonHeld(Key key) + { + return (Keyboard != null) ? + Keyboard[key].isPressed : false; + } + /// + /// Returns if a button is pressed on any map. + /// + public static bool GetButtonPressed(Key key) + { + return (Keyboard != null) ? + Keyboard[key].wasPressedThisFrame : false; + } + /// + /// Returns if a button is released on any map. + /// + public static bool GetButtonReleased(Key key) + { + return (Keyboard != null) ? + Keyboard[key].wasReleasedThisFrame : false; + } + + + } + + public static class MouseExtensions + { + public static Vector3 GetPosition(this Mouse m) + { + return m.position.ReadValue(); + } + } + + public static class KeyboardExtensions + { + + public static bool GetKeyPressed(this Keyboard kb, Key kc) + { + return kb[kc].wasPressedThisFrame; + } + + public static bool GetKeyHeld(this Keyboard kb, Key kc) + { + return kb[kc].isPressed; + } + + public static bool GetKeyReleased(this Keyboard kb, Key kc) + { + return kb[kc].wasReleasedThisFrame; + } + + + + } + + public static class InputActionMapExtensions + { + #region Strings. + public static float GetAxisRaw(this InputActionMap map, string negativeName, string positiveName) + { + return map.GetAxisRaw(negativeName, positiveName, out _); + } + + public static float GetAxisRaw(this InputActionMap map, string negativeName, string positiveName, out bool found) + { + found = false; + InputAction negativeIa = map.FindAction(negativeName); + InputAction positiveIa = map.FindAction(positiveName); + if (negativeIa == null || positiveIa == null) + return 0f; + + found = true; + bool negativePressed = negativeIa.IsPressed(); + bool positivePressed = positiveIa.IsPressed(); + /* If both are pressed then they cancel each other out. + * And if neither are pressed then result is naturally + * 0f. */ + if (negativePressed == positivePressed) + return 0f; + else if (negativePressed) + return -1f; + else + return 1f; + } + + public static float GetAxisRaw(this InputActionMap map, string inputName) + { + return map.GetAxisRaw(inputName, out _); + } + public static float GetAxisRaw(this InputActionMap map, string inputName, out bool found) + { + found = false; + InputAction ia = map.FindAction(inputName); + if (ia == null) + return 0f; + + found = true; + float axis = ia.ReadValue(); + if (axis == 0f) + return 0f; + else + return Mathf.Sign(axis); + } + + public static bool GetButtonHeld(this InputActionMap map, string inputName) + { + InputAction ia = map.FindAction(inputName); + return (ia == null) ? false : ia.IsPressed(); + } + + public static bool GetButtonPressed(this InputActionMap map, string inputName) + { + InputAction ia = map.FindAction(inputName); + return (ia == null) ? false : ia.WasPressedThisFrame(); + } + + public static bool GetButtonReleased(this InputActionMap map, string inputName) + { + InputAction ia = map.FindAction(inputName); + return (ia == null) ? false : ia.WasReleasedThisFrame(); + } + #endregion + + #region InputActions. + public static float GetAxisRaw(InputAction negativeIa, InputAction positiveIa) + { + return GetAxisRaw(negativeIa, positiveIa, out _); + } + + public static float GetAxisRaw(InputAction negativeIa, InputAction positiveIa, out bool found) + { + found = false; + if (negativeIa == null || positiveIa == null) + return 0f; + + found = true; + bool negativePressed = negativeIa.IsPressed(); + bool positivePressed = positiveIa.IsPressed(); + /* If both are pressed then they cancel each other out. + * And if neither are pressed then result is naturally + * 0f. */ + if (negativePressed == positivePressed) + return 0f; + else if (negativePressed) + return -1f; + else + return 1f; + } + + public static float GetAxisRaw(this InputAction ia) + { + return GetAxisRaw(ia, out _); + } + public static float GetAxisRaw(this InputAction ia, out bool found) + { + found = false; + if (ia == null) + return 0f; + + found = true; + float axis = ia.ReadValue(); + if (axis == 0f) + return 0f; + else + return Mathf.Sign(axis); + } + + public static bool GetButtonHeld(this InputAction ia) + { + return (ia == null) ? false : ia.IsPressed(); + } + + public static bool GetButtonPressed(this InputAction ia) + { + return (ia == null) ? false : ia.WasPressedThisFrame(); + } + + public static bool GetButtonReleased(this InputAction ia) + { + return (ia == null) ? false : ia.WasReleasedThisFrame(); + } + #endregion + } + +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs.meta new file mode 100644 index 0000000..23d595a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ea6d91237dc169249b4a375e7e9eef00 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/NewInput.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs new file mode 100644 index 0000000..06d7d80 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs @@ -0,0 +1,771 @@ +using GameKit.Dependencies.Utilities.Types; +using System.Collections.Generic; +using System.Runtime.CompilerServices; + +namespace GameKit.Dependencies.Utilities +{ + /// + /// Implement to use type with Caches. + /// + public interface IResettable + { + /// + /// Resets values when being placed in a cache. + /// + void ResetState(); + + /// + /// Initializes values after being retrieved from a cache. + /// + void InitializeState(); + } + + #region Resettable caches. + /// + /// Caches collections of multiple generics. + /// + public static class ResettableCollectionCaches where T1 : IResettable, new() where T2 : IResettable, new() + { + /// + /// Retrieves a collection. + /// + /// + public static Dictionary RetrieveDictionary() => CollectionCaches.RetrieveDictionary(); + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref Dictionary value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(Dictionary value) + { + if (value == null) + return; + + foreach (KeyValuePair kvp in value) + { + ResettableObjectCaches.Store(kvp.Key); + ResettableObjectCaches.Store(kvp.Value); + } + + value.Clear(); + CollectionCaches.Store(value); + } + } + + /// + /// Caches collections of multiple generics. + /// + public static class ResettableT1CollectionCaches where T1 : IResettable, new() + { + /// + /// Retrieves a collection. + /// + /// + public static Dictionary RetrieveDictionary() => CollectionCaches.RetrieveDictionary(); + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref Dictionary value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(Dictionary value) + { + if (value == null) + return; + + foreach (T1 item in value.Keys) + ResettableObjectCaches.Store(item); + + value.Clear(); + CollectionCaches.Store(value); + } + } + + /// + /// Caches collections of multiple generics. + /// + public static class ResettableT2CollectionCaches where T2 : IResettable, new() + { + /// + /// Retrieves a collection. + /// + /// + public static Dictionary RetrieveDictionary() => CollectionCaches.RetrieveDictionary(); + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref Dictionary value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(Dictionary value) + { + if (value == null) + return; + + foreach (T2 item in value.Values) + ResettableObjectCaches.Store(item); + + value.Clear(); + CollectionCaches.Store(value); + } + } + + /// + /// Caches collections of a single generic. + /// + public static class ResettableCollectionCaches where T : IResettable, new() + { + /// + /// Cache for ResettableRingBuffer. + /// + private readonly static Stack> _resettableRingBufferCache = new(); + + /// + /// Retrieves a collection. + /// + public static ResettableRingBuffer RetrieveRingBuffer() + { + ResettableRingBuffer result; + if (!_resettableRingBufferCache.TryPop(out result)) + result = new(); + + return result; + } + + /// + /// Retrieves a collection. + /// + /// + public static T[] RetrieveArray() => CollectionCaches.RetrieveArray(); + + /// + /// Retrieves a collection. + /// + /// + public static List RetrieveList() => CollectionCaches.RetrieveList(); + + /// + /// Retrieves a collection. + /// + /// + public static HashSet RetrieveHashSet() => CollectionCaches.RetrieveHashSet(); + + /// + /// Retrieves a collection. + /// + /// + public static Queue RetrieveQueue() => CollectionCaches.RetrieveQueue(); + + /// + /// Retrieves a collection. + /// + /// + public static BasicQueue RetrieveBasicQueue() => CollectionCaches.RetrieveBasicQueue(); + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + /// Number of entries in the array from the beginning. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref ResettableRingBuffer value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + /// Number of entries in the array from the beginning. + public static void Store(ResettableRingBuffer value) + { + if (value == null) + return; + + value.ResetState(); + _resettableRingBufferCache.Push(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + /// Number of entries in the array from the beginning. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref T[] value, int count) + { + Store(value, count); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + /// Number of entries in the array from the beginning. + public static void Store(T[] value, int count) + { + if (value == null) + return; + + for (int i = 0; i < count; i++) + ResettableObjectCaches.Store(value[i]); + + CollectionCaches.Store(value, count); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref List value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(List value) + { + if (value == null) + return; + + for (int i = 0; i < value.Count; i++) + ResettableObjectCaches.Store(value[i]); + + value.Clear(); + CollectionCaches.Store(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref HashSet value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(HashSet value) + { + if (value == null) + return; + + foreach (T item in value) + ResettableObjectCaches.Store(item); + + value.Clear(); + CollectionCaches.Store(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref Queue value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(Queue value) + { + if (value == null) + return; + + foreach (T item in value) + ResettableObjectCaches.Store(item); + + value.Clear(); + CollectionCaches.Store(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref BasicQueue value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(BasicQueue value) + { + if (value == null) + return; + + while (value.TryDequeue(out T result)) + ResettableObjectCaches.Store(result); + + value.Clear(); + CollectionCaches.Store(value); + } + } + + /// + /// Caches objects of a single generic. + /// + public static class ResettableObjectCaches where T : IResettable, new() + { + /// + /// Retrieves an instance of T. + /// + public static T Retrieve() + { + T result = ObjectCaches.Retrieve(); + result.InitializeState(); + return result; + } + + /// + /// Stores an instance of T and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref T value) + { + Store(value); + value = default; + } + + /// + /// Stores an instance of T. + /// + /// Value to store. + public static void Store(T value) + { + if (value == null) + return; + + value.ResetState(); + ObjectCaches.Store(value); + } + } + #endregion + + #region NonResettable caches. + /// + /// Caches collections of multiple generics. + /// + public static class CollectionCaches + { + /// + /// Cache for dictionaries. + /// + private static readonly Stack> _dictionaryCache = new(); + + /// + /// Retrieves a collection. + /// + /// + public static Dictionary RetrieveDictionary() + { + Dictionary result; + if (!_dictionaryCache.TryPop(out result)) + result = new(); + + return result; + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref Dictionary value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(Dictionary value) + { + if (value == null) + return; + + value.Clear(); + _dictionaryCache.Push(value); + } + } + + /// + /// Caches collections of a single generic. + /// + public static class CollectionCaches + { + /// + /// Cache for arrays. + /// + private readonly static Stack _arrayCache = new(); + /// + /// Cache for lists. + /// + private readonly static Stack> _listCache = new(); + /// + /// Cache for queues. + /// + private readonly static Stack> _queueCache = new(); + /// + /// Cache for queues. + /// + private readonly static Stack> _basicQueueCache = new(); + /// + /// Cache for hashset. + /// + private readonly static Stack> _hashsetCache = new(); + + /// + /// Retrieves a collection. + /// + /// + public static T[] RetrieveArray() + { + T[] result; + if (!_arrayCache.TryPop(out result)) + result = new T[0]; + + return result; + } + + /// + /// Retrieves a collection. + /// + /// + public static List RetrieveList() + { + List result; + if (!_listCache.TryPop(out result)) + result = new(); + + return result; + } + + /// + /// Retrieves a collection. + /// + /// + public static Queue RetrieveQueue() + { + Queue result; + if (!_queueCache.TryPop(out result)) + result = new(); + + return result; + } + + /// + /// Retrieves a collection. + /// + /// + public static BasicQueue RetrieveBasicQueue() + { + BasicQueue result; + if (!_basicQueueCache.TryPop(out result)) + result = new(); + + return result; + } + + /// + /// Retrieves a collection adding one entry. + /// + /// + public static Queue RetrieveQueue(T entry) + { + Queue result; + if (!_queueCache.TryPop(out result)) + result = new(); + + result.Enqueue(entry); + return result; + } + + /// + /// Retrieves a collection adding one entry. + /// + /// + public static List RetrieveList(T entry) + { + List result; + if (!_listCache.TryPop(out result)) + result = new(); + + result.Add(entry); + return result; + } + + /// + /// Retrieves a HashSet. + /// + /// + public static HashSet RetrieveHashSet() + { + HashSet result; + if (!_hashsetCache.TryPop(out result)) + result = new(); + + return result; + } + + /// + /// Retrieves a collection adding one entry. + /// + /// + public static HashSet RetrieveHashSet(T entry) + { + HashSet result; + if (!_hashsetCache.TryPop(out result)) + return new(); + + result.Add(entry); + return result; + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + /// Number of entries in the array set default, from the beginning. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref T[] value, int count) + { + Store(value, count); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + /// Number of entries in the array from the beginning. + public static void Store(T[] value, int count) + { + if (value == null) + return; + + for (int i = 0; i < count; i++) + value[i] = default; + + _arrayCache.Push(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref List value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(List value) + { + if (value == null) + return; + + value.Clear(); + _listCache.Push(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref Queue value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(Queue value) + { + if (value == null) + return; + + value.Clear(); + _queueCache.Push(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref BasicQueue value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(BasicQueue value) + { + if (value == null) + return; + + value.Clear(); + _basicQueueCache.Push(value); + } + + /// + /// Stores a collection and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref HashSet value) + { + Store(value); + value = default; + } + + /// + /// Stores a collection. + /// + /// Value to store. + public static void Store(HashSet value) + { + if (value == null) + return; + + value.Clear(); + _hashsetCache.Push(value); + } + } + + /// + /// Caches objects of a single generic. + /// + public static class ObjectCaches where T : new() + { + /// + /// Stack to use. + /// + private static readonly Stack _stack = new(); + + /// + /// Returns a value from the stack or creates an instance when the stack is empty. + /// + /// + public static T Retrieve() + { + T result; + if (!_stack.TryPop(out result)) + result = new(); // Activator.CreateInstance(); + + return result; + } + + /// + /// Stores an instance of T and sets the original reference to default. + /// Method will not execute if value is null. + /// + /// Value to store. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void StoreAndDefault(ref T value) + { + Store(value); + value = default; + } + + /// + /// Stores a value to the stack. + /// + /// + public static void Store(T value) + { + if (value == null) + return; + + _stack.Push(value); + } + } + #endregion +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs.meta new file mode 100644 index 0000000..225536a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3cb13274f7491a941b6e89a767905f56 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/ObjectCaching.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs new file mode 100644 index 0000000..eff9e25 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs @@ -0,0 +1,67 @@ + +using GameKit.Dependencies.Utilities.Types; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Objects + { + /// + /// Returns if an object has been destroyed from memory. + /// + /// + /// + public static bool IsDestroyed(this GameObject gameObject) + { + // UnityEngine overloads the == operator for the GameObject type + // and returns null when the object has been destroyed, but + // actually the object is still there but has not been cleaned up yet + // if we test both we can determine if the object has been destroyed. + return (gameObject == null && !ReferenceEquals(gameObject, null)); + } + + /// + /// Finds all objects in the scene of type. This method is very expensive. + /// + /// + /// True if the scene must be fully loaded before trying to seek objects. + /// + public static List FindAllObjectsOfType(bool activeSceneOnly = true, bool requireSceneLoaded = false, bool includeDDOL = true, bool includeInactive = true) + { + List results = new(); + for (int i = 0; i < SceneManager.sceneCount; i++) + { + Scene scene = SceneManager.GetSceneAt(i); + //If to include only current scene. + if (activeSceneOnly) + { + if (SceneManager.GetActiveScene() != scene) + continue; + } + //If the scene must be fully loaded to seek objects within. + if (!scene.isLoaded && requireSceneLoaded) + continue; + + GameObject[] allGameObjects = scene.GetRootGameObjects(); + for (int j = 0; j < allGameObjects.Length; j++) + { + results.AddRange(allGameObjects[j].GetComponentsInChildren(includeInactive)); + } + } + + //If to also include DDOL. + if (includeDDOL) + { + GameObject ddolGo = DDOL.GetDDOL().gameObject; + results.AddRange(ddolGo.GetComponentsInChildren(includeInactive)); + } + + return results; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs.meta new file mode 100644 index 0000000..83ad933 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4fa6d28a28dbf6b4295602abad3de328 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Objects.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs new file mode 100644 index 0000000..3944c56 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs @@ -0,0 +1,109 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Particles + { + /// + /// Issues stop on the specified particle systems. + /// + /// + public static float StopParticleSystem(ParticleSystem[] systems, bool stopLoopingOnly) + { + return StopParticleSystem(systems, stopLoopingOnly, ParticleSystemStopBehavior.StopEmitting); + } + + /// + /// Issues stop on the specified particle systems while returning the time required to play out. + /// + /// + public static float StopParticleSystem(ParticleSystem[] systems, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting) + { + return StopParticleSystem(systems, false, stopBehavior); + } + + /// + /// Issues stop on the specified particle systems while returning the time required to play out. + /// + /// + public static float StopParticleSystem(ParticleSystem[] systems, bool stopLoopingOnly, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting) + { + if (systems == null) + return 0f; + + float playOutDuration = 0f; + for (int i = 0; i < systems.Length; i++) + playOutDuration = Mathf.Max(playOutDuration, StopParticleSystem(systems[i], stopLoopingOnly, stopBehavior)); + + return playOutDuration; + } + + /// + /// Issues stop on the specified particle systems. + /// + /// + public static float StopParticleSystem(ParticleSystem system, bool stopLoopingOnly, bool stopChildren = false) + { + return StopParticleSystem(system, stopLoopingOnly, ParticleSystemStopBehavior.StopEmitting, stopChildren); + } + + /// + /// Issues stop on the specified particle systems while returning the time required to play out. + /// + /// + public static float StopParticleSystem(ParticleSystem system, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting, bool stopChildren = false) + { + return StopParticleSystem(system, false, stopBehavior, stopChildren); + } + /// + /// Issues stop on the specified particle system while returning the time required to play out. + /// + public static float StopParticleSystem(ParticleSystem system, bool stopLoopingOnly, ParticleSystemStopBehavior stopBehavior = ParticleSystemStopBehavior.StopEmitting, bool stopChildren = false) + { + if (system == null) + return 0f; + if (stopChildren) + { + ParticleSystem[] all = system.GetComponentsInChildren(); + StopParticleSystem(all, stopLoopingOnly, stopBehavior); + } + + float playOutDuration = 0f; + float timeLeft = system.main.duration - system.time; + playOutDuration = Mathf.Max(playOutDuration, timeLeft); + + if (stopLoopingOnly) + { + if (system.main.loop) + system.Stop(false, stopBehavior); + } + else + { + system.Stop(false, stopBehavior); + } + + return playOutDuration; + } + + /// + /// Returns the longest time required for all systems to stop. + /// + /// + /// + public static float ReturnLongestCycle(ParticleSystem[] systems) + { + float longestPlayTime = 0f; + for (int i = 0; i < systems.Length; i++) + { + float timeLeft = systems[i].main.duration - systems[i].time; + longestPlayTime = Mathf.Max(longestPlayTime, timeLeft); + } + + return longestPlayTime; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs.meta new file mode 100644 index 0000000..a760d25 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5f3d973dcfa06554998575e8eef0938a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Particles.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs new file mode 100644 index 0000000..b67b47c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs @@ -0,0 +1,64 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Quaternions + { + + /// + /// Returns how fast an object must rotate over duration to reach goal. + /// + /// Quaternion to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// + public static float GetRate(this Quaternion a, Quaternion goal, float duration, out float angle, uint interval = 1, float tolerance = 0f) + { + angle = a.Angle(goal, true); + return angle / (duration * interval); + } + + /// + /// Subtracts b quaternion from a. + /// + public static Quaternion Subtract(this Quaternion a, Quaternion b) => (Quaternion.Inverse(b) * a); + /// + /// Adds quaternion b onto quaternion a. + /// + public static Quaternion Add(this Quaternion a, Quaternion b) => (a * b); + + /// + /// 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/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs.meta new file mode 100644 index 0000000..6078440 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 02a9084f4f788cd4293cdff56a49b5dd +timeCreated: 1522043602 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Quaternions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs new file mode 100644 index 0000000..647f3c9 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs @@ -0,0 +1,128 @@ + +using System; +using System.Text; + +namespace GameKit.Dependencies.Utilities +{ + + + public static class Strings + { + /// + /// Used to encode and decode strings. + /// + private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + /// + /// A buffer convert data and discard. + /// + public static byte[] Buffer = new byte[1024]; + + /// + /// Converts a member string text to PascalCase + /// + /// A member string is expected to be in the format '_memberName'. + public static string MemberToPascalCase(this string txt) + { + if (txt.Length < 2) + { + UnityEngine.Debug.LogError($"Text '{txt}' is too short."); + return string.Empty; + } + + if (txt[0] != '_') + { + UnityEngine.Debug.LogError($"Text '{txt}' has the incorrect member prefix."); + return string.Empty; + } + + string firstLeter = txt[1].ToString().ToUpper(); + + string substring = (txt.Length > 2) ? txt.Substring(2) : string.Empty; + return $"{firstLeter}{substring}"; + } + + /// + /// Converts a pascal case string to member case. + /// + /// A PascalCase string is expected to be in the format 'PropertyName'. + public static string PascalCaseToMember(this string txt) + { + if (txt.Length < 1) + { + UnityEngine.Debug.LogError($"Text '{txt}' is too short."); + return string.Empty; + } + + string firstLeter = txt[0].ToString().ToLower(); + + string subString = (txt.Length > 1) ? txt.Substring(1) : string.Empty; + return $"_{firstLeter}{subString}"; + } + + + /// + /// Attachs or detaches an suffix to a string. + /// + /// + /// + /// + public static string ReturnModifySuffix(string text, string suffix, bool addExtension) + { + /* Since saving to a json, add the .json extension if not present. + * Length must be greater than 6 to contain a character and .json. */ + if (text.Length > (suffix.Length + 1)) + { + //If to add the extension. + if (addExtension) + { + //If doesn't contain the extension then add it on. + if (!text.Substring(text.Length - suffix.Length).Contains(suffix, StringComparison.CurrentCultureIgnoreCase)) + return (text + suffix); + //Already contains extension. + else + return text; + } + //Remove extension. + else + { + //If contains extension. + if (text.Substring(text.Length - suffix.Length).Contains(suffix, StringComparison.CurrentCultureIgnoreCase)) + return text.Substring(0, text.Length - (suffix.Length)); + //Doesn't contain extension. + return text; + } + } + //Text isn't long enough to manipulate. + else + { + return text; + } + } + + /// + /// Converts a string into a byte array buffer. + /// + /// Number of bytes written to the buffer. + public static int ToBytes(this string value, ref byte[] buffer) + { + int strLength = value.Length; + //Number of minimum bytes the buffer must be. + int bytesNeeded = _encoding.GetMaxByteCount(strLength); + + //Grow string buffer if needed. + if (buffer.Length < bytesNeeded) + Array.Resize(ref buffer, (bytesNeeded * 2)); + + return _encoding.GetBytes(value, 0, strLength, buffer, 0); + } + + /// + /// Converts a string to bytes while allocating. + /// + public static byte[] ToBytesAllocated(this string value) => Encoding.Unicode.GetBytes(value); + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs.meta new file mode 100644 index 0000000..3db6097 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: b396f2be4de550a4e92b552650311600 +timeCreated: 1525378031 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Strings.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs new file mode 100644 index 0000000..f9c0581 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs @@ -0,0 +1,195 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + public static class Transforms + { + /// + /// Returns the sizeDelta halfed. + /// + /// True to multiple values by RectTransform scale. + public static Vector2 HalfSizeDelta(this RectTransform rectTransform, bool useScale = false) + { + Vector2 sizeDelta = (useScale) ? rectTransform.SizeDeltaScaled() : rectTransform.sizeDelta; + return (sizeDelta / 2f); + } + + /// + /// Returns the sizeDelta multiplied by scale. + /// + public static Vector2 SizeDeltaScaled(this RectTransform rectTransform) + { + return (rectTransform.sizeDelta * rectTransform.localScale); + } + + /// + /// Returns a position for the rectTransform ensuring it's fully on the screen. + /// + /// Preferred position for the rectTransform. + /// How much padding the transform must be from the screen edges. + public static Vector3 GetOnScreenPosition(this RectTransform rectTransform, Vector3 desiredPosition, Vector2 padding) + { + RectTransform canvasRectTransform = rectTransform.GetComponentInParent().transform as RectTransform; + Vector2 clampedPos = desiredPosition; + Vector2 localScale = canvasRectTransform.localScale; + Vector2 oneMinusPivot = Vector2.one - rectTransform.pivot; + + //The size has to be scaled to account for the size and scale of the Canvas it is childed to + Vector2 scaledSize = rectTransform.sizeDelta * localScale; + + //Calculate the minimum and maximum bounds of the canvas our object can occupy + Vector2 minClamp = scaledSize * rectTransform.pivot + padding; + Vector2 maxClamp = ((canvasRectTransform.rect.size) - (rectTransform.sizeDelta * oneMinusPivot + padding)) * localScale; + + float clampX = Mathf.Clamp(clampedPos.x, minClamp.x, maxClamp.x); + float clampY = Mathf.Clamp(clampedPos.y, minClamp.y, maxClamp.y); + + return new Vector2(clampX, clampY); + } + + /// + /// Sets a parent for src while maintaining position, rotation, and scale of src. + /// + /// Transform to become a child of. + public static void SetParentAndKeepTransform(this Transform src, Transform parent) + { + Vector3 pos = src.position; + Quaternion rot = src.rotation; + Vector3 scale = src.localScale; + + src.SetParent(parent); + src.position = pos; + src.rotation = rot; + src.localScale = scale; + } + + /// + /// Destroys all children under the specified transform. + /// + public static void DestroyChildren(this Transform t, bool destroyImmediately = false) + { + //If destroying immediately then the iteration needs to occur only on the top-most children. + if (destroyImmediately) + { + List children = CollectionCaches.RetrieveList(); + int childCount = t.childCount; + + for (int i = 0; i < childCount; i++) + children.Add(t.GetChild(i)); + + foreach (Transform child in children) + UnityEngine.Object.DestroyImmediate(child); + + CollectionCaches.Store(children); + } + //Iterate using Unitys enumerator. + else + { + foreach (Transform child in t) + UnityEngine.Object.Destroy(child.gameObject); + } + } + + /// + /// Destroys all children of a type under the specified transform. + /// + public static void DestroyChildren(this Transform t, bool destroyImmediately = false) where T : MonoBehaviour + { + T[] children = t.GetComponentsInChildren(); + foreach (T child in children) + { + if (destroyImmediately) + MonoBehaviour.DestroyImmediate(child.gameObject); + else + UnityEngine.Object.Destroy(child.gameObject); + } + } + + /// + /// Gets components in children and optionally parent. + /// + /// + /// + /// + /// + /// + public static void GetComponentsInChildren(this Transform parent, List results, bool includeParent = true, bool includeInactive = false) where T : Component + { + if (!includeParent) + { + List current = CollectionCaches.RetrieveList(); + for (int i = 0; i < parent.childCount; i++) + { + parent.GetChild(i).GetComponentsInChildren(includeInactive, current); + results.AddRange(current); + } + CollectionCaches.Store(current); + } + else + { + parent.GetComponentsInChildren(includeInactive, results); + } + } + + /// + /// Returns the position of this transform. + /// + public static Vector3 GetPosition(this Transform t, bool localSpace) + { + return (localSpace) ? t.localPosition : t.position; + } + + /// + /// Returns the rotation of this transform. + /// + public static Quaternion GetRotation(this Transform t, bool localSpace) + { + return (localSpace) ? t.localRotation : t.rotation; + } + + /// + /// Returns the scale of this transform. + /// + public static Vector3 GetScale(this Transform t) + { + return t.localScale; + } + + /// + /// Sets the position of this transform. + /// + /// + /// + public static void SetPosition(this Transform t, bool localSpace, Vector3 pos) + { + if (localSpace) + t.localPosition = pos; + else + t.position = pos; + } + + /// + /// Sets the position of this transform. + /// + /// + /// + public static void SetRotation(this Transform t, bool localSpace, Quaternion rot) + { + if (localSpace) + t.localRotation = rot; + else + t.rotation = rot; + } + + /// + /// Sets the position of this transform. + /// + /// + /// + public static void SetScale(this Transform t, Vector3 scale) + { + t.localScale = scale; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs.meta new file mode 100644 index 0000000..e7d7fbc --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ba23540de73f58b458e7d7a200f3bb30 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Transforms.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types.meta new file mode 100644 index 0000000..1d918be --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a29cd7621a70a044bb205cc8cfd96b3c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs new file mode 100644 index 0000000..b786be7 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs @@ -0,0 +1,243 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + /// + /// Unity 2022 has a bug where codegen will not compile when referencing a Queue type, + /// while also targeting .Net as the framework API. + /// As a work around this class is used for queues instead. + /// + public class BasicQueue + { + /// + /// Maximum size of the collection. + /// + public int Capacity => Collection.Length; + /// + /// Number of elements in the queue. + /// + public int Count => _written; + /// + /// Collection containing data. + /// + private T[] Collection = new T[4]; + /// + /// Current write index of the collection. + /// + public int WriteIndex { get; private set; } + /// + /// Buffer for resizing. + /// + private T[] _resizeBuffer = new T[0]; + /// + /// Read position of the next Dequeue. + /// + private int _read; + + /// + /// Length of the queue. + /// + private int _written; + + /// + /// Enqueues an entry. + /// + /// + public void Enqueue(T data) + { + if (_written == Collection.Length) + Resize(); + + if (WriteIndex >= Collection.Length) + WriteIndex = 0; + Collection[WriteIndex] = data; + + WriteIndex++; + _written++; + } + + /// + /// Tries to dequeue the next entry. + /// + /// Dequeued entry. + /// True to set the array entry as default. + /// True if an entry existed to dequeue. + public bool TryDequeue(out T result, bool defaultArrayEntry = true) + { + if (_written == 0) + { + result = default; + return false; + } + + result = Dequeue(defaultArrayEntry); + return true; + } + + /// + /// Dequeues the next entry. + /// + /// True to set the array entry as default. + public T Dequeue(bool defaultArrayEntry = true) + { + if (_written == 0) + return default; + + T result = Collection[_read]; + if (defaultArrayEntry) + Collection[_read] = default; + + _written--; + _read++; + if (_read >= Collection.Length) + _read = 0; + + return result; + } + + /// + /// Tries to peek the next entry. + /// + /// Peeked entry. + /// True if an entry existed to peek. + public bool TryPeek(out T result) + { + if (_written == 0) + { + result = default; + return false; + } + + result = Peek(); + return true; + } + + /// + /// Peeks the next queue entry. + /// + /// + public T Peek() + { + if (_written == 0) + throw new($"Queue of type {typeof(T).Name} is empty."); + + return Collection[_read]; + } + + /// + /// Returns an entry at index or default if index is invalid. + /// + public T GetIndexOrDefault(int simulatedIndex) + { + int offset = GetRealIndex(simulatedIndex, allowUnusedBuffer: false, log: false); + if (offset != -1 && offset < Collection.Length) + return Collection[offset]; + + return default; + } + + /// + /// Clears the queue. + /// + public void Clear() + { + _read = 0; + WriteIndex = 0; + _written = 0; + + DefaultCollection(Collection); + DefaultCollection(_resizeBuffer); + + void DefaultCollection(T[] array) + { + int count = array.Length; + for (int i = 0; i < count; i++) + array[i] = default; + } + } + + /// + /// Doubles the queue size. + /// + private void Resize() + { + int length = _written; + int doubleLength = (length * 2); + int read = _read; + + /* Make sure copy array is the same size as current + * and copy contents into it. */ + //Ensure large enough to fit contents. + T[] resizeBuffer = _resizeBuffer; + if (resizeBuffer.Length < doubleLength) + Array.Resize(ref resizeBuffer, doubleLength); + //Copy from the read of queue first. + int copyLength = (length - read); + Array.Copy(Collection, read, resizeBuffer, 0, copyLength); + /* If read index was higher than 0 + * then copy remaining data as well from 0. */ + if (read > 0) + Array.Copy(Collection, 0, resizeBuffer, copyLength, read); + + //Set _array to resize. + Collection = resizeBuffer; + //Reset positions. + _read = 0; + WriteIndex = length; + } + + /// + /// 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 = GetRealIndex(simulatedIndex); + return Collection[offset]; + } + set + { + int offset = GetRealIndex(simulatedIndex); + Collection[offset] = value; + } + } + + /// + /// Returns the real index of the collection using a simulated index. + /// + /// True to allow an index be returned from an unused portion of the buffer so long as it is within bounds. + private int GetRealIndex(int simulatedIndex, bool allowUnusedBuffer = false, bool log = true) + { + if (simulatedIndex >= Capacity) + { + return ReturnError(); + } + else + { + int written = _written; + //May be out of bounds if allowUnusedBuffer is false. + if (simulatedIndex >= written) + { + if (!allowUnusedBuffer) + return ReturnError(); + } + int offset = (Capacity - written) + simulatedIndex + WriteIndex; + if (offset >= Capacity) + offset -= Capacity; + + return offset; + } + + int ReturnError() + { + if (log) + UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Collection count is {_written}, Capacity is {Capacity}"); + return -1; + } + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs.meta new file mode 100644 index 0000000..dbc94f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f966ec63b499e77438c185b2870595cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/BasicQueue.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs new file mode 100644 index 0000000..8b94503 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs @@ -0,0 +1,48 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + + [System.Serializable] + public struct ByteRange + { + public ByteRange(byte minimum, byte maximum) + { + Minimum = minimum; + Maximum = maximum; + } + /// + /// Minimum range. + /// + public byte Minimum; + /// + /// Maximum range. + /// + public byte Maximum; + + /// + /// Returns an exclusive random value between Minimum and Maximum. + /// + /// + public byte RandomExclusive() => Bytes.RandomExclusiveRange(Minimum, Maximum); + /// + /// Returns an inclusive random value between Minimum and Maximum. + /// + /// + public byte RandomInclusive() => Bytes.RandomInclusiveRange(Minimum, Maximum); + + /// + /// Clamps value between Minimum and Maximum. + /// + public byte Clamp(byte value) => Bytes.Clamp(value, Minimum, Maximum); + + /// + /// True if value is within range of Minimum and Maximum. + /// + public bool InRange(byte value) => (value >= Minimum) && (value <= Maximum); + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs.meta new file mode 100644 index 0000000..41be76e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2756054f52fbd5d4cb1871b0dd6ff5b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ByteRange.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs new file mode 100644 index 0000000..4b30540 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs @@ -0,0 +1,237 @@ + +using Sirenix.OdinInspector; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + public class CanvasGroupFader : MonoBehaviour + { + #region Types. + /// + /// Current fade state or goal for this class. + /// + public enum FadeGoalType + { + Unset = 0, + Hidden = 1, + Visible = 2, + } + #endregion + + #region Public. + /// + /// Current goal for the fader. + /// + public FadeGoalType FadeGoal { get; private set; } = FadeGoalType.Unset; + /// + /// True if hidden or in the process of hiding. + /// + public bool IsHiding => (FadeGoal == FadeGoalType.Hidden); + /// + /// True if visible. Will be true long as the CanvasGroup has alpha. Also see IsHiding. + /// + public bool IsVisible => (CanvasGroup.alpha > 0f); + #endregion + + #region Serialized. + /// + /// CanvasGroup to fade in and out. + /// + [Tooltip("CanvasGroup to fade in and out.")] + [SerializeField, TabGroup("Components")] + protected CanvasGroup CanvasGroup; + /// + /// True to update the CanvasGroup blocking settings when showing and hiding. + /// + [Tooltip("True to update the CanvasGroup blocking settings when showing and hiding.")] + [SerializeField, TabGroup("Effects")] + protected bool UpdateCanvasBlocking = true; + /// + /// How long it should take to fade in the CanvasGroup. + /// + [SerializeField, TabGroup("Effects")] + protected float FadeInDuration = 0.1f; + /// + /// How long it should take to fade out the CanvasGroup. + /// + [SerializeField, TabGroup("Effects")] + protected float FadeOutDuration = 0.3f; + #endregion + + #region Private. + /// + /// True if a fade cycle has completed at least once. + /// + private bool _completedOnce; + #endregion + + protected virtual void OnEnable() + { + FadeGoal = (CanvasGroup.alpha > 0f) ? FadeGoalType.Visible : FadeGoalType.Hidden; + } + + protected virtual void OnDisable() + { + if (FadeGoal == FadeGoalType.Visible) + ShowImmediately(); + else + HideImmediately(); + } + + protected virtual void Update() + { + Fade(); + } + + /// + /// Shows CanvasGroup immediately. + /// + public virtual void ShowImmediately() + { + SetFadeGoal(true); + CompleteFade(true); + OnShow(); + } + + /// + /// Hides CanvasGroup immediately. + /// + public virtual void HideImmediately() + { + SetFadeGoal(false); + CompleteFade(false); + OnHide(); + } + + /// + /// Shows CanvasGroup with a fade. + /// + public virtual void Show() + { + if (FadeInDuration <= 0f) + { + ShowImmediately(); + } + else + { + SetFadeGoal(true); + OnShow(); + } + } + + /// + /// Called after Show or ShowImmediate. + /// + protected virtual void OnShow() { } + + /// + /// Hides CanvasGroup with a fade. + /// + public virtual void Hide() + { + if (FadeOutDuration <= 0f) + { + HideImmediately(); + } + else + { + //Immediately make unclickable so players cannot hit UI objects as it's fading out. + SetCanvasGroupBlockingType(CanvasGroupBlockingType.Block); + SetFadeGoal(false); + OnHide(); + } + } + + /// + /// Called after Hide or HideImmediate. + /// + protected virtual void OnHide() { } + + /// + /// Sets showing and begins fading if required. + /// + /// + private void SetFadeGoal(bool fadeIn) + { + FadeGoal = (fadeIn) ? FadeGoalType.Visible : FadeGoalType.Hidden; + } + + /// + /// Fades in or out over time. + /// + /// + private void Fade() + { + //Should not be possible. + if (FadeGoal == FadeGoalType.Unset) + { + Debug.LogError($"{gameObject.name} has an unset FadeGoal. This should not be possible."); + return; + } + + bool fadingIn = (FadeGoal == FadeGoalType.Visible); + float duration; + float targetAlpha; + if (fadingIn) + { + targetAlpha = 1f; + duration = FadeInDuration; + } + else + { + targetAlpha = 0f; + duration = FadeOutDuration; + } + + /* Already at goal and had completed an iteration at least once. + * This is checked because even if at alpha we want to + * complete the cycle if not done once so that all + * local states and canvasgroup settings are proper. */ + if (_completedOnce && CanvasGroup.alpha == targetAlpha) + return; + + float rate = (1f / duration); + CanvasGroup.alpha = Mathf.MoveTowards(CanvasGroup.alpha, targetAlpha, rate * Time.deltaTime); + + //If complete. + if (CanvasGroup.alpha == targetAlpha) + CompleteFade(fadingIn); + } + + /// + /// Called when the fade completes. + /// + protected virtual void CompleteFade(bool fadingIn) + { + CanvasGroupBlockingType blockingType; + float alpha; + if (fadingIn) + { + blockingType = CanvasGroupBlockingType.Block; + alpha = 1f; + } + else + { + blockingType = CanvasGroupBlockingType.DoNotBlock; + alpha = 0f; + } + + SetCanvasGroupBlockingType(blockingType); + CanvasGroup.alpha = alpha; + _completedOnce = true; + } + + /// + /// Changes the CanvasGroups interactable and bloacking state. + /// + protected virtual void SetCanvasGroupBlockingType(CanvasGroupBlockingType blockingType) + { + if (UpdateCanvasBlocking) + CanvasGroup.SetBlockingType(blockingType); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs.meta new file mode 100644 index 0000000..7c75592 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6aa7bd78c33474948b74e3c7a1d97454 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/CanvasGroupFader.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases.meta new file mode 100644 index 0000000..a558f8f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f04cb37e5f34ee749b8be696cb9c6ba3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs new file mode 100644 index 0000000..2457fe1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs @@ -0,0 +1,59 @@ + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + public class ButtonData : IResettable + { + #region Public. + /// + /// Text to place on the button. + /// + public string Text { get; protected set; } = string.Empty; + /// + /// When not null this will be called when action is taken. + /// + /// Optional key to associate with callback. + public delegate void PressedDelegate(string key); + /// + /// Optional key to include within the callback. + /// + public string Key { get; protected set; } = string.Empty; + #endregion + + /// + /// Delegate to invoke when pressed. + /// + private PressedDelegate _delegate = null; + + /// + /// Initializes this for use. + /// + /// Text to display on the button. + /// Callback when OnPressed is called. + /// Optional key to include within the callback. + public void Initialize(string text, PressedDelegate callback, string key = "") + { + Text = text; + Key = key; + _delegate = callback; + } + + /// + /// Called whewn the button for this data is pressed. + /// + public virtual void OnPressed() + { + _delegate?.Invoke(Key); + } + + public virtual void ResetState() + { + Text = string.Empty; + _delegate = null; + Key = string.Empty; + } + + public void InitializeState() { } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs.meta new file mode 100644 index 0000000..12bc3bb --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1baacd2a6a8a0e94b897c6b7176c7000 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ButtonData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs new file mode 100644 index 0000000..3119da9 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; + +namespace GameKit.Dependencies.Utilities.Types +{ + /// + /// Used to track generic canvases and their states. + /// + public static class CanvasTracker + { + /// + /// Canvases which should block input. + /// + public static IReadOnlyList InputBlockingCanvases => _inputBlockingCanvases; + private static List _inputBlockingCanvases = new(); + /// + /// Canvases which are currently open, in the order they were opened. + /// + public static IReadOnlyList OpenCanvases => _openCanvases; + private static List _openCanvases = new(); + /// + /// True if any blocking canvas is open. + /// + public static bool IsInputBlockingCanvasOpen => (_inputBlockingCanvases.Count > 0); + /// + /// Returns true if is the last canvas opened or if no canvases are set as opened. + /// + public static bool IsLastOpenCanvas(object canvas) => IsEmptyCollectionOrLastEntry(canvas, _openCanvases); + /// + /// Returns true if is the last canvas blocking input or if no input blocking canvases are set as opened. + /// + public static bool IsLastInputBlockingCanvas(object canvas) => IsEmptyCollectionOrLastEntry(canvas, _inputBlockingCanvases); + + /// + /// Returns true if canvas is the last object in collection or collection is empty. + /// + /// + private static bool IsEmptyCollectionOrLastEntry(object canvas, List collection) + { + int count = collection.Count; + if (count == 0) + return true; + + return (collection[count - 1] == canvas); + } + + /// + /// Clears all collections. + /// + public static void ClearCollections() + { + _openCanvases.Clear(); + _inputBlockingCanvases.Clear(); + } + + /// + /// Removes null references of canvases. + /// This can be used as clean-up if you were unable to remove a canvas properly. + /// Using this method regularly could be expensive if there are hundreds of open canvases. + /// + public static void RemoveNullReferences() + { + RemoveNullEntries(_openCanvases); + RemoveNullEntries(_inputBlockingCanvases); + + void RemoveNullEntries(List collection) + { + for (int i = 0; i < collection.Count; i++) + { + if (collection[i] == null) + { + collection.RemoveAt(i); + i--; + } + } + } + } + + /// + /// Returns true if canvas is an open canvas. + /// + public static bool IsOpenCanvas(object canvas) + { + return _openCanvases.Contains(canvas); + } + /// + /// Returns if the canvas is an input blocking canvas. + /// + public static bool IsInputBlockingCanvas(object canvas) + { + return _inputBlockingCanvases.Contains(canvas); + } + + /// + /// Adds a canvas to OpenCanvases if not already added. + /// + /// True to also add as an input blocking canvas. + /// True if the canvas was added, false if already added. + public static bool AddOpenCanvas(object canvas, bool addToBlocking) + { + bool added = _openCanvases.AddUnique(canvas); + if (added && addToBlocking) + _inputBlockingCanvases.Add(canvas); + + return added; + } + + /// + /// Removes a canvas from OpenCanvases. + /// + /// True if the canvas was removed, false if it was not added. + public static bool RemoveOpenCanvas(object canvas) + { + _inputBlockingCanvases.Remove(canvas); + return _openCanvases.Remove(canvas); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs.meta new file mode 100644 index 0000000..5bfc405 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bd8ee85ef5b290246b270cefc7f018ca +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/CanvasTracker.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs new file mode 100644 index 0000000..37cf07a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs @@ -0,0 +1,234 @@ +using GameKit.Dependencies; + +using System.Runtime.CompilerServices; +using Sirenix.OdinInspector; +using UnityEngine; + + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + + public class FloatingContainer : CanvasGroupFader + { + #region Serialized. + /// + /// RectTransform to move. + /// + [Tooltip("RectTransform to move.")] + [SerializeField, TabGroup("Components")] + protected RectTransform RectTransform; + /// + /// True to use edge avoidance. + /// + [Tooltip("True to use edge avoidance.")] + [SerializeField, TabGroup("Sizing")] + protected bool UseEdgeAvoidance = true; + /// + /// How much to avoid screen edges when being moved. + /// + [Tooltip("How much to avoid screen edges when being moved.")] + [SerializeField, TabGroup("Sizing"), ShowIf(nameof(UseEdgeAvoidance), true)] + protected Vector2 EdgeAvoidance; + #endregion + + #region Private. + /// + /// Desired position. + /// + private Vector3 _positionGoal; + /// + /// Desired rotation. + /// + private Quaternion _rotationGoal; + /// + /// Desired scale. + /// + private Vector3 _scaleGoal = Vector3.one; + /// + /// How much edge avoidance to use. + /// + private Vector2? _edgeAvoidance; + #endregion + + /// + /// Attachs a gameObject as a child of this object and sets transform valus to default. + /// + /// GameObject to attach. + public void AttachGameObject(GameObject go) + { + if (go == null) + return; + + Transform goT = go.transform; + goT.SetParent(transform); + goT.localPosition = Vector3.zero; + goT.localRotation = Quaternion.identity; + goT.localScale = Vector3.one; + } + + /// + /// Shows the container. + /// + /// Position to use. + /// Rotation to use. + /// Scale to use. + /// Pivot for rectTransform. + /// How far to keep the RectTransform from the edge. If null serialized avoidance will be used. + public virtual void Show(Vector3 position, Quaternion rotation, Vector3 scale, Vector2 pivot, Vector2? edgeAvoidanceOverride = null) + { + UpdateEdgeAvoidance(edgeAvoidanceOverride, false); + UpdatePivot(pivot, false); + UpdatePositionRotationAndScale(position, rotation, scale); + base.Show(); + } + + /// + /// Shows the container. + /// + /// Position to use. + /// How far to keep the RectTransform from the edge. If null serialized avoidance will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Show(Vector3 position, Vector2? edgeAvoidanceOverride = null) + { + Show(position, Quaternion.identity, Vector3.one, RectTransform.pivot); + } + + /// + /// Shows the container. + /// + /// Position to use. + /// Rotation to use. + /// How far to keep the RectTransform from the edge. If null serialized avoidance will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Show(Vector3 position, Quaternion rotation, Vector2? edgeAvoidanceOverride = null) + { + Show(position, rotation, Vector3.one, RectTransform.pivot); + } + + /// + /// Shows the container. + /// + /// Transform to use for position, rotation, and scale. + /// How far to keep the RectTransform from the edge. If null serialized avoidance will be used. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void Show(Transform startingPoint, Vector2? edgeAvoidanceOverride = null) + { + if (startingPoint == null) + { + Debug.LogError($"A null Transform cannot be used as the starting point."); + return; + } + + Show(startingPoint.position, startingPoint.rotation, startingPoint.localScale, RectTransform.pivot); + } + + /// + /// Updates the rectTransform pivot. + /// + /// New pivot. + /// True to move the RectTransform after updating. + public virtual void UpdatePivot(Vector2 pivot, bool move = true) + { + RectTransform.pivot = pivot; + if (move) + Move(); + } + + /// + /// Updates to a new position. + /// + /// Next position. + /// True to move towards new position. + public virtual void UpdatePosition(Vector3 position, bool move = true) + { + _positionGoal = position; + if (move) + Move(); + } + + /// + /// Updates to a new rotation. + /// + /// Next rotation. + public virtual void UpdateRotation(Quaternion rotation, bool move = true) + { + _rotationGoal = rotation; + if (move) + Move(); + } + + /// + /// Updates to a new scale. + /// + /// Next scale. + /// True to move the RectTransform after updating. + public virtual void UpdateScale(Vector3 scale, bool move = true) + { + _scaleGoal = scale; + if (move) + Move(); + } + + /// + /// Updates to a new position and rotation. + /// + /// Next position. + /// Next rotation. + /// True to move the RectTransform after updating. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void UpdatePositionAndRotation(Vector3 position, Quaternion rotation, bool move = true) + { + UpdatePosition(position, false); + UpdateRotation(rotation, false); + if (move) + Move(); + } + /// + /// Updates to a new position, rotation, and scale. + /// + /// Next position. + /// Next rotation. + /// Next scale. + /// True to move the RectTransform after updating. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public virtual void UpdatePositionRotationAndScale(Vector3 position, Quaternion rotation, Vector3 scale, bool move = true) + { + UpdatePositionAndRotation(position, rotation, false); + UpdateScale(scale, false); + Move(); + } + + /// + /// Updates how much edge avoidance to use. When null serialized values are used. + /// + /// How far to keep the RectTransform from the edge. If null serialized avoidance will be used. + /// True to move the RectTransform after updating. + public virtual void UpdateEdgeAvoidance(Vector2? edgeAvoidanceOverride = null, bool move = true) + { + _edgeAvoidance = (edgeAvoidanceOverride.HasValue) ? edgeAvoidanceOverride.Value : EdgeAvoidance; + if (move) + Move(); + } + + /// + /// Moves to configured goals. + /// + protected virtual void Move() + { + //Update scale first so edge avoidance takes it into consideration. + RectTransform.localScale = _scaleGoal; + + Vector2 position = _positionGoal; + if (UseEdgeAvoidance) + { + Vector2 avoidance = (_edgeAvoidance.HasValue) ? _edgeAvoidance.Value : EdgeAvoidance; + position = RectTransform.GetOnScreenPosition(_positionGoal, avoidance); + } + + RectTransform.SetPositionAndRotation(position, _rotationGoal); + + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs.meta new file mode 100644 index 0000000..ddce2e4 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f1fcd998728ace94ea809c34ad0d09e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingContainer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs new file mode 100644 index 0000000..4c0da94 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs @@ -0,0 +1,36 @@ + +using Sirenix.OdinInspector; +using UnityEngine; +using UnityEngine.UI; + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + + public class FloatingImage : FloatingContainer + { + /// + /// Renderer to apply sprite on. + /// + [Tooltip("Renderer to apply sprite on.")] + [SerializeField, TabGroup("Components")] + protected Image Renderer; + + /// + /// Sets which sprite to use. + /// + /// Sprite to use. + /// When has value the renderer will be set to this size. Otherwise, the size of the sprite will be used. This value assumes the sprite anchors are set to center. + public virtual void SetSprite(Sprite sprite, Vector3? sizeOverride) + { + Renderer.sprite = sprite; + Vector3 size = (sizeOverride == null) + ? (sprite.bounds.size * sprite.pixelsPerUnit) + : sizeOverride.Value; + + Renderer.rectTransform.sizeDelta = size; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs.meta new file mode 100644 index 0000000..c09b22b --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2169e34ec7dd63a4e99b5cc89fd0e966 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingImage.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs new file mode 100644 index 0000000..18d587a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs @@ -0,0 +1,44 @@ +using GameKit.Dependencies.Utilities.Types.CanvasContainers; +using System.Collections.Generic; + + +namespace GameKit.Dependencies.Utilities.Types.OptionMenuButtons +{ + + public class FloatingOptions : CanvasGroupFader + { + #region Protected. + /// + /// Current buttons. + /// + protected List Buttons = new(); + #endregion + + /// + /// Adds buttons. + /// + /// True to clear existing buttons first. + /// Buttons to add. + protected virtual void AddButtons(bool clearExisting, IEnumerable buttonDatas) + { + if (clearExisting) + RemoveButtons(); + foreach (ButtonData item in buttonDatas) + Buttons.Add(item); + } + + /// + /// Removes all buttons. + /// + protected virtual void RemoveButtons() + { + foreach (ButtonData item in Buttons) + ResettableObjectCaches.Store(item); + Buttons.Clear(); + } + + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs.meta new file mode 100644 index 0000000..bd519eb --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cbc5b08db621303449f60b59f56eac92 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/FloatingOptions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs new file mode 100644 index 0000000..d44113c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs @@ -0,0 +1,37 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + public class ImageButtonData : ButtonData + { + #region Public. + /// + /// Image to display. + /// + public Sprite DisplayImage { get; protected set; } = null; + #endregion + + /// + /// Initializes this for use. + /// + /// Image to use on the button. + /// Text to display on the button. + /// Callback when OnPressed is called. + /// Optional key to include within the callback. + public void Initialize(Sprite sprite, string text, PressedDelegate callback, string key = "") + { + base.Initialize(text, callback, key); + DisplayImage = sprite; + } + + public override void ResetState() + { + base.ResetState(); + DisplayImage = null; + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs.meta new file mode 100644 index 0000000..89cae6e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d60e355b6d09be14487df1ed91e901b2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageButtonData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs new file mode 100644 index 0000000..5e57275 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs @@ -0,0 +1,29 @@ +#if TEXTMESHPRO +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + + public class OptionMenuImageButton : OptionMenuButton + { + #region Serialized. + /// + /// Image component to show image on. + /// + [Tooltip("Image component to show image on.")] + [SerializeField] + private Image _image; + #endregion + + public virtual void Initialize(ImageButtonData buttonData) + { + base.Initialize(buttonData); + _image.sprite = buttonData.DisplayImage; + } + } + + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs.meta new file mode 100644 index 0000000..9b70b5f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2fee1d5cad375e84eaab6a4cd1d6e915 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ImageOptionButton.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs new file mode 100644 index 0000000..5bbc892 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs @@ -0,0 +1,36 @@ +#if TEXTMESHPRO +using TMPro; +using UnityEngine; + + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + + public class OptionMenuButton : MonoBehaviour + { + #region Public. + /// + /// ButtonData for this button. + /// + public ButtonData ButtonData { get; protected set; } + #endregion + + #region Serialized. + /// + /// Text component to show button text. + /// + [Tooltip("Text component to show button text.")] + [SerializeField] + private TextMeshProUGUI _text; + #endregion + + public virtual void Initialize(ButtonData buttonData) + { + ButtonData = buttonData; + _text.text = buttonData.Text; + } + } + + +} +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs.meta new file mode 100644 index 0000000..b2e7627 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 22d4118a529704b49858d02b956dc48d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/OptionButton.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs new file mode 100644 index 0000000..5af7c67 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + /// + /// Gameplay canvases register to this manager. + /// + public class RectTransformResizer : MonoBehaviour + { + #region Types. + public class ResizeData : IResettable + { + public byte Remaining; + public ResizeDelegate Delegate; + + public ResizeData() + { + Remaining = 2; + } + + public void InitializeState() { } + + public void ResetState() + { + Remaining = 2; + Delegate = null; + } + } + #endregion + + #region Public. + /// + /// Delegate for resizing RectTransforms. + /// + /// True if the resize iterations are complete. Typically show your visuals when true. + public delegate void ResizeDelegate(bool complete); + #endregion + + #region Private. + /// + /// Elements to resize. + /// + private List _resizeDatas = new(); + /// + /// Singleton instance of this class. + /// + private static RectTransformResizer _instance; + #endregion + + private void OnDestroy() + { + foreach (ResizeData item in _resizeDatas) + ResettableObjectCaches.Store(item); + } + + private void Update() + { + Resize(); + } + + /// + /// Calls pending resizeDatas. + /// + private void Resize() + { + for (int i = 0; i < _resizeDatas.Count; i++) + { + _resizeDatas[i].Remaining--; + bool complete = (_resizeDatas[i].Remaining == 0); + _resizeDatas[i].Delegate?.Invoke(complete); + if (complete) + { + ResettableObjectCaches.Store(_resizeDatas[i]); + _resizeDatas.RemoveAt(i); + i--; + } + } + + } + + + /// + /// Used to call a delegate twice, over two frames. + /// This is an easy way to resize RectTransforms multiple times as they will often fail after the first resize due to Unity limitations. + /// Note: this work-around may not be required for newer Unity versions. + /// + /// Delegate to invoke when resizing completes. + public static void Resize(ResizeDelegate del) + { + //Check to make a singleton instance. + if (_instance == null) + { + GameObject go = new(typeof(RectTransformResizer).Name); + _instance = go.AddComponent(); + DontDestroyOnLoad(go); + } + + _instance.Resize_Internal(del); + } + private void Resize_Internal(ResizeDelegate del) + { + ResizeData rd = ResettableObjectCaches.Retrieve(); + rd.Delegate = del; + _instance._resizeDatas.Add(rd); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs.meta new file mode 100644 index 0000000..050fef4 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c4b4655d59b39584faed638f43d4d287 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/RectTransformResizer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs new file mode 100644 index 0000000..0072259 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs @@ -0,0 +1,53 @@ +using System.Runtime.CompilerServices; +using UnityEngine; + + +namespace GameKit.Dependencies.Utilities.Types.CanvasContainers +{ + + public class ResizableContainer : FloatingContainer + { + #region Serialized. + /// + /// Minimum and maximum range for widwth and height of the RectTransform. + /// + [Tooltip("Minimum and maximum range for width and height of the RectTransform.")] + //[Foldout("Sizing")] + public FloatRange2D SizeLimits = new() + { + X = new(0f, 999999f), + Y = new(0f, 999999f) + }; + #endregion + + /// + /// Sets a size, and resizes if needed. + /// Other transform values must be set separately using inherited methods. + /// + /// New size to use. + /// True to ignore serialized Size limits. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetSizeAndShow(Vector2 size, bool ignoreSizeLimits = false) + { + ResizeAndShow(size, ignoreSizeLimits); + } + + /// + /// Resizes this canvas. + /// + protected virtual void ResizeAndShow(Vector2 desiredSize, bool ignoreSizeLimits) + { + float widthRequired = desiredSize.x; + float heightRequired = desiredSize.y; + //Clamp width and height. + widthRequired = Mathf.Clamp(widthRequired, SizeLimits.X.Minimum, SizeLimits.X.Maximum); + heightRequired = Mathf.Clamp(heightRequired, SizeLimits.Y.Minimum, SizeLimits.Y.Maximum); + base.RectTransform.sizeDelta = new(widthRequired, heightRequired); + base.Move(); + base.Show(); + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs.meta new file mode 100644 index 0000000..b444b2e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5764e627642cf5643a3f4518d450840e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Canvases/ResizableContainer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs new file mode 100644 index 0000000..dc7d400 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs @@ -0,0 +1,41 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + + public class DDOL : MonoBehaviour + { + #region Public. + /// + /// Created instance of DDOL. + /// + private static DDOL _instance; + #endregion + + /// + /// Returns the current DDOL or creates one if not yet created. + /// + public static DDOL GetDDOL() + { + //Not yet made. + if (_instance == null) + { + GameObject obj = new(); + obj.name = "FirstGearGames DDOL"; + DDOL ddol = obj.AddComponent(); + DontDestroyOnLoad(ddol); + _instance = ddol; + return ddol; + } + //Already made. + else + { + return _instance; + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs.meta new file mode 100644 index 0000000..2d80b55 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 4e0b25628cfd4f241a22a7c0ee2b932f +timeCreated: 1528404670 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/DDOL.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor.meta new file mode 100644 index 0000000..d63a9ac --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a4e1653f734aa924a947707391413c6c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs new file mode 100644 index 0000000..0e23b17 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs @@ -0,0 +1,51 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types.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/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs.meta new file mode 100644 index 0000000..14b7cdb --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c144891e57ef5054d9b7e03b82c00cf2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Editor/SceneDrawer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs new file mode 100644 index 0000000..973f3ed --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs @@ -0,0 +1,40 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + + [System.Serializable] + public struct FloatRange + { + public FloatRange(float minimum, float maximum) + { + Minimum = minimum; + Maximum = maximum; + } + /// + /// Minimum range. + /// + public float Minimum; + /// + /// Maximum range. + /// + public float Maximum; + + /// + /// Returns a random value between Minimum and Maximum. + /// + /// + public float RandomInclusive() + { + return Floats.RandomInclusiveRange(Minimum, Maximum); + } + + public float Lerp(float percent) + { + return Mathf.Lerp(Minimum, Maximum, percent); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs.meta new file mode 100644 index 0000000..06ba97d --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 35d4414338c0ab248a1b838402887ac0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs new file mode 100644 index 0000000..62169ca --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs @@ -0,0 +1,58 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + + [System.Serializable] + + public struct FloatRange2D + { + public FloatRange X; + public FloatRange Y; + + public FloatRange2D(FloatRange x, FloatRange y) + { + X = x; + Y = y; + } + + + public FloatRange2D(float xMin, float xMax, float yMin, float yMax) + { + X = new(xMin, xMax); + Y = new(yMin, yMax); + } + + public Vector2 Clamp(Vector2 original) + { + return new( + ClampX(original.x), + ClampY(original.y) + ); + } + + public Vector3 Clamp(Vector3 original) + { + return new( + ClampX(original.x), + ClampY(original.y), + original.z + ); + } + + public float ClampX(float original) + { + return Mathf.Clamp(original, X.Minimum, X.Maximum); + } + + public float ClampY(float original) + { + return Mathf.Clamp(original, Y.Minimum, Y.Maximum); + } + + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs.meta new file mode 100644 index 0000000..19d2168 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4a2c6d65e69e9154b8f99096ed3096dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/FloatRange2D.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs new file mode 100644 index 0000000..8761eec --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs @@ -0,0 +1,10 @@ +namespace GameKit.Dependencies.Utilities.Types +{ + + public interface IOrderable + { + public int Order { get; } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs.meta new file mode 100644 index 0000000..68accd1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 310411b63c217834297f4b81bda8b175 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IOrderable.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs new file mode 100644 index 0000000..5b688c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs @@ -0,0 +1,55 @@ +namespace GameKit.Dependencies.Utilities.Types +{ + + + [System.Serializable] + public struct IntRange + { + public IntRange(int minimum, int maximum) + { + Minimum = minimum; + Maximum = maximum; + } + /// + /// Minimum range. + /// + public int Minimum; + /// + /// Maximum range. + /// + public int Maximum; + + /// + /// Returns an exclusive random value between Minimum and Maximum. + /// + /// + public float RandomExclusive() + { + return Ints.RandomExclusiveRange(Minimum, Maximum); + } + /// + /// Returns an inclusive random value between Minimum and Maximum. + /// + /// + public float RandomInclusive() + { + return Ints.RandomInclusiveRange(Minimum, Maximum); + } + + /// + /// Returns value clamped within minimum and maximum. + /// + /// + /// + public int Clamp(int value) + { + if (value < Minimum) + return Minimum; + if (value > Maximum) + return Maximum; + return value; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs.meta new file mode 100644 index 0000000..ff6ac46 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 16f03af589a154c47bcbcc3f82b710a6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/IntRange.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling.meta new file mode 100644 index 0000000..188bb36 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 05bf45e6c53189b428920bb4f1f4244d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt new file mode 100644 index 0000000..28a6184 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt @@ -0,0 +1,3 @@ +2020/09/22 + + Added demo scene. + + You can now Store with a delay, which will behave similar to Destroy(obj, delay). \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt.meta new file mode 100644 index 0000000..faa4cfd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: f20abb868e2b92a4d979359d81d97aa0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/CHANGELOG.txt + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo.meta new file mode 100644 index 0000000..90499c8 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d9e1126c6a21b924ebe296adf0d3e282 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs.meta new file mode 100644 index 0000000..be7bb66 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eb1d9debba52ef04999d361f88e67f06 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab new file mode 100644 index 0000000..2c1f9ff --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab @@ -0,0 +1,1616 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &7907540910888487359 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540910888487358} + - component: {fileID: 7907540910888486979} + - component: {fileID: 7907540910888487356} + m_Layer: 0 + m_Name: Cube (5) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540910888487358 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910888487359} + 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: 7907540912251384649} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540910888486979 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910888487359} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540910888487356 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910888487359} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540910936535018 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540910936535017} + - component: {fileID: 7907540910936535022} + - component: {fileID: 7907540910936535023} + 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 &7907540910936535017 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910936535018} + 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: 7907540912251384649} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540910936535022 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910936535018} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540910936535023 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910936535018} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540910941342763 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540910941342762} + - component: {fileID: 7907540910941342767} + - component: {fileID: 7907540910941342760} + 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 &7907540910941342762 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910941342763} + 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: 7907540912251384649} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540910941342767 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910941342763} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540910941342760 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910941342763} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540910993797764 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540910993797771} + - component: {fileID: 7907540910993797768} + - component: {fileID: 7907540910993797769} + m_Layer: 0 + m_Name: Cube (6) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540910993797771 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910993797764} + 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: 7907540912251384649} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540910993797768 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910993797764} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540910993797769 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540910993797764} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540911054708688 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540911054708695} + - component: {fileID: 7907540911054708692} + - component: {fileID: 7907540911054708693} + m_Layer: 0 + m_Name: Cube (10) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540911054708695 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911054708688} + 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: 7907540912251384649} + m_RootOrder: 10 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540911054708692 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911054708688} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540911054708693 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911054708688} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540911066760052 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540911066760059} + - component: {fileID: 7907540911066760056} + - component: {fileID: 7907540911066760057} + m_Layer: 0 + m_Name: Cube (14) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540911066760059 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911066760052} + 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: 7907540912251384649} + m_RootOrder: 14 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540911066760056 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911066760052} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540911066760057 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911066760052} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540911135056709 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540911135056708} + - component: {fileID: 7907540911135056713} + - component: {fileID: 7907540911135056714} + m_Layer: 0 + m_Name: Cube (8) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540911135056708 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911135056709} + 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: 7907540912251384649} + m_RootOrder: 8 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540911135056713 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911135056709} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540911135056714 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911135056709} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540911310580621 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540911310580620} + - component: {fileID: 7907540911310580625} + - component: {fileID: 7907540911310580626} + m_Layer: 0 + m_Name: Cube (15) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540911310580620 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911310580621} + 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: 7907540912251384649} + m_RootOrder: 15 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540911310580625 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911310580621} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540911310580626 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911310580621} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540911355857649 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540911355857648} + - component: {fileID: 7907540911355857653} + - component: {fileID: 7907540911355857654} + m_Layer: 0 + m_Name: Cube (18) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540911355857648 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911355857649} + 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: 7907540912251384649} + m_RootOrder: 18 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540911355857653 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911355857649} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540911355857654 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911355857649} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540911360556844 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540911360556851} + - component: {fileID: 7907540911360556848} + - component: {fileID: 7907540911360556849} + 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 &7907540911360556851 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911360556844} + 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: 7907540912251384649} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540911360556848 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911360556844} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540911360556849 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540911360556844} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912101350235 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912101350234} + - component: {fileID: 7907540912101350239} + - component: {fileID: 7907540912101350232} + m_Layer: 0 + m_Name: Cube (12) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912101350234 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912101350235} + 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: 7907540912251384649} + m_RootOrder: 12 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912101350239 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912101350235} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912101350232 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912101350235} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912162186602 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912162186601} + - component: {fileID: 7907540912162186606} + - component: {fileID: 7907540912162186607} + m_Layer: 0 + m_Name: Cube (7) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912162186601 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912162186602} + 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: 7907540912251384649} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912162186606 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912162186602} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912162186607 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912162186602} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912199241673 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912199241672} + - component: {fileID: 7907540912199241677} + - component: {fileID: 7907540912199241678} + m_Layer: 0 + m_Name: Cube (17) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912199241672 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912199241673} + 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: 7907540912251384649} + m_RootOrder: 17 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912199241677 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912199241673} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912199241678 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912199241673} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912223181973 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912223181972} + - component: {fileID: 7907540912223181977} + - component: {fileID: 7907540912223181978} + m_Layer: 0 + m_Name: Cube (4) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912223181972 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912223181973} + 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: 7907540912251384649} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912223181977 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912223181973} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912223181978 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912223181973} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912239175903 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912239175902} + - component: {fileID: 7907540912239175907} + - component: {fileID: 7907540912239175900} + m_Layer: 0 + m_Name: Cube + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912239175902 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912239175903} + 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: 7907540912251384649} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912239175907 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912239175903} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912239175900 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912239175903} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912251384645 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912251384649} + - component: {fileID: 7907540912251384650} + - component: {fileID: 7907540912251384651} + - component: {fileID: 7907540912251384644} + m_Layer: 0 + m_Name: Projectile + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912251384649 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912251384645} + 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: 7907540912239175902} + - {fileID: 7907540910941342762} + - {fileID: 7907540911360556851} + - {fileID: 7907540910936535017} + - {fileID: 7907540912223181972} + - {fileID: 7907540910888487358} + - {fileID: 7907540910993797771} + - {fileID: 7907540912162186601} + - {fileID: 7907540911135056708} + - {fileID: 7907540912290450599} + - {fileID: 7907540911054708695} + - {fileID: 7907540912591829530} + - {fileID: 7907540912101350234} + - {fileID: 7907540912252377743} + - {fileID: 7907540911066760059} + - {fileID: 7907540911310580620} + - {fileID: 7907540912627847313} + - {fileID: 7907540912199241672} + - {fileID: 7907540911355857648} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912251384650 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912251384645} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912251384651 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912251384645} + 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: 0 + 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!114 &7907540912251384644 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912251384645} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 80e69d14230e52a45bba1fa6f40fd30c, type: 3} + m_Name: + m_EditorClassIdentifier: + DestroyDelay: 3 + MoveRate: 15 +--- !u!1 &7907540912252377736 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912252377743} + - component: {fileID: 7907540912252377740} + - component: {fileID: 7907540912252377741} + m_Layer: 0 + m_Name: Cube (13) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912252377743 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912252377736} + 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: 7907540912251384649} + m_RootOrder: 13 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912252377740 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912252377736} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912252377741 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912252377736} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912290450592 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912290450599} + - component: {fileID: 7907540912290450596} + - component: {fileID: 7907540912290450597} + m_Layer: 0 + m_Name: Cube (9) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912290450599 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912290450592} + 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: 7907540912251384649} + m_RootOrder: 9 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912290450596 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912290450592} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912290450597 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912290450592} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912591829531 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912591829530} + - component: {fileID: 7907540912591829535} + - component: {fileID: 7907540912591829528} + m_Layer: 0 + m_Name: Cube (11) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912591829530 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912591829531} + 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: 7907540912251384649} + m_RootOrder: 11 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912591829535 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912591829531} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912591829528 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912591829531} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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 &7907540912627847314 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 7907540912627847313} + - component: {fileID: 7907540912627847318} + - component: {fileID: 7907540912627847319} + m_Layer: 0 + m_Name: Cube (16) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &7907540912627847313 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912627847314} + 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: 7907540912251384649} + m_RootOrder: 16 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &7907540912627847318 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912627847314} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &7907540912627847319 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 7907540912627847314} + m_Enabled: 1 + m_CastShadows: 3 + m_ReceiveShadows: 0 + 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: 0 + 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/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab.meta new file mode 100644 index 0000000..5995cd3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 6ed8235e90a17434597bd75f8631dfe4 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Prefabs/Projectile.prefab + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes.meta new file mode 100644 index 0000000..1f378f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4777e69010a7d0c4e92e16a1b690014a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity new file mode 100644 index 0000000..9d36985 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity @@ -0,0 +1,398 @@ +%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: 3 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 0 + 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_LightingSettings: {fileID: 4890085278179872738, guid: 531b927ada5215049a609a1d06225375, type: 2} +--- !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 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &1154039407 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1154039409} + - component: {fileID: 1154039408} + m_Layer: 0 + m_Name: ObjectPool + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1154039408 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1154039407} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ab8f6cec624bac24d8fcc6a7a27a07e8, type: 3} + m_Name: + m_EditorClassIdentifier: + _dataExpirationDelay: 60 +--- !u!4 &1154039409 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1154039407} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1433044100 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1433044102} + - component: {fileID: 1433044101} + m_Layer: 0 + m_Name: ProjectileSpawner + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1433044101 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1433044100} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 19304078402371645ac157640b97d523, type: 3} + m_Name: + m_EditorClassIdentifier: + Prefab: {fileID: 7907540912251384645, guid: 6ed8235e90a17434597bd75f8631dfe4, type: 3} + UsePool: 1 + _instantiateDelay: 0.075 +--- !u!4 &1433044102 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1433044100} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: -5.81, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 1832075967} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1459728710 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1459728713} + - component: {fileID: 1459728712} + - component: {fileID: 1459728711} + 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 &1459728711 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1459728710} + m_Enabled: 1 +--- !u!20 &1459728712 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1459728710} + 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: 1 + orthographic size: 8 + 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 &1459728713 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1459728710} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1832075966 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1832075967} + - component: {fileID: 1832075970} + - component: {fileID: 1832075969} + - component: {fileID: 1832075968} + m_Layer: 0 + m_Name: Sphere + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1832075967 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1832075966} + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 1433044102} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!135 &1832075968 +SphereCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1832075966} + 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 &1832075969 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1832075966} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1832075970 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1832075966} + m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0} diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity.meta new file mode 100644 index 0000000..42c5aa9 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 986fe9a85bd2d904fa3d44ac2c7bf0da +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPool.unity + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting new file mode 100644 index 0000000..9878565 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting @@ -0,0 +1,64 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!850595691 &4890085278179872738 +LightingSettings: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: ObjectPoolSettings + serializedVersion: 4 + m_GIWorkflowMode: 1 + m_EnableBakedLightmaps: 0 + m_EnableRealtimeLightmaps: 0 + m_RealtimeEnvironmentLighting: 1 + m_BounceScale: 1 + m_AlbedoBoost: 1 + m_IndirectOutputScale: 1 + m_UsingShadowmask: 1 + m_BakeBackend: 1 + m_LightmapMaxSize: 1024 + m_BakeResolution: 40 + m_Padding: 2 + m_LightmapCompression: 3 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAO: 0 + m_MixedBakeMode: 2 + m_LightmapsBakeMode: 1 + m_FilterMode: 1 + m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_RealtimeResolution: 2 + m_ForceWhiteAlbedo: 0 + m_ForceUpdates: 0 + m_FinalGather: 0 + m_FinalGatherRayCount: 256 + m_FinalGatherFiltering: 1 + m_PVRCulling: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_LightProbeSampleCountMultiplier: 4 + m_PVRBounces: 2 + m_PVRMinBounces: 2 + m_PVREnvironmentMIS: 1 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_PVRTiledBaking: 0 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting.meta new file mode 100644 index 0000000..714853b --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting.meta @@ -0,0 +1,15 @@ +fileFormatVersion: 2 +guid: 531b927ada5215049a609a1d06225375 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 4890085278179872738 + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scenes/ObjectPoolSettings.lighting + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts.meta new file mode 100644 index 0000000..088972d --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 898e607de9d654d40a5fcdd6a6aff11c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs new file mode 100644 index 0000000..21fa75a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs @@ -0,0 +1,87 @@ +#if UNITY_EDITOR +using UnityEditor; +#endif +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.ObjectPooling.Examples +{ + + public class Projectile : MonoBehaviour + { + /// + /// If above 0f projectiles are stored with a delay rather than when off screen. + /// + [Tooltip("If above 0f projectiles are stored with a delay rather than when off screen.")] + [Range(0f, 5f)] + public float DestroyDelay = 0f; + + public float MoveRate = 30f; + + private ProjectileSpawner _spawner; + private MeshRenderer[] _renderers; + private Vector3 _moveDirection; + /// + /// True if existing play mode. + /// + private bool _exitingPlayMode = false; + + private void Awake() + { +#if UNITY_EDITOR + EditorApplication.playModeStateChanged += EditorApplication_playModeStateChanged; +#endif + + + //Used as our pretend overhead. + for (int i = 0; i < 30; i++) + { + _spawner = GameObject.FindObjectOfType(); + _renderers = GetComponentsInChildren(); + } + } + + +#if UNITY_EDITOR + /// + /// Received when editor play mode changes. + /// + /// + private void EditorApplication_playModeStateChanged(PlayModeStateChange obj) + { + if (!EditorApplication.isPlayingOrWillChangePlaymode && EditorApplication.isPlaying) + _exitingPlayMode = true; + } +#endif + + private void OnBecameInvisible() + { + //Don't try to pool if exiting play mode. Doesn't harm anything but creates annoying errors. + if (_exitingPlayMode) + return; + + if (DestroyDelay <= 0f) + { + if (_spawner.UsePool) + { + ObjectPool.Store(gameObject); + } + else + { + Destroy(gameObject); + } + } + } + + private void OnEnable() + { + _moveDirection = new Vector3(Random.Range(-1f, 1f), 1f, 0f).normalized; + if (_spawner.UsePool && DestroyDelay > 0f) + ObjectPool.Store(gameObject, DestroyDelay); + } + + private void Update() + { + transform.position += _moveDirection * MoveRate * Time.deltaTime; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs.meta new file mode 100644 index 0000000..9c9dcd2 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 80e69d14230e52a45bba1fa6f40fd30c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/Projectile.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs new file mode 100644 index 0000000..9b90dd9 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs @@ -0,0 +1,34 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.ObjectPooling.Examples +{ + + public class ProjectileSpawner : MonoBehaviour + { + public GameObject Prefab; + public bool UsePool = true; + + public float _instantiateDelay = 0.075f; + private float _nextInstantiate = 0f; + + // Update is called once per frame + void Update() + { + if (Time.unscaledTime < _nextInstantiate) + return; + + _nextInstantiate = Time.unscaledTime + _instantiateDelay; + + if (UsePool) + { + ObjectPool.Retrieve(Prefab, transform.position, Quaternion.identity); + } + else + { + Instantiate(Prefab, transform.position, Quaternion.identity); + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs.meta new file mode 100644 index 0000000..1b805dd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 19304078402371645ac157640b97d523 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Demo/Scripts/ProjectileSpawner.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt new file mode 100644 index 0000000..58850e7 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt @@ -0,0 +1,18 @@ +IMPORTANT: this project requires the following packages. +- Fundamentals. + + +Fast Object Pool +---------------- + +Setup: + - Add ObjectPool to any object in your scene. + - (Optional) Add ObjectPool under a DontDestroyOnLoad object for it to persist through scene changes. + +Usage: + - Return objects by using ObjectPool.Retrieve(). + - You may return types, such as scripts, by using ObjectPool.Retrieve(). + - Send objects back to the pool using ObjectPool.Store(). You may also store using a delay, like as with Destroy(obj, delay). + +Notes: + - All of Fast Object Pool Retrieve() methods mimic Unity Instantiate() overrides. diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt.meta new file mode 100644 index 0000000..396e85f --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: bc20ec02dbc4fea4aa15d8b2c132de4c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/README.txt + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts.meta new file mode 100644 index 0000000..d3deffc --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f82c3c8b428ad15498a91dbfd2f7ad87 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs new file mode 100644 index 0000000..2fdba05 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs @@ -0,0 +1,153 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.ObjectPooling +{ + + + public class ListStack + { + public ListStack() + { + _lastAccessedTime = Time.time; + } + + #region Public. + /// + /// Number of entries within the stack. + /// + public int Count + { + get { return Entries.Count; } + } + /// + /// Entries within this ListStack. + /// + public List Entries { get; private set; } = new(); + /// + /// Time an entry was added. Indexes will always match up with Entries. + /// + public List EntriesAddedTimes { get; private set; } = new(); + #endregion + + #region Private. + /// + /// Last time this ListStack was pushed or popped. + /// + private float _lastAccessedTime = 0f; + #endregion + + /// + /// Returns if this ListStack has been accessed recently. + /// + /// + /// + public bool AccessedRecently(float threshold) + { + return ((Time.time - _lastAccessedTime) < threshold); + } + + /// + /// Returns a list of GameObjects which were culled from the stack. + /// + /// + /// + public List Cull(float threshold) + { + List results = new(); + float time = Time.time; + + for (int i = 0; i < EntriesAddedTimes.Count; i++) + { + if (time - EntriesAddedTimes[i] > threshold) + results.Add(Entries[i]); + } + + if (results.Count > 0) + { + Entries.RemoveRange(0, results.Count); + EntriesAddedTimes.RemoveRange(0, results.Count); + } + + return results; + } + + /// + /// Push an item to the stack. + /// + /// + public void Push(GameObject item) + { + _lastAccessedTime = Time.time; + Entries.Add(item); + EntriesAddedTimes.Add(_lastAccessedTime); + } + + /// + /// Pop an item from the stack. + /// + /// + public GameObject Pop() + { + _lastAccessedTime = Time.time; + if (Entries.Count > 0) + { + //Return the last entry as it's cheaper than returning the first. + int nextIndex = Entries.Count - 1; + //Set entry then remove from lists. + GameObject entry = Entries[nextIndex]; + Entries.RemoveAt(nextIndex); + EntriesAddedTimes.RemoveAt(nextIndex); + return entry; + } + else + { + return default(GameObject); + } + } + + /// + /// Remove an entry at a specified index. + /// + /// + public void Remove(int index) + { + _lastAccessedTime = Time.time; + Entries.RemoveAt(index); + EntriesAddedTimes.RemoveAt(index); + } + + /// + /// Attempts to remove an item from the entries. + /// + /// + /// True if an item was removed. + public bool Remove(GameObject item) + { + _lastAccessedTime = Time.time; + int index = Entries.IndexOf(item); + if (index == -1) + { + return false; + } + else + { + Entries.RemoveAt(index); + EntriesAddedTimes.RemoveAt(index); + return true; + } + } + + /// + /// Clears the stack; does not destroy items within the stack. + /// + public void Clear() + { + _lastAccessedTime = Time.time; + Entries.Clear(); + EntriesAddedTimes.Clear(); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs.meta new file mode 100644 index 0000000..eb14c6b --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5d88e910cb7436e44bf78505a26d77ed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ListStack.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs new file mode 100644 index 0000000..1676ee0 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs @@ -0,0 +1,633 @@ +using GameKit.Dependencies.Utilities; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.ObjectPooling +{ + + public class ObjectPool : MonoBehaviour + { + #region Types. + /// + /// Data for a delayed store object. + /// + private struct DelayedStoreData + { + public DelayedStoreData(float storeTime, bool parentPooler) + { + StoreTime = storeTime; + ParentPooler = parentPooler; + } + public readonly float StoreTime; + public readonly bool ParentPooler; + } + #endregion + + #region Serialized. + /// + /// Time to wait before destroying object pools with no activity as well pool entries which haven't been used recently. Use -1f to disable this feature. + /// + [Tooltip("Time to wait before destroying object pools with no activity as well pool entries which haven't been used recently. Use -1f to disable this feature.")] + [SerializeField] + private float _dataExpirationDelay = 60f; + #endregion + + #region Private. + /// + /// A singleton instance of this script. + /// + private static ObjectPool _instance; + /// + /// Transform which houses all pooled objects. + /// + private Transform _collector = null; + /// + /// List of all object pools. + /// + private List _pools = new(); + /// + /// Holds transform information for categories of pooled objects. Used to sort pooled objects for easier hierarchy navigation. + /// + private Dictionary _categoryChildren = new(); + /// + /// Stores which PoolData prefabs are using. + /// + private Dictionary _poolPrefabs = new(); + /// + /// Stores which Pooldata retrieved objects belong to. + /// + private Dictionary _activeObjects = new(); + /// + /// Objects which will be stored after a delay. + /// + private Dictionary _delayedStoreObjects = new(); + #endregion + + private void Awake() + { + //Make sure there is only once instance. + if (_instance != null && _instance != this) + { + if (Debug.isDebugBuild) Debug.LogWarning("Multiple ObjectPool scripts found. This script auto loads itself and does not need to be placed in your scenes."); + Destroy(this); + return; + } + else + { + _instance = this; + } + + } + + private void Update() + { + + } + private void Start() + { + StartCoroutine(__CleanupChecks()); + } + + /// + /// Destroys null objects within SpawnedObjects. + /// + private IEnumerator __CleanupChecks() + { + int poolIndex = 0; + + while (true) + { + /* Check which objects must be delayed stored first. + * This is so objects which need to be stored will + * be added to their appropriate pool before it + * potentially gets expired, which would double the work + * because then the pool would get recreated. */ + if (_delayedStoreObjects.Count > 0) + { + List keysToRemove = new(); + foreach (KeyValuePair item in _delayedStoreObjects) + { + if (Time.time >= item.Value.StoreTime) + { + //Add to collection of keys to remove after. + keysToRemove.Add(item.Key); + //Store object. + Store(item.Key, item.Value.ParentPooler); + } + } + //Now store away keys to remove, and get them out of the dictionary. + for (int i = 0; i < keysToRemove.Count; i++) + _delayedStoreObjects.Remove(keysToRemove[i]); + } + + if (_dataExpirationDelay > 0f && _pools.Count > 0) + { + if (poolIndex >= _pools.Count) + poolIndex = 0; + + //If pool hasn't been used in awhile then expire it. + if (_pools[poolIndex].PoolExpired()) + { + _poolPrefabs.Remove(_pools[poolIndex].Prefab); + DestroyPool(_pools[poolIndex], false); + _pools.RemoveAt(poolIndex); + poolIndex--; + } + //Not expired. Try to cull the pool. + else + { + List culledObjects = _pools[poolIndex].Cull(); + for (int i = 0; i < culledObjects.Count; i++) + { + Destroy(culledObjects[i]); + } + } + + poolIndex++; + } + + //Wait a frame. + yield return null; + } + } + + /// + /// Destroys all stored and optionally retrieved objects. Race conditions may be created by this coroutine if trying to retrieve or store objects before it finishes executing. + /// + /// True to also destroy active retrieved gameObjects. False will erase active objects from memory, but not destroy them. + public IEnumerator __Reset(bool destroyActive) + { + /* Clear references to which pool is for each prefab, + * as well to which transform spawned objects child to when stored. */ + _poolPrefabs.Clear(); + _categoryChildren.Clear(); + _pools.Clear(); + /* When resetting clear the delayed store + * objects. If destroyActive is set to true + * then they will be picked up. */ + _delayedStoreObjects.Clear(); + + //Destory all children. + transform.DestroyChildren(false); + + WaitForEndOfFrame endOfFrame = new(); + while (transform.childCount > 0) + yield return endOfFrame; + + //Destroy active objects. + if (destroyActive) + { + //Make a new list to prevent collection from being modified. + List objects = new(); + foreach (KeyValuePair value in _activeObjects) + objects.Add(value.Key); + //Go through collection. + for (int i = 0; i < objects.Count; i++) + { + if (objects[i] != null) + { + Destroy(objects[i]); + while (objects[i] != null) + yield return endOfFrame; + } + } + } + _activeObjects.Clear(); + } + + /// + /// Destroys all Objects within the specified PoolData then clears the PoolData. + /// + /// + /// True to remove from pools list. + private void DestroyPool(PoolData poolData, bool removeFromList) + { + for (int i = 0; i < poolData.Objects.Entries.Count; i++) + { + if (poolData.Objects.Entries[i] != null) + Destroy(poolData.Objects.Entries[i]); + } + + if (removeFromList) + _pools.Remove(poolData); + } + + /// + /// Returns the PoolData which houses the desired prefab. + /// + /// Prefab to return a pool for. + /// + private PoolData ReturnPoolData(GameObject prefab) + { + PoolData result; + _poolPrefabs.TryGetValue(prefab, out result); + + if (result == null) + result = CreatePool(prefab); + + return result; + } + + /// + /// Sets the position and rotation of the specified GameObject. + /// + /// + /// + /// + private void SetGameObjectPositionRotation(GameObject result, Vector3 position, Quaternion rotation) + { + result.transform.position = position; + result.transform.rotation = rotation; + } + + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// + public static GameObject Retrieve(GameObject poolObject) + { + return _instance.RetrieveInternal(poolObject); + } + private GameObject RetrieveInternal(GameObject poolObject) + { + PoolData pool; + GameObject result = ReturnPooledObject(poolObject, out pool); + + //If pool isn't null then reset result. + if (result != null && pool != null) + { + SetGameObjectPositionRotation(result, pool.Prefab.transform.position, pool.Prefab.transform.rotation); + result.transform.SetParent(null); + } + + return FinalizeRetrieve(result, pool); + } + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// Parent to attach the retrieved prefab to. + /// Use true when assigning a parent Object to maintain the world position of the Object, instead of setting its position relative to the new parent. Pass false to set the Object's position relative to its new parent. + /// + public static GameObject Retrieve(GameObject poolObject, Transform parent, bool instantiateInWorldSpace = true) + { + return _instance.RetrieveInternal(poolObject, parent, instantiateInWorldSpace); + } + public GameObject RetrieveInternal(GameObject poolObject, Transform parent, bool instantiateInWorldSpace = true) + { + PoolData pool; + GameObject result = ReturnPooledObject(poolObject, out pool); + + //If pool isn't null then reset result. + if (result != null && pool != null) + { + SetGameObjectPositionRotation(result, pool.Prefab.transform.position, pool.Prefab.transform.rotation); + result.transform.SetParent(parent, instantiateInWorldSpace); + } + + return FinalizeRetrieve(result, pool); + } + + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// Position for the retrieved object. + /// Orientation for the retrieved object. + /// + public static GameObject Retrieve(GameObject poolObject, Vector3 position, Quaternion rotation) + { + return _instance.RetrieveInternal(poolObject, position, rotation); + } + private GameObject RetrieveInternal(GameObject poolObject, Vector3 position, Quaternion rotation) + { + PoolData pool; + GameObject result = ReturnPooledObject(poolObject, out pool); + + //If pool isn't null then reset result. + if (result != null) + { + SetGameObjectPositionRotation(result, position, rotation); + result.transform.SetParent(null); + } + + return FinalizeRetrieve(result, pool); + } + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// Position for the retrieved object. + /// Orientation for the retrieved object. + /// Transform to parent the retrieved object to. + /// + public GameObject Retrieve(GameObject poolObject, Vector3 position, Quaternion rotation, Transform parent) + { + PoolData pool; + GameObject result = ReturnPooledObject(poolObject, out pool); + + //If pool isn't null then reset result. + if (result != null) + { + SetGameObjectPositionRotation(result, position, rotation); + result.transform.SetParent(parent, true); + } + + return FinalizeRetrieve(result, pool); + } + + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// + public static T Retrieve(GameObject prefab) + { + return _instance.RetrieveInternal(prefab); + } + private T RetrieveInternal(GameObject prefab) + { + PoolData pool; + GameObject result = ReturnPooledObject(prefab, out pool); + + //If pool isn't null then reset result. + if (result != null && pool != null) + { + SetGameObjectPositionRotation(result, pool.Prefab.transform.position, pool.Prefab.transform.rotation); + result.transform.SetParent(null); + } + + return FinalizeRetrieve(result, pool).GetComponent(); + } + + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// Parent to attach the retrieved prefab to. + /// Use true when assigning a parent Object to maintain the world position of the Object, instead of setting its position relative to the new parent. Pass false to set the Object's position relative to its new parent. + /// + public static T Retrieve(GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) + { + return _instance.RetrieveInternal(prefab, parent, instantiateInWorldSpace); + } + private T RetrieveInternal(GameObject prefab, Transform parent, bool instantiateInWorldSpace = true) + { + PoolData pool; + GameObject result = ReturnPooledObject(prefab, out pool); + + //If pool isn't null then reset result. + if (result != null && pool != null) + { + SetGameObjectPositionRotation(result, pool.Prefab.transform.position, pool.Prefab.transform.rotation); + result.transform.SetParent(parent, instantiateInWorldSpace); + } + + return FinalizeRetrieve(result, pool).GetComponent(); + } + + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// Position for the retrieved object. + /// Orientation for the retrieved object. + /// + public static T Retrieve(GameObject prefab, Vector3 position, Quaternion rotation) + { + return _instance.RetrieveInternal(prefab, position, rotation); + } + private T RetrieveInternal(GameObject prefab, Vector3 position, Quaternion rotation) + { + PoolData pool; + GameObject result = ReturnPooledObject(prefab, out pool); + + //If pool isn't null then reset result. + if (result != null) + { + SetGameObjectPositionRotation(result, position, rotation); + result.transform.SetParent(null); + } + + return FinalizeRetrieve(result, pool).GetComponent(); + } + /// + /// Returns a pooled object of the specified prefab. + /// + /// Prefab to retrieve. + /// Position for the retrieved object. + /// Orientation for the retrieved object. + /// Transform to parent the retrieved object to. + /// + public static T Retrieve(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) + { + return _instance.RetrieveInternal(prefab, position, rotation, parent); + } + private T RetrieveInternal(GameObject prefab, Vector3 position, Quaternion rotation, Transform parent) + { + PoolData pool; + GameObject result = ReturnPooledObject(prefab, out pool); + + //If pool isn't null then reset result. + if (result != null) + { + SetGameObjectPositionRotation(result, position, rotation); + result.transform.SetParent(parent, true); + } + + return FinalizeRetrieve(result, pool).GetComponent(); + } + + /// + /// Finalizes the retrieved object by performing additional reset actions before returning git. + /// + /// + /// + /// + private GameObject FinalizeRetrieve(GameObject result, PoolData pool) + { + _activeObjects[result] = pool; + //Set the item active based on if the prefab is. + if (pool != null) + result.SetActive(pool.Prefab.activeSelf); + + return result; + } + + /// + /// Returns an object to it's pool. + /// + /// + /// True to set the objects parent as the ObjectPooler. + public static void Store(GameObject instantiatedObject, float delay, bool parentPooler = true) + { + + _instance.StoreInternal(instantiatedObject, delay, parentPooler); + } + private void StoreInternal(GameObject instantiatedObject, float delay, bool parentPooler = true) + { + _delayedStoreObjects[instantiatedObject] = new(Time.time + delay, parentPooler); + } + /// + /// Returns an object to it's pool. + /// + /// + /// True to set the objects parent as the ObjectPooler. + public static void Store(GameObject instantiatedObject, bool parentPooler = true) + { + _instance.StoreInternal(instantiatedObject, parentPooler); + } + private void StoreInternal(GameObject instantiatedObject, bool parentPooler = true) + { + //If passed in pool object is null. + if (instantiatedObject == null) + { + Debug.LogWarning("ObjectPooler -> StoreObject -> poolObject cannot be null."); + return; + } + + PoolData pool; + /* Try to get the pool from the dictionary lookup. + * If not found then create a new pool. */ + if (_activeObjects.TryGetValue(instantiatedObject, out pool)) + _activeObjects.Remove(instantiatedObject); + else + pool = ReturnPoolData(instantiatedObject); + + AddToPool(instantiatedObject, pool, parentPooler); + } + + /// + /// Makes and assigns the collector transform. + /// + private void MakeCollector() + { + if (_collector == null) + { + /* If collect is null it's safe to assume it's children were destroyed + * in which case clear dictionary used for categories. */ + _categoryChildren.Clear(); + //Make a new collector. + _collector = new GameObject().transform; + _collector.name = "ObjectPoolerCollector"; + } + } + + + /// + /// Returns a poolobject without performing any operations on it. + /// + /// + /// Pool the returned GameObject came from. + private GameObject ReturnPooledObject(GameObject prefab, out PoolData pool) + { + //If passed in prefab is null. + if (prefab == null) + { + pool = null; + Debug.LogError("ObjectPooler -> RetrieveObject -> prefab cannot be null."); + return null; + } + + pool = ReturnPoolData(prefab); + + GameObject result = pool.Objects.Pop(); + /* If a null object was returned then instantiate a new object and + * use it as the result. Doing so bypasses the need to check pool + * count, as well handles conditions in which the object may have + * been destroyed or the pool has become corrupt. In the chance the pool was + * corrupt or an object destroyed it will progressively clean itself with every Pop(). */ + if (result == null) + result = Instantiate(prefab); + + return result; + } + + /// + /// Creates a pool of the specified prefab with one entry and adds it to the pools list. + /// + /// + /// Returns the created PoolData. + private PoolData CreatePool(GameObject prefab) + { + if (prefab == null) + { + Debug.LogError("ObjectPooler -> CreatePool -> prefab cannot be null."); + return null; + } + + //Make a new pool. + PoolData pool = new(prefab, _dataExpirationDelay); + + /* Check if the poolObject has a name in the scene. + * If it does then the object is spawned. If not + * it must be a prefab. */ + if (prefab.scene.name != null) + AddToPool(prefab, pool, true); + + //Add new pool to list then return it. + _pools.Add(pool); + _poolPrefabs[prefab] = pool; + + return pool; + } + + /// + /// Adds an object to a specified pool. + /// + /// + /// + /// True to set the objects parent as the ObjectPooler. + private void AddToPool(GameObject instantiatedObject, PoolData pool, bool parentPooler = true) + { + if (instantiatedObject == null) + { + Debug.LogError("ObjectPooler -> AddToPool -> instantiatedObject is null."); + return; + } + if (pool == null) + { + Debug.LogError("ObjectPooler -> AddToPool -> pool is null."); + return; + } + + instantiatedObject.SetActive(false); + + pool.Objects.Push(instantiatedObject); + + if (parentPooler) + ParentPooler(instantiatedObject, true); + } + + /// + /// Sets the parent of the specified object to the proper child within the ObjectPooler. + /// + /// + private void ParentPooler(GameObject poolObject, bool worldPositionStays) + { + MakeCollector(); + + Transform newParent; + string tag = poolObject.tag; + /* Try to set parent from dictionary. If not found then make a new + * child object attached to the object pooler and add to dictionary, + * then use created value. */ + if (!_categoryChildren.TryGetValue(tag, out newParent)) + { + newParent = new GameObject().transform; + newParent.name = tag; + //Set category transform to collector. + newParent.SetParent(_collector); + _categoryChildren[tag] = newParent; + } + + poolObject.transform.SetParent(newParent, worldPositionStays); + } + + } + + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs.meta new file mode 100644 index 0000000..5037d08 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ab8f6cec624bac24d8fcc6a7a27a07e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/ObjectPool.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs new file mode 100644 index 0000000..0e13173 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using UnityEngine; + + +namespace GameKit.Dependencies.Utilities.ObjectPooling +{ + + public class PoolData + { + public PoolData(GameObject prefab, float expirationDuration) + { + Prefab = prefab; + _expirationDuration = expirationDuration; + } + + #region Public. + /// + /// Prefab for this pool. + /// + public readonly GameObject Prefab = null; + /// + /// Objects currently in the pool. + /// + public ListStack Objects = new(); + #endregion + + #region Private. + /// + /// Time this pool must remain idle before it's considered expired. + /// + private float _expirationDuration = -1f; + #endregion + + /// + /// Returns if the pool has expired due to inactivity. + /// + /// + /// + public bool PoolExpired() + { + if (_expirationDuration == -1f) + return false; + + return !Objects.AccessedRecently(_expirationDuration); + } + + /// + /// Returns a list of GameObjects which were culled from the stack using the default expiration duration. + /// + /// + /// + public List Cull() + { + if (_expirationDuration == -1f) + return new(); + + return Objects.Cull(_expirationDuration); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs.meta new file mode 100644 index 0000000..ff3d0a1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: f8b47752cc2784843b70d83bf47de090 +timeCreated: 1522106622 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ObjectPooling/Scripts/PoolData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs new file mode 100644 index 0000000..ffaafbd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs @@ -0,0 +1,31 @@ +using UnityEngine; +using UnityEngine.EventSystems; + +namespace GameKit.Dependencies.Utilities +{ + public abstract class PointerMonoBehaviour : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler + { + /// + /// Called when the pointer enters this objects rect transform. + /// + public void OnPointerEnter(PointerEventData eventData) => OnHovered(true, eventData); + + /// + /// Called when the pointer exits this objects rect transform. + /// + public void OnPointerExit(PointerEventData eventData) => OnHovered(false, eventData); + + /// + /// Called when the pointer presses this objects rect transform. + /// + public void OnPointerDown(PointerEventData eventData) => OnPressed(true, eventData); + + /// + /// Called when the pointer releases this objects rect transform. + /// + public void OnPointerUp(PointerEventData eventData) => OnPressed(false, eventData); + + public virtual void OnHovered(bool hovered, PointerEventData eventData) { } + public virtual void OnPressed(bool pressed, PointerEventData eventData) { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs.meta new file mode 100644 index 0000000..2d3fc8c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f26032017027b804c8da0163e267675d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/PointerMonoBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs new file mode 100644 index 0000000..42f5d1b --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs @@ -0,0 +1,572 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + /// + /// Writes values to a collection of a set size, overwriting old values as needed. + /// + public class ResettableRingBuffer : IResettable, IEnumerable where T : IResettable + { + #region Types. + /// + /// Custom enumerator to prevent garbage collection. + /// + 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 = _enumeratedRingBuffer.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 ResettableRingBuffer _enumeratedRingBuffer; + /// + /// Collection to iterate. + /// + private T[] _collection; + /// + /// Number of entries read during the enumeration. + /// + private int _entriesEnumerated; + /// + /// Number of entries read during the enumeration. + /// + private int _read; + /// + /// Start index of enumerations. + /// + private int _startIndex; + /// + /// True if currently enumerating. + /// + private bool _enumerating => (_enumeratedRingBuffer != null); + /// + /// Count of the collection during initialization. + /// + private int _initializeCollectionCount; + #endregion + + public void Initialize(ResettableRingBuffer c) + { + //if none are written then return. + if (c.Count == 0) + return; + + _entriesEnumerated = 0; + _startIndex = c.GetRealIndex(0); + _enumeratedRingBuffer = c; + _collection = c.Collection; + _initializeCollectionCount = c.Count; + Current = default; + } + + public bool MoveNext() + { + if (!_enumerating) + return false; + + int written = _enumeratedRingBuffer.Count; + + if (written != _initializeCollectionCount) + { + Debug.LogError($"{_enumeratedRingBuffer.GetType().Name} collection was modified during enumeration."); + //This will force a return/reset. + _entriesEnumerated = written; + } + + if (_entriesEnumerated >= written) + { + Reset(); + return false; + } + + int index = (_startIndex + _entriesEnumerated); + int capacity = _enumeratedRingBuffer.Capacity; + if (index >= capacity) + index -= capacity; + Current = _collection[index]; + + _entriesEnumerated++; + + return true; + } + + /// + /// Resets read count. + /// + public void Reset() + { + /* Only need to reset value types. + * Numeric types change during initialization. */ + _enumeratedRingBuffer = default; + _collection = default; + Current = default; + } + + 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 allowed to be used within collection. + /// Collection length may be larger than this value due to re-using larger collections. + /// + public int Capacity; + /// + /// 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; + /// + /// True if written is at capacity. + /// + private bool _atCapacity => (_written == Capacity); + #endregion + + + #region Consts. + /// + /// Default capacity when none is psecified. + /// + public const int DEFAULT_CAPACITY = 60; + #endregion + + public ResettableRingBuffer() + { + Initialize(DEFAULT_CAPACITY); + } + + /// + /// 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) + { + UnityEngine.Debug.LogError($"Collection length must be larger than 0."); + return; + } + + //If already initialized then resetstate first. + if (Initialized) + ResetState(); + + if (Collection == null) + { + GetNewCollection(); + } + else if (Collection.Length < capacity) + { + ArrayPool.Shared.Return(Collection); + GetNewCollection(); + } + + Capacity = capacity; + Initialized = true; + + void GetNewCollection() => Collection = ArrayPool.Shared.Rent(capacity); + + } + + /// + /// Initializes with default capacity. + /// + /// True to log automatic initialization. + public void Initialize() + { + if (!Initialized) + { + UnityEngine.Debug.Log($"RingBuffer for type {typeof(T).FullName} is being initialized with a default capacity of {DEFAULT_CAPACITY}."); + Initialize(DEFAULT_CAPACITY); + } + } + + + /// + /// Clears the collection to default values and resets indexing. + /// + public void Clear() + { + if (Collection != null) + { + for (int i = 0; i < Capacity; i++) + { + if (i < _written) + Collection[i].ResetState(); + Collection[i] = default; + } + } + + _written = 0; + WriteIndex = 0; + _enumerator.Reset(); + } + /// + /// Resets the collection without clearing. + /// + [Obsolete("This method no longer functions. Use Clear() instead.")] //Remove on 2024/06/01. + public void Reset() { } + + + /// + /// Inserts an entry into the collection. + /// This is can be an expensive operation on larger buffers. + /// + /// Simulated index to return. A value of 0 would return the first simulated index in the collection. + /// Data to insert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Insert(int simulatedIndex, T data) + { + if (!IsInitializedWithError()) + return; + + int realIndex = GetRealIndex(simulatedIndex); + if (realIndex == -1) + return; + + int written = _written; + //If adding to the end. + if (simulatedIndex == (written - 1)) + { + Add(data); + return; + } + + bool atCapacity = _atCapacity; + int lastSimulatedIndex = (written == Capacity) ? (written - 1) : written; + + //If at capacity then reset the last entry since it will be dropped off. + if (atCapacity) + Collection[GetRealIndex(lastSimulatedIndex)].ResetState(); + + while (lastSimulatedIndex > simulatedIndex) + { + int lRealIndex = GetRealIndex(lastSimulatedIndex, true); + int lPrevRealIndex = GetRealIndex(lastSimulatedIndex - 1); + Collection[lRealIndex] = Collection[lPrevRealIndex]; + lastSimulatedIndex--; + } + + Collection[realIndex] = data; + //If written was not maxed out then increase it. + if (!atCapacity) + IncreaseWritten(); + } + + /// + /// Adds an entry to the collection. + /// + /// Data to add. + /// True to reset state of the entry being replaced. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Add(T data, bool resetState = true) + { + Initialize(); + + T current = Collection[WriteIndex]; + //If current has data then reset it. + if (_atCapacity && resetState) + current.ResetState(); + + Collection[WriteIndex] = data; + IncreaseWritten(); + + return current; + } + + + /// + /// Returns the first entry and removes it from the buffer. + /// + /// + public T Dequeue() + { + if (_written == 0) + return default; + + int offset = GetRealIndex(0); + T result = Collection[offset]; + + RemoveRange(fromStart: true, 1, resetRemoved: false); + return result; + } + + + /// + /// Returns if able to dequeue an entry and removes it from the buffer if so. + /// + /// + public bool TryDequeue(out T result) + { + if (_written == 0) + { + result = default; + return false; + } + + int offset = GetRealIndex(0); + result = Collection[offset]; + + RemoveRange(fromStart: true, 1, resetRemoved: false); + return true; + } + + /// + /// Adds an entry to the collection, returning a replaced entry. + /// This method internally redirects to add. + /// + public T Enqueue(T data) => Add(data); + + + /// + /// 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 = GetRealIndex(simulatedIndex); + if (offset >= 0) + return Collection[offset]; + else + return default; + } + set + { + int offset = GetRealIndex(simulatedIndex); + if (offset >= 0) + Collection[offset] = value; + } + } + + + /// + /// Increases written count and handles offset changes. + /// + private void IncreaseWritten() + { + int capacity = Capacity; + + 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; + } + + + /// + /// Returns the real index of the collection using a simulated index. + /// + /// True to allow an index be returned from an unused portion of the buffer so long as it is within bounds. + private int GetRealIndex(int simulatedIndex, bool allowUnusedBuffer = false) + { + if (simulatedIndex >= Capacity) + { + return ReturnError(); + } + else + { + int written = _written; + //May be out of bounds if allowUnusedBuffer is false. + if (simulatedIndex >= written) + { + if (!allowUnusedBuffer) + return ReturnError(); + } + int offset = (Capacity - written) + simulatedIndex + WriteIndex; + if (offset >= Capacity) + offset -= Capacity; + + return offset; + } + + int ReturnError() + { + UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Collection count is {_written}, Capacity is {Capacity}"); + return -1; + } + } + + /// + /// 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, bool resetRemoved = true) + { + if (length == 0) + return; + if (length < 0) + { + UnityEngine.Debug.LogError($"Negative values cannot be removed."); + return; + } + //Full reset if value is at or more than written. + if (length >= _written) + { + Clear(); + return; + } + + //Reset state on removed. + if (resetRemoved) + { + //Resetting from the beginning of the collection. + if (fromStart) + { + for (int i = 0; i < length; i++) + { + int index = GetRealIndex(i); + Collection[index].ResetState(); + } + } + //Resetting from the end. + else + { + for (int i = 0; i < length; i++) + { + /* If written is 5 the last entry index + * would be 4. So if i == 0 the simulated + * index would be... (5 - (0 + 1)) = 4, the + * last entry. */ + /* Another example, if there were 3 total written + * and all 3 were being removed then the simulated + * indexes in order would be: + * 2, 1, 0. + * (3 - (0 + 1)) = 2. + * (2 - (0 + 1)) = 1. + * (1 - (0 + 1)) = 0. + * */ + int simulatedIndex = _written - (i + 1); + int index = GetRealIndex(simulatedIndex); + Collection[index].ResetState(); + } + } + } + + _written -= length; + if (fromStart) + { + //No steps are needed from start other than reduce written, which is done above. + } + else + { + WriteIndex -= length; + if (WriteIndex < 0) + WriteIndex += Capacity; + } + } + + /// + /// Returns if initialized and errors if not. + /// + /// + private bool IsInitializedWithError() + { + if (!Initialized) + { + UnityEngine.Debug.LogError($"RingBuffer has not yet been initialized."); + return false; + } + + return true; + } + + /// + /// Resets values when being placed in a cache. + /// + public void ResetState() + { + Clear(); + if (Collection != null) + { + ArrayPool.Shared.Return(Collection); + Collection = null; + } + Initialized = false; + } + + public void InitializeState() { } + + + /// + /// Returns Enumerator for the collection. + /// + /// + public Enumerator GetEnumerator() + { + Initialize(); + _enumerator.Initialize(this); + return _enumerator; + } + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // Collection.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); // Collection.GetEnumerator(); + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs.meta new file mode 100644 index 0000000..48df075 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2fa03073e536c6e4db989416ee03c6e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ResettableRingBuffer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs new file mode 100644 index 0000000..b4174b3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs @@ -0,0 +1,468 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + /// + /// Writes values to a collection of a set size, overwriting old values as needed. + /// + public class RingBuffer : IEnumerable + { + #region Types. + /// + /// Custom enumerator to prevent garbage collection. + /// + public struct Enumerator : IEnumerator + { + #region Public. + /// + /// Current entry in the enumerator. + /// + public T Current { get; private set; } + #endregion + + #region Private. + /// + /// RollingCollection to use. + /// + private RingBuffer _enumeratedRingBuffer; + /// + /// Collection to iterate. + /// + private T[] _collection; + /// + /// Number of entries read during the enumeration. + /// + private int _entriesEnumerated; + /// + /// Start index of enumerations. + /// + private int _startIndex; + /// + /// True if currently enumerating. + /// + private bool _enumerating => (_enumeratedRingBuffer != null); + /// + /// Count of the collection during initialization. + /// + private int _initializeCollectionCount; + #endregion + + public void Initialize(RingBuffer c) + { + //if none are written then return. + if (c.Count == 0) + return; + + _entriesEnumerated = 0; + _startIndex = c.GetRealIndex(0); + _enumeratedRingBuffer = c; + _collection = c.Collection; + _initializeCollectionCount = c.Count; + Current = default; + } + + public bool MoveNext() + { + if (!_enumerating) + return false; + + int written = _enumeratedRingBuffer.Count; + + if (written != _initializeCollectionCount) + { + Debug.LogError($"{_enumeratedRingBuffer.GetType().Name} collection was modified during enumeration."); + //This will force a return/reset. + _entriesEnumerated = written; + } + + if (_entriesEnumerated >= written) + { + Reset(); + return false; + } + + int index = (_startIndex + _entriesEnumerated); + int capacity = _enumeratedRingBuffer.Capacity; + if (index >= capacity) + index -= capacity; + Current = _collection[index]; + + _entriesEnumerated++; + + return true; + } + + /// + /// Resets read count. + /// + public void Reset() + { + /* Only need to reset value types. + * Numeric types change during initialization. */ + _enumeratedRingBuffer = default; + _collection = default; + Current = default; + } + + 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 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 + + #region Consts. + /// + /// Default capacity when none is psecified. + /// + public const int DEFAULT_CAPACITY = 60; + #endregion + + /// + /// Initializes with default capacity. + /// + public RingBuffer() + { + Initialize(DEFAULT_CAPACITY); + } + + /// + /// Initializes with a set capacity. + /// + /// Size to initialize the collection as. This cannot be changed after initialized. + public RingBuffer(int capacity) + { + Initialize(capacity); + } + + /// + /// 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) + { + UnityEngine.Debug.LogError($"Collection length must be larger than 0."); + return; + } + + if (Collection == null) + { + GetNewCollection(); + } + else if (Collection.Length < capacity) + { + Clear(); + ArrayPool.Shared.Return(Collection); + GetNewCollection(); + } + else + { + Clear(); + } + + Capacity = capacity; + Initialized = true; + + void GetNewCollection() => Collection = ArrayPool.Shared.Rent(capacity); + } + + /// + /// Initializes with default capacity. + /// + /// True to log automatic initialization. + public void Initialize() + { + if (!Initialized) + { + UnityEngine.Debug.Log($"RingBuffer for type {typeof(T).FullName} is being initialized with a default capacity of {DEFAULT_CAPACITY}."); + Initialize(DEFAULT_CAPACITY); + } + } + + /// + /// Clears the collection to default values and resets indexing. + /// + public void Clear() + { + for (int i = 0; i < Capacity; i++) + Collection[i] = default; + + _written = 0; + WriteIndex = 0; + _enumerator.Reset(); + } + + /// + /// Resets the collection without clearing. + /// + [Obsolete("This method no longer functions. Use Clear() instead.")] //Remove on V5 + public void Reset() { } + + /// + /// Inserts an entry into the collection. + /// This is can be an expensive operation on larger buffers. + /// + /// Simulated index to return. A value of 0 would return the first simulated index in the collection. + /// Data to insert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T Insert(int simulatedIndex, T data) + { + Initialize(); + + int written = _written; + //If simulatedIndex is 0 and none are written then add. + if (simulatedIndex == 0 && written == 0) + return Add(data); + + int realIndex = GetRealIndex(simulatedIndex); + if (realIndex == -1) + return default; + + + //If adding to the end or none written. + if (simulatedIndex == (written - 1)) + return Add(data); + + int lastSimulatedIndex = (written == Capacity) ? (written - 1) : written; + + while (lastSimulatedIndex > simulatedIndex) + { + int lRealIndex = GetRealIndex(lastSimulatedIndex, true); + int lPrevRealIndex = GetRealIndex(lastSimulatedIndex - 1); + Collection[lRealIndex] = Collection[lPrevRealIndex]; + lastSimulatedIndex--; + } + + T prev = Collection[realIndex]; + Collection[realIndex] = data; + //If written was not maxed out then increase it. + if (written < Capacity) + IncreaseWritten(); + + return prev; + } + + /// + /// 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) + { + Initialize(); + + T current = Collection[WriteIndex]; + + Collection[WriteIndex] = data; + IncreaseWritten(); + + return current; + } + + + /// + /// Returns the first entry and removes it from the buffer. + /// + /// + public T Dequeue() + { + if (_written == 0) + return default; + + int offset = GetRealIndex(0); + T result = Collection[offset]; + + RemoveRange(fromStart: true, 1); + return result; + } + + /// + /// Returns if able to dequeue an entry and removes it from the buffer if so. + /// + /// + public bool TryDequeue(out T result) + { + if (_written == 0) + { + result = default; + return false; + } + + int offset = GetRealIndex(0); + result = Collection[offset]; + + RemoveRange(fromStart: true, 1); + return true; + } + + /// + /// Adds an entry to the collection, returning a replaced entry. + /// This method internally redirects to add. + /// + public T Enqueue(T data) => Add(data); + + /// + /// 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 = GetRealIndex(simulatedIndex); + if (offset >= 0) + return Collection[offset]; + else + return default; + } + set + { + int offset = GetRealIndex(simulatedIndex); + if (offset >= 0) + Collection[offset] = value; + } + } + + /// + /// Increases written count and handles offset changes. + /// + private void IncreaseWritten() + { + int capacity = Capacity; + + 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; + } + + /// + /// Returns the real index of the collection using a simulated index. + /// + /// True to allow an index be returned from an unused portion of the buffer so long as it is within bounds. + private int GetRealIndex(int simulatedIndex, bool allowUnusedBuffer = false) + { + if (simulatedIndex >= Capacity) + { + return ReturnError(); + } + else + { + int written = _written; + //May be out of bounds if allowUnusedBuffer is false. + if (simulatedIndex >= written) + { + if (!allowUnusedBuffer) + return ReturnError(); + } + int offset = (Capacity - written) + simulatedIndex + WriteIndex; + if (offset >= Capacity) + offset -= Capacity; + + return offset; + } + + int ReturnError() + { + UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Written count is {_written}, Capacity is {Capacity}"); + return -1; + } + } + + /// + /// 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) + { + UnityEngine.Debug.LogError($"Negative values cannot be removed."); + return; + } + //Full reset if value is at or more than written. + if (length >= _written) + { + Clear(); + return; + } + + _written -= length; + if (fromStart) + { + //No steps are needed from start other than reduce written, which is done above. + } + else + { + WriteIndex -= length; + if (WriteIndex < 0) + WriteIndex += Capacity; + } + } + + /// + /// Returns Enumerator for the collection. + /// + /// + public Enumerator GetEnumerator() + { + Initialize(); + _enumerator.Initialize(this); + return _enumerator; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs.meta new file mode 100644 index 0000000..dc494e8 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4398caa06d4585440822752ab3424be0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/RingBuffer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs new file mode 100644 index 0000000..6057ef1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs @@ -0,0 +1,12 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + /* 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/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs.meta new file mode 100644 index 0000000..b4a37d5 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 39fdd420f2fa10548af8c39d451d8a21 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SceneAttribute.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs new file mode 100644 index 0000000..328a4bd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs @@ -0,0 +1,64 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace GameKit.Dependencies.Utilities.Types +{ + + + /// + /// Forces a scroolbar to a value over multiple frames. + /// Often scrollbars will not stay at the right value when a recttransform is redrawn; this solves that problem. + /// + public class ScrollbarValueSetter + { + /// + /// Scrollbar to fix. + /// + private Scrollbar _scrollBar; + /// + /// Value to set scrollbar at. + /// + private float _value; + /// + /// Frame when value was updated. + /// + private int _updatedFrame = -1; + /// + /// Number of frames to wait before fixing. + /// + private int _fixFrames; + + public ScrollbarValueSetter(Scrollbar sb, int fixFrames = 2) + { + _scrollBar = sb; + _fixFrames = fixFrames; + } + + /// + /// Sets value of the scrollbar. + /// + /// + public void SetValue(float value) + { + _scrollBar.value = value; + _value = value; + _updatedFrame = Time.frameCount; + } + + /// + /// Checks to fix scrollbar value. Should be called every frame. + /// + public void LateUpdate() + { + if (_updatedFrame == -1) + return; + if ((Time.frameCount - _updatedFrame) < _fixFrames) + return; + + _updatedFrame = -1; + _scrollBar.value = _value; + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs.meta new file mode 100644 index 0000000..9d43be3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3cb592f43ed49de44bf7d5407b88391f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/ScrollbarValueSetter.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs new file mode 100644 index 0000000..cce3a6c --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs @@ -0,0 +1,41 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + /// + /// Creates a singleton instance of a scriptable object. + /// + /// + public abstract class SingletonScriptableObject : ScriptableObject where T : ScriptableObject + { + private static T _instance = null; + public static T Instance + { + get + { + if (_instance == null) + { + T[] results = UnityEngine.Resources.FindObjectsOfTypeAll(); + if (results.Length == 0) + { + Debug.LogError("SingletonScriptableObject: results length is 0 of " + typeof(T).ToString()); + return null; + } + if (results.Length > 1) + { + Debug.LogError("SingletonScriptableObject: results length is greater than 1 of " + typeof(T).ToString()); + return null; + } + + _instance = results[0]; + _instance.hideFlags = HideFlags.DontUnloadUnusedAsset; + } + + return _instance; + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs.meta new file mode 100644 index 0000000..8793b49 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bef14acaff843864185c13ac82c05995 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SingletonScriptableObject.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs new file mode 100644 index 0000000..2460ba3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs @@ -0,0 +1,43 @@ +using System; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + /// + /// Moves smoothly to transform changes over ticks giving cameras something to follow. + /// + public class SmoothCameraTarget : MonoBehaviour + { + #region Public. + /// + /// Created instance of DDOL. + /// + private static DDOL _instance; + #endregion + + /// + /// Returns the current DDOL or creates one if not yet created. + /// + public static DDOL GetDDOL() + { + //Not yet made. + if (_instance == null) + { + GameObject obj = new(); + obj.name = "FirstGearGames DDOL"; + DDOL ddol = obj.AddComponent(); + DontDestroyOnLoad(ddol); + _instance = ddol; + return ddol; + } + //Already made. + else + { + return _instance; + } + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs.meta new file mode 100644 index 0000000..7eb31ab --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: ebb1599afe9a562408745db70647972f +timeCreated: 1528404670 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/SmoothCameraTarget.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs new file mode 100644 index 0000000..4e6875d --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + /// + /// Limits how often an operation can be performed through TryUseOperation. + /// + public class TimedOperation + { + /// + /// + /// + /// Interval frequency to allow operations. + /// True to compare against scaled time. + public TimedOperation(float interval, bool scaledTime = false) + { + _interval = interval; + _scaledTime = scaledTime; + } + + #region Private. + /// + /// How much time must past between each operation. + /// + private readonly float _interval; + /// + /// True to use scaled time. + /// + private readonly bool _scaledTime; + /// + /// Last times specific key operations were performed. + /// + private Dictionary _operationTimes = new(); + /// + /// Last time a global operation was performed. + /// + private float _lastGlobalTime = 0f; + #endregion + + /// + /// Returns if can perform operation at the configured interval. + /// + /// + public bool TryUseOperation() + { + float time = (_scaledTime) ? Time.time : Time.unscaledTime; + + //If enough time has passed. + if (time - _lastGlobalTime >= _interval) + { + _lastGlobalTime = time + _interval; + return true; + } + //Not enough time passed. + else + { + return false; + } + } + + /// + /// Returns if can perform operation at the configured interval for the specified key. + /// + /// + /// + public bool TryUseOperation(string key) + { + float time = (_scaledTime) ? Time.time : Time.unscaledTime; + + float result; + //Key already exist. + if (_operationTimes.TryGetValue(key, out result)) + { + //If enough time has passed. + if (time - result >= _interval) + { + _operationTimes[key] = time + _interval; + return true; + } + //Not enough time passed. + else + { + return false; + } + } + //Key not yet added. + else + { + _operationTimes[key] = time + _interval; + return true; + } + } + + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs.meta new file mode 100644 index 0000000..e72ee98 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a8ab7253f5bacb94798c4cf15852a66b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/TimedOperation.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs new file mode 100644 index 0000000..97d0df7 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs @@ -0,0 +1,49 @@ + +namespace GameKit.Dependencies.Utilities.Types +{ + + + [System.Serializable] + public struct UIntRange + { + public UIntRange(uint minimum, uint maximum) + { + Minimum = minimum; + Maximum = maximum; + } + /// + /// Minimum range. + /// + public uint Minimum; + /// + /// Maximum range. + /// + public uint Maximum; + + /// + /// Returns an exclusive random value between Minimum and Maximum. + /// + /// + public uint RandomExclusive() + { + return UInts.RandomExclusiveRange(Minimum, Maximum); + } + /// + /// Returns an inclusive random value between Minimum and Maximum. + /// + /// + public uint RandomInclusive() => UInts.RandomInclusiveRange(Minimum, Maximum); + + /// + /// Clamps value between Minimum and Maximum. + /// + public uint Clamp(uint value) => UInts.Clamp(value, Minimum, Maximum); + + /// + /// True if value is within range of Minimum and Maximum. + /// + public bool InRange(uint value) => (value >= Minimum) && (value <= Maximum); + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs.meta new file mode 100644 index 0000000..b1f4f8a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f67169fcc3392b849939473e4e86b6f4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/UIntRange.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs new file mode 100644 index 0000000..9995a25 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs @@ -0,0 +1,43 @@ +using UnityEngine; + +namespace GameKit.Dependencies.Utilities.Types +{ + + + [System.Serializable] + public struct Vector2Range + { + public Vector2Range(Vector2 minimum, Vector2 maximum) + { + X = new(minimum.x, maximum.x); + Y = new(minimum.y, maximum.y); + } + public Vector2Range(FloatRange minimum, FloatRange maximum) + { + X = minimum; + Y = maximum; + } + /// + /// Minimum range. + /// + public FloatRange X; + /// + /// Maximum range. + /// + public FloatRange Y; + + /// + /// Returns a random value between Minimum and Maximum. + /// + /// + public Vector2 RandomInclusive() + { + return new( + Floats.RandomInclusiveRange(X.Minimum, X.Maximum), + Floats.RandomInclusiveRange(Y.Minimum, Y.Maximum) + ); + } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs.meta new file mode 100644 index 0000000..58d7b57 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 1b1d5cb52def18c46a0c1e71b5f1245f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Types/Vector2Range.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs new file mode 100644 index 0000000..78ab80a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs @@ -0,0 +1,69 @@ + +using System.Runtime.CompilerServices; + +namespace GameKit.Dependencies.Utilities +{ + + /// + /// Various utility classes relating to floats. + /// + public static class UInts + { + /// + /// Pads an index a specified value. Preferred over typical padding so that pad values used with skins can be easily found in the code. + /// + public static string Pad(this uint value, int padding) + { + if (padding < 0) + padding = 0; + return value.ToString().PadLeft(padding, '0'); + } + /// + /// Provides a random inclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Inclusive maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RandomInclusiveRange(uint minimum, uint maximum) => (uint)Ints.RandomInclusiveRange((int)minimum, (int)maximum); + /// + /// Provides a random exclusive int within a given range. Preferred over Unity's Random to eliminate confusion as Unity uses inclusive for floats max, and exclusive for int max. + /// + /// Inclusive minimum value. + /// Exclusive maximum value. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint RandomExclusiveRange(uint minimum, uint maximum) => (uint)Ints.RandomExclusiveRange((int)minimum, (int)maximum); + + /// + /// Returns a clamped int within a specified range. + /// + /// Value to clamp. + /// Minimum value. + /// Maximum value. + /// + public static uint Clamp(uint value, uint minimum, uint maximum) + { + if (value < minimum) + value = minimum; + else if (value > maximum) + value = maximum; + + return value; + } + + /// + /// Returns whichever value is lower. + /// + public static uint Min(uint a, uint b) => (a < b) ? a : b; + + /// + /// Determins if all values passed in are the same. + /// + /// Values to check. + /// True if all values are the same. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ValuesMatch(params uint[] values) => Ints.ValuesMatch((int[])(object)values); + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs.meta new file mode 100644 index 0000000..b9b7bdc --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 9d1ed50ac02fb974c8260d36bf2abfdc +timeCreated: 1527268448 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/UInts.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs new file mode 100644 index 0000000..b20b87a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs @@ -0,0 +1,251 @@ +using System; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + + public static class Vectors + { + /// + /// Vector3.zero. + /// + private static readonly Vector3 VECTOR3_ZERO = new(0.0f, 0.0f, 0.0f); + /// + /// Float epislon. + /// + private const float FLOAT_EPSILON = 0.00001f; + + #region Vector3. + /// + /// Returns how fast an object must move over duration to reach goal. + /// + /// Vector3 to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// + public static float GetRate(this Vector3 a, Vector3 b, float duration, out float distance, uint interval = 1) + { + distance = Vector3.Distance(a, b); + return distance / (duration * interval); + } + /// + /// Adds a Vector2 X/Y onto a Vector3. + /// + public static Vector3 Add(this Vector3 v3, Vector2 v2) + { + return (v3 + new Vector3(v2.x, v2.y, 0f)); + } + /// + /// Subtracts a Vector2 X/Y from a Vector3. + /// + public static Vector3 Subtract(this Vector3 v3, Vector2 v2) + { + return (v3 - new Vector3(v2.x, v2.y, 0f)); + } + /// + /// Calculates the linear parameter t that produces the interpolant value within the range [a, b]. + /// + /// + /// + /// + /// + public static float InverseLerp(Vector3 a, Vector3 b, Vector3 value) + { + Vector3 ab = b - a; + Vector3 av = value - a; + return Mathf.Clamp01(Vector3.Dot(av, ab) / Vector3.Dot(ab, ab)); + } + + /// + /// Returns if the target Vector3 is within variance of the source Vector3. + /// + /// Source vector. + /// Target vector. + /// How close the target vector must be to be considered close. + /// + public static bool Near(this Vector3 a, Vector3 b, float tolerance = 0.01f) + { + return (Vector3.Distance(a, b) <= tolerance); + } + + /// + /// Returns if any values within a Vector3 are NaN. + /// + /// + /// + public static bool IsNan(this Vector3 source) + { + return (float.IsNaN(source.x) || float.IsNaN(source.y) || float.IsNaN(source.z)); + } + + /// + /// Lerp between three Vector3 values. + /// + /// + public static Vector3 Lerp3(Vector3 a, Vector3 b, Vector3 c, float percent) + { + Vector3 r0 = Vector3.Lerp(a, b, percent); + Vector3 r1 = Vector3.Lerp(b, c, percent); + return Vector3.Lerp(r0, r1, percent); + } + + /// + /// Lerp between three Vector3 values. + /// + /// + /// + /// + public static Vector3 Lerp3(Vector3[] vectors, float percent) + { + if (vectors.Length < 3) + { + Debug.LogWarning("Vectors -> Lerp3 -> Vectors length must be 3."); + return Vector3.zero; + } + + return Lerp3(vectors[0], vectors[1], vectors[2], percent); + } + + /// + /// Multiplies a Vector3 by another. + /// + /// + /// + /// + public static Vector3 Multiply(this Vector3 src, Vector3 multiplier) + { + return new(src.x * multiplier.x, src.y * multiplier.y, src.z * multiplier.z); + } + + #region Fast. + /* Fast checks are property of: + * Copyright (c) 2020 Maxim Munnig Schmidt + * + * 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. + */ + /// + /// Fast Distance. + /// + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float FastDistance(Vector3 a, Vector3 b) + { + var distx = a.x - b.x; + var disty = a.y - b.y; + var distz = a.z - b.z; + return (float)Math.Sqrt(distx * distx + disty * disty + distz * distz); + } + /// + /// Fast SqrMagnitude. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float FastSqrMagnitude(Vector3 vector) + { + return vector.x * vector.x + vector.y * vector.y + vector.z * vector.z; + } + /// + /// Fast Normalize. + /// + /// + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector3 FastNormalize(Vector3 value) + { + float mag = (float)Math.Sqrt(value.x * value.x + value.y * value.y + value.z * value.z); //Magnitude(value); + if (mag > FLOAT_EPSILON) + { + Vector3 result; + result.x = value.x / mag; + result.y = value.y / mag; + result.z = value.z / mag; + return result;// value / mag; + } + else + return VECTOR3_ZERO; + } + #endregion + #endregion + + #region Vector2. + /// + /// Returns how fast an object must move over duration to reach goal. + /// + /// Vector3 to measure distance against. + /// How long it should take to move to goal. + /// A multiplier applied towards interval. Typically this is used for ticks passed. + /// + public static float GetRate(this Vector2 a, Vector2 goal, float duration, out float distance, uint interval = 1) + { + distance = Vector2.Distance(a, goal); + return distance / (duration * interval); + } + + /// + /// Lerp between three Vector2 values. + /// + /// + /// + /// + /// + /// + public static Vector2 Lerp3(Vector2 a, Vector2 b, Vector2 c, float percent) + { + Vector2 r0 = Vector2.Lerp(a, b, percent); + Vector2 r1 = Vector2.Lerp(b, c, percent); + return Vector2.Lerp(r0, r1, percent); + } + + /// + /// Lerp between three Vector2 values. + /// + /// + /// + /// + public static Vector2 Lerp2(Vector2[] vectors, float percent) + { + if (vectors.Length < 3) + { + Debug.LogWarning("Vectors -> Lerp3 -> Vectors length must be 3."); + return Vector2.zero; + } + + return Lerp3(vectors[0], vectors[1], vectors[2], percent); + } + + + /// + /// Multiplies a Vector2 by another. + /// + /// + /// + /// + public static Vector2 Multiply(this Vector2 src, Vector2 multiplier) + { + return new(src.x * multiplier.x, src.y * multiplier.y); + } + #endregion + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs.meta new file mode 100644 index 0000000..f71bed1 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs.meta @@ -0,0 +1,20 @@ +fileFormatVersion: 2 +guid: 6bf25d6d9fa7a754b85a60006db774c4 +timeCreated: 1522043602 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/Vectors.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs new file mode 100644 index 0000000..32dc6a6 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs @@ -0,0 +1,95 @@ +using GameKit.Dependencies.Utilities.Types; +using System.Collections.Generic; +using UnityEngine; + +namespace GameKit.Dependencies.Utilities +{ + public interface IWeighted + { + float GetWeight(); + ByteRange GetQuantity(); + } + + public static class WeightedRandom + { + /// + /// Gets random entries by weight. + /// + /// Entries to pull from. + /// Number of entries to get. + /// Results of entries. Key is the entry, Value is the number of drops. + /// True to allow the same entry to be included within results more than once. + public static void GetEntries(List source, IntRange countRange, ref Dictionary results, bool allowRepeatingDrops = false) where T : IWeighted + { + if (source == null || source.Count == 0) + { + Debug.Log($"Source list of type {typeof(T).Name} cannot be null or empty."); + return; + } + + int count = Ints.RandomInclusiveRange(countRange.Minimum, countRange.Maximum); + //If to not return any then exit early. + if (count == 0) + return; + + //Number of times each item has dropped. + Dictionary dropCount = CollectionCaches.RetrieveDictionary(); + + //Get the total weight. + float totalWeight = 0f; + for (int i = 0; i < source.Count; i++) + totalWeight += source[i].GetWeight(); + + //Make a copy of source to not modify source. + List sourceCopy = CollectionCaches.RetrieveList(); + foreach (T item in source) + sourceCopy.Add(item); + + while (results.Count < count) + { + int startCount = results.Count; + /* Reset copy to totalWeight. + * totalWeight will be modified if + * a non-repeatable item is pulled. */ + float tWeightCopy = totalWeight; + float rnd = UnityEngine.Random.Range(0f, totalWeight); + + for (int i = 0; i < sourceCopy.Count; i++) + { + T item = sourceCopy[i]; + float weight = item.GetWeight(); + + if (rnd <= weight) + { + //Try to get current count. + results.TryGetValueIL2CPP(item, out uint currentCount); + //Set new vlaue. + results[item] = (currentCount + 1); + /* If cannot stay in collection then remove it + * from copy and remove its weight + * from total. */ + if (!allowRepeatingDrops) + { + sourceCopy.RemoveAt(i); + totalWeight -= weight; + } + break; + } + else + { + tWeightCopy -= weight; + } + } + + /* If nothing was added to results then + * something went wrong. */ + if (results.Count == startCount) + break; + } + + CollectionCaches.Store(dropCount); + + } + } + +} diff --git a/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs.meta b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs.meta new file mode 100644 index 0000000..d1fc1c6 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: daf7f2aaf711e2046a1996b15c58d0a7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/GameKit/Dependencies/Utilities/WeightedRandom.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/Yak.meta b/Assets/FishNet/Runtime/Plugins/Yak.meta new file mode 100644 index 0000000..ae7de97 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: db8731a93272f9a48b7fc3d035e88bfa +folderAsset: yes +DefaultImporter: + 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..01542f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8bb6f9b7646a3004493b32151b5572ab +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..75740c3 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs @@ -0,0 +1,57 @@ +using FishNet.Transporting.Yak.Server; +using System; +using System.Collections.Generic; + +namespace FishNet.Transporting.Yak.Client +{ + /// + /// Creates a fake client connection to interact with the ServerSocket. + /// + public class ClientSocket : CommonSocket + { + #region Private. + /// + /// Socket for the server. + /// + private ServerSocket _server; + /// + /// Incomimg data. + /// + private Queue _incoming = new(); + #endregion + + + + /// + /// Starts the client connection. + /// + internal bool StartConnection() + { + + return true; + } + + + + /// + /// Stops the local socket. + /// + internal bool StopConnection() + { + + return true; + } + + + + + + + + #region Local server. + + #endregion + + + } +} \ No newline at end of file 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..08b25c6 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 26269fe7187f5da4e957080519ea0f13 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/Yak/Core/ClientSocket.cs + uploadId: 762203 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..0cb5e5a --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; + +namespace FishNet.Transporting.Yak +{ + + public abstract class CommonSocket + { + + #region Public. + /// + /// Current ConnectionState. + /// + private LocalConnectionState _connectionState = LocalConnectionState.Stopped; + /// + /// Returns the current ConnectionState. + /// + /// + internal LocalConnectionState GetLocalConnectionState() + { + return _connectionState; + } + + + #endregion + + #region Protected. + /// + /// Transport controlling this socket. + /// + protected Transport Transport = null; + #endregion + + /// + /// Initializes this for use. + /// + internal virtual void Initialize(Transport t, CommonSocket socket) + { + Transport = t; + } + + /// + /// Clears a queue. + /// + internal void ClearQueue(ref Queue queue) + { + + } + } + +} 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..a7f59ae --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 63afa30fa0251df44b9496aded55d795 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/Yak/Core/CommonSocket.cs + uploadId: 762203 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..9059f08 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs @@ -0,0 +1,11 @@ +using FishNet.Utility.Performance; +using System; + +namespace FishNet.Transporting.Yak +{ + internal struct LocalPacket + { + + } + +} 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..d741ccd --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f52ce359669f91c4d981dc605a8875b7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/Yak/Core/LocalPacket.cs + uploadId: 762203 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..d9a8c43 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs @@ -0,0 +1,83 @@ +using FishNet.Connection; +using FishNet.Transporting.Yak.Client; +using System; +using System.Collections.Generic; + +namespace FishNet.Transporting.Yak.Server +{ + /// + /// Creates a fake socket acting as 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) + { + if (connectionId != NetworkConnection.SIMULATED_CLIENTID_VALUE) + return RemoteConnectionState.Stopped; + + LocalConnectionState state = _client.GetLocalConnectionState(); + return (state == LocalConnectionState.Started) ? RemoteConnectionState.Started : + RemoteConnectionState.Stopped; + } + #endregion + + #region Private. + /// + /// Packets received from local client. + /// + private Queue _incoming = new(); + /// + /// Socket for client. + /// + private ClientSocket _client; + #endregion + + + + /// + /// Starts the server. + /// + internal bool StartConnection() + { + + return true; + } + + + + + /// + /// Stops the local socket. + /// + internal bool StopConnection() + { + + return true; + } + + /// + /// Stops a remote client from the server, disconnecting the client. + /// + /// ConnectionId of the client to disconnect. + internal bool StopConnection(int connectionId) + { + + return true; + } + + + + + + #region Local client. + + + + #endregion + } +} \ No newline at end of file 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..d827826 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9181ba25449c96446b966d0bd62e5813 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/Yak/Core/ServerSocket.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs b/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs new file mode 100644 index 0000000..cb01f7e --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs @@ -0,0 +1,317 @@ +using FishNet.Connection; +using FishNet.Managing; +using System; +using UnityEngine; + +namespace FishNet.Transporting.Yak +{ + [AddComponentMenu("FishNet/Transport/Yak")] + public class Yak : Transport + { + #region Private. + /// + /// Client when acting as host. + /// + private Client.ClientSocket _client; + /// + /// Server for the transport. + /// + private Server.ServerSocket _server; + #endregion + + #region Const. + /// + /// Maximum packet size for this transport. + /// + private const int MTU = 5000; + #endregion + + public override void Initialize(NetworkManager networkManager, int transportIndex) + { + + } + + private void OnDestroy() + { + + } + + #region ConnectionStates. + /// + /// Gets the IP address of a remote connection Id. + /// + /// + /// + public override string GetConnectionAddress(int connectionId) + { + return String.Empty; + } +#pragma warning disable CS0067 + /// + /// 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; +#pragma warning restore CS0067 + /// + /// Gets the current local ConnectionState. + /// + /// True if getting ConnectionState for the server. + public override LocalConnectionState GetConnectionState(bool server) + { + + return LocalConnectionState.Stopped; + } + /// + /// Gets the current ConnectionState of a remote client on the server. + /// + /// ConnectionId to get ConnectionState for. + public override RemoteConnectionState GetConnectionState(int connectionId) + { + return (_server == null) ? RemoteConnectionState.Stopped : _server.GetConnectionState(connectionId); + } + /// + /// Handles a ConnectionStateArgs for the local client. + /// + /// + public override void HandleClientConnectionState(ClientConnectionStateArgs connectionStateArgs) + { + + } + /// + /// Handles a ConnectionStateArgs for the local server. + /// + /// + public override void HandleServerConnectionState(ServerConnectionStateArgs connectionStateArgs) + { + + } + /// + /// Handles a ConnectionStateArgs for a remote client. + /// + /// + public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs) + { + + } + #endregion + + #region Iterating. + /// + /// Processes data received by the socket. + /// + /// True to process data received on the server. + public override void IterateIncoming(bool server) + { + + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to process data received on the server. + public override void IterateOutgoing(bool server) { } + #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. + public override void SendToServer(byte channelId, ArraySegment segment) + { + + } + /// + /// Sends data to a client. + /// + /// + /// + /// + public override void SendToClient(byte channelId, ArraySegment segment, int connectionId) + { + + } + #endregion + + #region Configuration. + /// + /// Returns if the transport is a local transport. + /// While true several security checks are disabled. + /// + public override bool IsLocalTransport(int connectionId) => true; + /// + /// 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 NetworkConnection.MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE; + } + /// + /// 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) { } + /// + /// Sets which address the client will connect to. + /// + /// + public override void SetClientAddress(string address) { } + /// + /// Sets which address the server will bind to. + /// + /// + public override void SetServerBindAddress(string address, IPAddressType addressType) { } + /// + /// Sets which port to use. + /// + /// + public override void SetPort(ushort 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(); + } + + /// + /// 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 stp the client socket without waiting socket thread. + public override bool StopConnection(int connectionId, bool immediately) + { + return StopClient(connectionId, immediately); + } + + /// + /// Stops both client and server. + /// + public override void Shutdown() + { + + } + + #region Privates. + /// + /// Starts server. + /// + /// True if there were no blocks. A true response does not promise a socket will or has connected. + private bool StartServer() + { + + + return (_server == null) ? false : _server.StartConnection(); + } + + /// + /// Stops server. + /// + private bool StopServer() + { + return (_server == null) ? false : _server.StopConnection(); + } + + /// + /// Starts the client. + /// + /// + /// True if there were no blocks. A true response does not promise a socket will or has connected. + private bool StartClient() + { + + return true; + } + + /// + /// Stops the client. + /// + private bool StopClient() + { + + return 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) + { + return (_server == null) ? false : _server.StopConnection(connectionId); + } + #endregion + #endregion + + #region Channels. + /// + /// 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 MTU; + } + #endregion + } + +} \ No newline at end of file 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..0b22144 --- /dev/null +++ b/Assets/FishNet/Runtime/Plugins/Yak/Yak.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 91f4cf5273666764789e6f8bada05e3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Plugins/Yak/Yak.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing.meta b/Assets/FishNet/Runtime/Serializing.meta new file mode 100644 index 0000000..1bf1442 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2a60afc166377a04f91b256be2d2e3fb +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..6b5dfa1 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/AutoPackType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2e94ebaa8f7024845a7e90ebd8246ac6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/AutoPackType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs b/Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs new file mode 100644 index 0000000..13327f5 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs @@ -0,0 +1,16 @@ +namespace FishNet.Serializing +{ + [System.Flags] + public enum DeltaSerializerOption : ulong + { + Unset = 0, + FullSerialize = 1, + RootSerialize = 2, + } + + public static class DeltaSerializerOptionExtensions + { + public static bool FastContains(this DeltaSerializerOption whole, DeltaSerializerOption part) => (whole & part) == part; + + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs.meta b/Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs.meta new file mode 100644 index 0000000..fd32fd1 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 66c970b59e4d53249bfed364df2cf1db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/DeltaSerializerOption.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/DeltaTypes.cs b/Assets/FishNet/Runtime/Serializing/DeltaTypes.cs new file mode 100644 index 0000000..714a116 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/DeltaTypes.cs @@ -0,0 +1,186 @@ +using System.Runtime.CompilerServices; + +namespace FishNet.Serializing +{ + /// + /// This is for internal use and may change at any time. + /// + [System.Flags] + public enum DeltaVector2Type : byte + { + /// + /// This is unused. + /// + Unset = 0, + /// + /// Contains X as 1 byte. + /// + XUInt8 = 1, + /// + /// Contains X as 2 bytes. + /// + XUInt16 = 2, + /// + /// Contains X as 4 bytes. + /// + XUInt32 = 4, + /// + /// Contains Z as 1 byte. + /// + YUInt8 = 8, + /// + /// Contains Z as 2 bytes. + /// + YUInt16 = 16, + /// + /// Contains Z as 4 bytes. + /// + YUInt32 = 32, + /// + /// Contains Y as 1 byte. + /// + XNextIsLarger = 64, + /// + /// Contains Y as 4 bytes. + /// + YNextIsLarger = 128, + } + + /// + /// This is for internal use and may change at any time. + /// + [System.Flags] + public enum DeltaVector3Type : ushort + { + /// + /// This is unused. + /// + Unset = 0, + /// + /// Contains X as 1 byte. + /// + XInt8 = 1, + /// + /// Contains X as 2 bytes. + /// + XInt16 = 2, + /// + /// Contains X as 4 bytes. + /// + XInt32 = 4, + /// + /// Contains Z as 1 byte. + /// + ZInt8 = 8, + /// + /// Contains Z as 2 bytes. + /// + ZInt16 = 16, + /// + /// Contains Z as 4 bytes. + /// + ZInt32 = 32, + /// + /// Contains Y as 1 byte. + /// + YInt8 = 64, + /// + /// Contains Y as 2 bytes. + /// + YInt32 = 128, + } + + [System.Flags] + internal enum DeltaWholeType : byte + { + /// + /// Indicates there is no compression. This can also be used to initialize the enum. + /// + Unset = 0, + /// + /// Data is written as a byte. + /// + UInt8 = 1, + /// + /// Data is written as a ushort. + /// + UInt16 = 2, + /// + /// Data is written as a uint. + /// + UInt32 = 4, + /// + /// Data is written as a ulong. + /// + UInt64 = 8, + /// + /// data is written as two ulong. + /// + UInt128 = 16, + /// + /// When set this indicates the new value is larger than the previous. + /// When not set, indicates new value is smaller than the previous. + /// + NextValueIsLarger = 32, + } + + /// + /// This is for internal use and may change at any time. + /// + [System.Flags] + public enum UDeltaPrecisionType : byte + { + /// + /// Indicates there is no compression. This can also be used to initialize the enum. + /// + Unset = 0, + /// + /// Data is written as a byte. + /// + UInt8 = 1, + /// + /// Data is written as a ushort. + /// + UInt16 = 2, + /// + /// Data is written as a uint. + /// + UInt32 = 4, + /// + /// Data is written as a ulong. + /// + UInt64 = 8, + /// + /// data is written as two ulong. + /// + UInt128 = 16, + /// + /// When set this indicates the new value is larger than the previous. + /// When not set, indicates new value is smaller than the previous. + /// + NextValueIsLarger = 128, + } + + /// + /// This is for internal use and may change at any time. + /// + public static class DeltaTypeExtensions + { + public static bool FastContains(this UDeltaPrecisionType whole, UDeltaPrecisionType part) => (whole & part) == part; + + public static bool FastContains(this UDeltaPrecisionType whole, UDeltaPrecisionType part, int shift) => FastContains((int)whole, (int)part, shift); + + public static bool FastContains(this DeltaVector3Type whole, DeltaVector3Type part) => (whole & part) == part; + + public static bool FastContains(this DeltaVector3Type whole, DeltaVector3Type part, int shift) => FastContains((int)whole, (int)part, shift); + + public static bool FastContains(this DeltaVector2Type whole, DeltaVector2Type part) => (whole & part) == part; + + private static bool FastContains(int whole, int part, int shift) + { + int intPart = part >> shift; + return (whole & intPart) == intPart; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/DeltaTypes.cs.meta b/Assets/FishNet/Runtime/Serializing/DeltaTypes.cs.meta new file mode 100644 index 0000000..6f22124 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/DeltaTypes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d9187bce6e160143aa8f742a5f6e0e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/DeltaTypes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs b/Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs new file mode 100644 index 0000000..778dcd8 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs @@ -0,0 +1,42 @@ +using FishNet.Documenting; +using FishNet.Utility; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +namespace FishNet.Serializing +{ + + /// + /// Used to read generic types. + /// + [APIExclude] + public static class GenericDeltaReader + { + public static Func Read { get; internal set; } + /// + /// True if this type has a custom writer. + /// + internal static bool HasCustomSerializer; + + public static void SetRead(Func value) + { + /* If a custom serializer has already been set then exit method + * to not overwrite serializer. */ + if (HasCustomSerializer) + return; + + bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedReaderPrefix); + /* If generated then see if a regular custom writer exists. If so + * then do not set a serializer to a generated one. */ + //TODO Make it so DefaultDeltaReader methods are picked up by codegen. + if (isGenerated && GenericReader.HasCustomSerializer) + return; + + //Set has custom serializer if value being used is not a generated method. + HasCustomSerializer = !isGenerated; + Read = value; + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs.meta b/Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs.meta new file mode 100644 index 0000000..35879ed --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 4024d774dc58c64409f498ad4f406ce9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/GenericDeltaReader.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs b/Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs new file mode 100644 index 0000000..d0a330b --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs @@ -0,0 +1,43 @@ +using FishNet.Documenting; +using FishNet.Serializing; +using FishNet.Utility; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +namespace FishNet.Serializing +{ + + /// + /// Used to write generic types. + /// + [APIExclude] + public static class GenericDeltaWriter + { + public static Func Write { get; internal set; } + /// + /// True if this type has a custom writer. + /// + internal static bool HasCustomSerializer; + + public static void SetWrite(Func value) + { + /* If a custom serializer has already been set then exit method + * to not overwrite serializer. */ + if (HasCustomSerializer) + return; + + bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedWriterPrefix); + /* If generated then see if a regular custom writer exists. If so + * then do not set a serializer to a generated one. */ + //TODO Make it so DefaultDeltaWriter methods are picked up by codegen. + if (isGenerated && GenericWriter.HasCustomSerializer) + return; + + //Set has custom serializer if value being used is not a generated method. + HasCustomSerializer = !isGenerated; + Write = value; + } + } + +} diff --git a/Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs.meta b/Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs.meta new file mode 100644 index 0000000..795ba5a --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7f5527790c602a447b437079b9262a90 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/GenericDeltaWriter.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/GenericReader.cs b/Assets/FishNet/Runtime/Serializing/GenericReader.cs new file mode 100644 index 0000000..5942050 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericReader.cs @@ -0,0 +1,42 @@ +using FishNet.Documenting; +using FishNet.Utility; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +//Required for internal tests. +[assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)] +namespace FishNet.Serializing +{ + /// + /// Used to read generic types. + /// + [APIExclude] + public static class GenericReader + { + public static Func Read { get; set; } + /// + /// True if this type has a custom writer. + /// + internal static bool HasCustomSerializer; + + public static void SetRead(Func value) + { + /* If a custom serializer has already been set then exit method + * to not overwrite serializer. */ + if (HasCustomSerializer) + return; + + bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedReaderPrefix); + + //If not generated then unset any generated delta serializer. + if (!isGenerated && GenericDeltaReader.HasCustomSerializer) + GenericDeltaReader.Read = null; + + //Set has custom serializer if value being used is not a generated method. + HasCustomSerializer = !isGenerated; + Read = value; + } + + } +} diff --git a/Assets/FishNet/Runtime/Serializing/GenericReader.cs.meta b/Assets/FishNet/Runtime/Serializing/GenericReader.cs.meta new file mode 100644 index 0000000..408a726 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2a02167b94bcdd84da90bbcf5d6ad649 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/GenericReader.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/GenericWriter.cs b/Assets/FishNet/Runtime/Serializing/GenericWriter.cs new file mode 100644 index 0000000..8900b7a --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericWriter.cs @@ -0,0 +1,40 @@ +using FishNet.Documenting; +using FishNet.Utility; +using System; +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] + +namespace FishNet.Serializing +{ + /// + /// Used to write generic types. + /// + [APIExclude] + public static class GenericWriter + { + public static Action Write { get; private set; } + /// + /// True if this type has a custom writer. + /// + internal static bool HasCustomSerializer; + + public static void SetWrite(Action value) + { + /* If a custom serializer has already been set then exit method + * to not overwrite serializer. */ + if (HasCustomSerializer) + return; + + bool isGenerated = value.Method.Name.StartsWith(UtilityConstants.GeneratedWriterPrefix); + + //If not generated then unset any generated delta serializer. + if (!isGenerated && GenericDeltaWriter.HasCustomSerializer) + GenericDeltaWriter.Write = null; + + //Set has custom serializer if value being used is not a generated method. + HasCustomSerializer = !isGenerated; + Write = value; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/GenericWriter.cs.meta b/Assets/FishNet/Runtime/Serializing/GenericWriter.cs.meta new file mode 100644 index 0000000..f1d4e7e --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/GenericWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9c79ac203808e16489cbc2072fad75db +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/GenericWriter.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping.meta b/Assets/FishNet/Runtime/Serializing/Helping.meta new file mode 100644 index 0000000..3701fa0 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 47d64f495dba6cf4da8615f022f5a801 +folderAsset: yes +DefaultImporter: + externalObjects: {} + 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..b6261ed --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs @@ -0,0 +1,223 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Serializing; +using FishNet.Transporting; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; + +namespace FishNet.Broadcast.Helping +{ + + internal static class BroadcastsSerializers + { + /// + /// Writes a broadcast to writer. + /// + internal static PooledWriter WriteBroadcast(NetworkManager networkManager, PooledWriter writer, T message, ref Channel channel) + { + writer.WritePacketIdUnpacked(PacketId.Broadcast); + writer.WriteUInt16(typeof(T).FullName.GetStableHashU16()); + //Write data to a new writer. + PooledWriter dataWriter = WriterPool.Retrieve(); + dataWriter.Write(message); + //Write length of data. + writer.WriteInt32(dataWriter.Length); + //Write data. + writer.WriteArraySegment(dataWriter.GetArraySegment()); + //Update channel to reliable if needed. + networkManager.TransportManager.CheckSetReliableChannel(writer.Length, ref channel); + + dataWriter.Store(); + + return writer; + } + } + + internal static class BroadcastExtensions + { + /// + /// Gets the key for a broadcast type. + /// + /// + /// + /// + internal static ushort GetKey() + { + return typeof(T).FullName.GetStableHashU16(); + } + } + + /// + /// Implemented by server and client broadcast handlers. + /// + public abstract class BroadcastHandlerBase + { + /// + /// Current index when iterating invokes. + /// This value will be -1 when not iterating. + /// + protected int IteratingIndex; + + public abstract void RegisterHandler(object obj); + public abstract void UnregisterHandler(object obj); + public virtual void InvokeHandlers(PooledReader reader, Channel channel) { } + public virtual void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) { } + public virtual bool RequireAuthentication => false; + } + + /// + /// Handles broadcasts received on server, from clients. + /// + internal class ClientBroadcastHandler : BroadcastHandlerBase + { + /// + /// Action handlers for the broadcast. + /// + private List> _handlers = new(); + /// + /// True to require authentication for the broadcast type. + /// + private bool _requireAuthentication; + + public ClientBroadcastHandler(bool requireAuthentication) + { + _requireAuthentication = requireAuthentication; + } + + /// + /// Invokes handlers after reading broadcast. + /// + /// True if a rebuild was required. + public override void InvokeHandlers(NetworkConnection conn, PooledReader reader, Channel channel) + { + T result = reader.Read(); + for (base.IteratingIndex = 0; base.IteratingIndex < _handlers.Count; base.IteratingIndex++) + { + Action item = _handlers[base.IteratingIndex]; + if (item != null) + { + item.Invoke(conn, result, channel); + } + else + { + _handlers.RemoveAt(base.IteratingIndex); + base.IteratingIndex--; + } + } + + base.IteratingIndex = -1; + } + + /// + /// Adds a handler for this type. + /// + public override void RegisterHandler(object obj) + { + Action handler = (Action)obj; + _handlers.AddUnique(handler); + } + + /// + /// Removes a handler from this type. + /// + /// + public override void UnregisterHandler(object obj) + { + Action handler = (Action)obj; + int indexOf = _handlers.IndexOf(handler); + //Not registered. + if (indexOf == -1) + return; + + /* Has already been iterated over, need to subtract + * 1 from iteratingIndex to accomodate + * for the entry about to be removed. */ + if (base.IteratingIndex >= 0 && (indexOf <= base.IteratingIndex)) + base.IteratingIndex--; + + //Remove entry. + _handlers.RemoveAt(indexOf); + } + + /// + /// True to require authentication for the broadcast type. + /// + public override bool RequireAuthentication => _requireAuthentication; + } + + + + /// + /// Handles broadcasts received on client, from server. + /// + internal class ServerBroadcastHandler : BroadcastHandlerBase + { + /// + /// Action handlers for the broadcast. + /// Even though List lookups are slower this allows easy adding and removing of entries during iteration. + /// + private List> _handlers = new(); + + /// + /// Invokes handlers after reading broadcast. + /// + /// True if a rebuild was required. + public override void InvokeHandlers(PooledReader reader, Channel channel) + { + T result = reader.Read(); + for (base.IteratingIndex = 0; base.IteratingIndex < _handlers.Count; base.IteratingIndex++) + { + Action item = _handlers[base.IteratingIndex]; + if (item != null) + { + item.Invoke(result, channel); + } + else + { + _handlers.RemoveAt(base.IteratingIndex); + base.IteratingIndex--; + } + } + + base.IteratingIndex = -1; + } + + /// + /// Adds a handler for this type. + /// + public override void RegisterHandler(object obj) + { + Action handler = (Action)obj; + _handlers.AddUnique(handler); + } + + /// + /// Removes a handler from this type. + /// + /// + public override void UnregisterHandler(object obj) + { + Action handler = (Action)obj; + int indexOf = _handlers.IndexOf(handler); + //Not registered. + if (indexOf == -1) + return; + + /* Has already been iterated over, need to subtract + * 1 from iteratingIndex to accomodate + * for the entry about to be removed. */ + if (base.IteratingIndex >= 0 && (indexOf <= base.IteratingIndex)) + base.IteratingIndex--; + + //Remove entry. + _handlers.RemoveAt(indexOf); + } + + /// + /// True to require authentication for the broadcast type. + /// + public override bool RequireAuthentication => false; + } + +} \ 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..3343316 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0636e29429649a24795091f80edbd892 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/Broadcasts.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs b/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs new file mode 100644 index 0000000..00f1b68 --- /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 PublicPropertyComparer + { + /// + /// Compare if T is default. + /// + public static Func IsDefault { get; set; } + /// + /// Compare if T is the same as T2. + /// + public static Func Compare { 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..518e8ff --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e912d0645f10b2c458cc2f01e24ecc27 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/Comparers.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs new file mode 100644 index 0000000..b205696 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs @@ -0,0 +1,201 @@ +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; + + /// + /// + /// + /// + /// + /// True to flip the smaller values when the largest axes is negative. Doing this saves a byte but the rotation numeric values will be reversed when decompressed. + public static void Compress(Writer writer, Quaternion quaternion, bool axesFlippingEnabled = true) + { + const float precision = 0.00098f; + + 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; + } + + bool largestIsNegative = (largest < 0); + + //If not flipping axes and any values are less than precision then 0 them out. + if (!axesFlippingEnabled) + { + if (absX < precision) + quaternion.x = 0f; + if (absY < precision) + quaternion.y = 0f; + if (absZ < precision) + quaternion.z = 0f; + if (absW < precision) + quaternion.w = 0f; + } + + 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 it's okay to flip when largest is negative. + if (largestIsNegative && axesFlippingEnabled) + { + a = -a; + b = -b; + c = -c; + } + + uint integerA = ScaleToUint(a); + uint integerB = ScaleToUint(b); + uint integerC = ScaleToUint(c); + + if (!axesFlippingEnabled) + writer.WriteBoolean((largest < 0f)); + + uint result = (((uint)largestComponent) << LargestComponentShift) | (integerA << AShift) | (integerB << BShift) | integerC; + writer.WriteUInt32Unpacked(result); + } + + 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; + } + + /// + /// + /// + /// + /// True if the smaller values were flipped during compression when the largest axes was negative. + /// + public static Quaternion Decompress(Reader reader, bool axesFlippingEnabled = true) + { + bool largestIsNegative = (axesFlippingEnabled) ? false : reader.ReadBoolean(); + uint compressed = reader.ReadUInt32Unpacked(); + + 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); + + if (largestIsNegative) + rotation.x *= -1f; + 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); + + if (largestIsNegative) + rotation.y *= -1f; + 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); + + if (largestIsNegative) + rotation.z *= -1f; + 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); + + if (largestIsNegative) + rotation.w *= -1f; + break; + default: + // Should never happen! + throw new ArgumentOutOfRangeException("Unknown rotation component type: " + largestComponentType); + } + + return rotation; + } + } +} \ No newline at end of file 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..4ee1def --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f71e61ed84064a0429577ec462a8fa79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/Quaternion32.cs + uploadId: 762203 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..a217c0f --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7afd33d2ca5433f4f831dfaf0169423c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/Quaternion64.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs new file mode 100644 index 0000000..1a07a68 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs @@ -0,0 +1,11 @@ +namespace FishNet.Serializing.Helping +{ + 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..4ef8eb0 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b7ac59ce12259104fa28fc837fb17ccf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionConverter.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs new file mode 100644 index 0000000..87ef180 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs @@ -0,0 +1,280 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FishNet.Managing; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + [System.Flags] + internal enum QuaternionDeltaPrecisionFlag : byte + { + Unset = 0, + + /* Its probably safe to discard '-IsNegative' + * and replace with a single 'largest is negative'. + * Doing this would still use the same amount of bytes + * though, and would require a refactor on this and the delta + * compression class. */ + NextAIsLarger = (1 << 0), + NextBIsLarger = (1 << 1), + NextCIsLarger = (1 << 2), + NextDIsNegative = (1 << 3), + + LargestIsX = (1 << 4), + LargestIsY = (1 << 5), + LargestIsZ = (1 << 6), + //This flag can be discarded via refactor if we need it later. + LargestIsW = (1 << 7), + } + + internal static class QuaternionDeltaPrecisionFlagExtensions + { + /// + /// Returns if whole contains part. + /// + internal static bool FastContains(this QuaternionDeltaPrecisionFlag whole, QuaternionDeltaPrecisionFlag part) => (whole & part) == part; + } + + public static class QuaternionDeltaPrecisionCompression + { + /// + /// Write a compressed a delta Quaternion using a variable precision. + /// + public static void Compress(Writer writer, Quaternion valueA, Quaternion valueB, float precision = 0.001f) + { + uint multiplier = (uint)Mathf.RoundToInt(1f / precision); + + //Position where the next byte is to be written. + int startPosition = writer.Position; + //Skip one byte so the flags can be inserted after everything else is writteh. + writer.Skip(1); + + QuaternionDeltaPrecisionFlag flags = QuaternionDeltaPrecisionFlag.Unset; + long largestUValue = -1; + + /* This becomes true if the largest difference is negative on valueB. + * EG: if Y is the largest and value.Y is < 0f then largestIsNegative becomes true. */ + bool largestIsNegative = false; + + /* Set next is larger values, and output differneces. */ + bool xIsLarger = GetNextIsLarger(valueA.x, valueB.x, multiplier, out uint xDifference); + UpdateLargestValues(xDifference, valueB.x, QuaternionDeltaPrecisionFlag.LargestIsX); + + bool yIsLarger = GetNextIsLarger(valueA.y, valueB.y, multiplier, out uint yDifference); + UpdateLargestValues(yDifference, valueB.y, QuaternionDeltaPrecisionFlag.LargestIsY); + + bool zIsLarger = GetNextIsLarger(valueA.z, valueB.z, multiplier, out uint zDifference); + UpdateLargestValues(zDifference, valueB.z, QuaternionDeltaPrecisionFlag.LargestIsZ); + + bool wIsLarger = GetNextIsLarger(valueA.w, valueB.w, multiplier, out uint wDifference); + UpdateLargestValues(wDifference, valueB.w, QuaternionDeltaPrecisionFlag.LargestIsW); + + //If flags are unset something went wrong. This should never be possible. + if (flags == QuaternionDeltaPrecisionFlag.Unset) + { + //Write that flags are unset and error. + writer.InsertUInt8Unpacked((byte)flags, startPosition); + NetworkManagerExtensions.LogError($"Flags should not be unset."); + return; + } + + //Updates largest values and flags. + void UpdateLargestValues(uint checkedValue, float fValue, QuaternionDeltaPrecisionFlag newFlag) + { + if (checkedValue > largestUValue) + { + largestUValue = checkedValue; + flags = newFlag; + largestIsNegative = (fValue < 0f); + } + } + + /* Write all but largest. */ + + //X is largest. + if (flags == QuaternionDeltaPrecisionFlag.LargestIsX) + WriteValues(yDifference, yIsLarger, zDifference, zIsLarger, wDifference, wIsLarger); + //Y is largest. + else if (flags == QuaternionDeltaPrecisionFlag.LargestIsY) + WriteValues(xDifference, xIsLarger, zDifference, zIsLarger, wDifference, wIsLarger); + //Z is largest. + else if (flags == QuaternionDeltaPrecisionFlag.LargestIsZ) + WriteValues(xDifference, xIsLarger, yDifference, yIsLarger, wDifference, wIsLarger); + //W is largest. + else if (flags == QuaternionDeltaPrecisionFlag.LargestIsW) + WriteValues(xDifference, xIsLarger, yDifference, yIsLarger, zDifference, zIsLarger); + + /* This must be set after values are written since the enum + * checks above use ==, rather than a bit comparer. */ + if (largestIsNegative) + flags |= QuaternionDeltaPrecisionFlag.NextDIsNegative; + + void WriteValues(uint aValue, bool aIsLarger, uint bValue, bool bIsLarger, uint cValue, bool cIsLarger) + { + writer.WriteUnsignedPackedWhole(aValue); + if (aIsLarger) + flags |= QuaternionDeltaPrecisionFlag.NextAIsLarger; + + writer.WriteUnsignedPackedWhole(bValue); + if (bIsLarger) + flags |= QuaternionDeltaPrecisionFlag.NextBIsLarger; + + writer.WriteUnsignedPackedWhole(cValue); + if (cIsLarger) + flags |= QuaternionDeltaPrecisionFlag.NextCIsLarger; + } + + //Insert flags. + writer.InsertUInt8Unpacked((byte)flags, startPosition); + } + + /// + /// Write a compressed a delta Quaternion using a variable precision. + /// + public static Quaternion Decompress(Reader reader, Quaternion valueA, float precision = 0.001f) + { + uint multiplier = (uint)Mathf.RoundToInt(1f / precision); + + QuaternionDeltaPrecisionFlag flags = (QuaternionDeltaPrecisionFlag)reader.ReadUInt8Unpacked(); + + //Unset flags mean something went wrong in writing. + if (flags == QuaternionDeltaPrecisionFlag.Unset) + { + NetworkManagerExtensions.LogError($"Unset flags were returned."); + return default; + } + + /* These values will be in order of X Y Z W. + * Whichever value is the highest will be left out. + * + * EG: if Y was the highest then the following will be true... + * a = X + * b = Z + * c = W */ + uint aWholeDifference = (uint)reader.ReadUnsignedPackedWhole(); + uint bWholeDifference = (uint)reader.ReadUnsignedPackedWhole(); + uint cWholeDifference = (uint)reader.ReadUnsignedPackedWhole(); + + //Debug.Log($"Read {aWholeDifference}, {bWholeDifference}, {cWholeDifference}. ValueA {valueA}"); + + float aFloatDifference = (float)aWholeDifference / multiplier; + float bFloatDifference = (float)bWholeDifference / multiplier; + float cFloatDifference = (float)cWholeDifference / multiplier; + + //Invert differences as needed so they can all be added onto the previous value as negative or positive. + if (!flags.FastContains(QuaternionDeltaPrecisionFlag.NextAIsLarger)) + aFloatDifference *= -1f; + if (!flags.FastContains(QuaternionDeltaPrecisionFlag.NextBIsLarger)) + bFloatDifference *= -1f; + if (!flags.FastContains(QuaternionDeltaPrecisionFlag.NextCIsLarger)) + cFloatDifference *= -1f; + + float nextA; + float nextB; + float nextC; + + /* Add onto the previous value. */ + if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsX)) + { + nextA = valueA.y + aFloatDifference; + nextB = valueA.z + bFloatDifference; + nextC = valueA.w + cFloatDifference; + } + else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsY)) + { + nextA = valueA.x + aFloatDifference; + nextB = valueA.z + bFloatDifference; + nextC = valueA.w + cFloatDifference; + } + else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsZ)) + { + nextA = valueA.x + aFloatDifference; + nextB = valueA.y + bFloatDifference; + nextC = valueA.w + cFloatDifference; + } + /* We do not really need the 'largest is W' since we know if + * the other 3 are not the largest, then the remaining must be. + * We have the available packing to use though, so use them + * for now. */ + else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsW)) + { + nextA = valueA.x + aFloatDifference; + nextB = valueA.y + bFloatDifference; + nextC = valueA.z + cFloatDifference; + } + else + { + NetworkManagerExtensions.LogError($"Largest axes was not handled. Flags {flags}."); + return default; + } + + float abcMagnitude = GetMagnitude(nextA, nextB, nextC); + + float nextD = 1f - abcMagnitude; + /* NextD should always be positive. But depending on precision + * the calculated result could be negative due to missing decimals. + * When negative make positive so nextD will normalize properly. */ + if (nextD < 0f) + nextD *= -1f; + + nextD = (float)Math.Sqrt(nextD); + + //Get magnitude of all values. + static float GetMagnitude(float a, float b, float c, float d = 0f) => (a * a + b * b + c * c + d * d); + + if (nextD >= 0f && flags.FastContains(QuaternionDeltaPrecisionFlag.NextDIsNegative)) + nextD *= -1f; + + if (!TryNormalize()) + return default; + + //Normalizes next values. + bool TryNormalize() + { + float magnitude = (float)Math.Sqrt(GetMagnitude(nextA, nextB, nextC, nextD)); + if (magnitude < float.Epsilon) + { + NetworkManagerExtensions.LogError($"Magnitude cannot be normalized."); + return false; + } + + nextA /= magnitude; + nextB /= magnitude; + nextC /= magnitude; + nextD /= magnitude; + + return true; + } + + /* Add onto the previous value. */ + if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsX)) + return new Quaternion(nextD, nextA, nextB, nextC); + else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsY)) + return new Quaternion(nextA, nextD, nextB, nextC); + else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsZ)) + return new Quaternion(nextA, nextB, nextD, nextC); + else if (flags.FastContains(QuaternionDeltaPrecisionFlag.LargestIsW)) + return new Quaternion(nextA, nextB, nextC, nextD); + else + NetworkManagerExtensions.LogError($"Unhandled Largest flag. Received flags are {flags}."); + + return default; + } + + /// + /// Returns if the next value is larger than the previous, and returns unsigned result with multiplier applied. + /// + private static bool GetNextIsLarger(float a, float b, uint lMultiplier, out uint multipliedUResult) + { + //Set is b is larger. + bool bIsLarger = (b > a); + + //Get multiplied u value. + float value = (bIsLarger) ? (b - a) : (a - b); + multipliedUResult = (uint)Mathf.RoundToInt(value * lMultiplier); + + return bIsLarger; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs.meta new file mode 100644 index 0000000..6abe264 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 37e43ec9c17f2dc43ac255695bd1a71f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionDeltaPrecisionCompression.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs new file mode 100644 index 0000000..98dcdf9 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs @@ -0,0 +1,212 @@ +using System; +using FishNet.Managing; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + [System.Flags] + internal enum QuaternionPrecisionFlag : byte + { + Unset = 0, + + /* Its probably safe to discard '-IsNegative' + * and replace with a single 'largest is negative'. + * Doing this would still use the same amount of bytes + * though, and would require a refactor on this and the delta + * compression class. */ + AIsNegative = (1 << 0), + BIsNegative = (1 << 1), + CIsNegative = (1 << 2), + DIsNegative = (1 << 3), + + LargestIsX = (1 << 4), + LargestIsY = (1 << 5), + LargestIsZ = (1 << 6), + //This flag can be discarded via refactor if we need it later. + LargestIsW = (1 << 7), + } + + internal static class QuaternionPrecisionFlagExtensions + { + /// + /// Returns if whole contains part. + /// + internal static bool FastContains(this QuaternionPrecisionFlag whole, QuaternionPrecisionFlag part) => (whole & part) == part; + } + + public static class QuaternionPrecisionCompression + { + /// + /// Write a compressed a delta Quaternion using a variable precision. + /// + public static void Compress(Writer writer, Quaternion value, float precision = 0.001f) + { + /* When using 0.001f or less accurate precision use the classic + * compression. This saves about a byte by send. */ + if (precision >= 0.001f) + { + Quaternion32Compression.Compress(writer, value, axesFlippingEnabled: false); + return; + } + + //Position where the next byte is to be written. + int startPosition = writer.Position; + + //Skip one byte so the flags can be inserted after everything else is writteh. + writer.Skip(1); + + QuaternionPrecisionFlag flags = QuaternionPrecisionFlag.Unset; + float largestAxesValue = float.MinValue; + + //Find out which value is the largest. + UpdateLargestValues(Math.Abs(value.x), QuaternionPrecisionFlag.LargestIsX); + UpdateLargestValues(Math.Abs(value.y), QuaternionPrecisionFlag.LargestIsY); + UpdateLargestValues(Math.Abs(value.z), QuaternionPrecisionFlag.LargestIsZ); + UpdateLargestValues(Math.Abs(value.w), QuaternionPrecisionFlag.LargestIsW); + + //Updates largest values and flags. + void UpdateLargestValues(float checkedValue, QuaternionPrecisionFlag newFlag) + { + if (checkedValue > largestAxesValue) + { + largestAxesValue = checkedValue; + flags = newFlag; + } + } + + /* Write all but largest. */ + + //X is largest. + if (flags == QuaternionPrecisionFlag.LargestIsX) + WriteValuesAndSetPositives(value.y, value.z, value.w, value.x); + //Y is largest. + else if (flags == QuaternionPrecisionFlag.LargestIsY) + WriteValuesAndSetPositives(value.x, value.z, value.w, value.y); + //Z is largest. + else if (flags == QuaternionPrecisionFlag.LargestIsZ) + WriteValuesAndSetPositives(value.x, value.y, value.w, value.z); + //W is largest. + else if (flags == QuaternionPrecisionFlag.LargestIsW) + WriteValuesAndSetPositives(value.x, value.y, value.z, value.w); + + void WriteValuesAndSetPositives(float aValue, float bValue, float cValue, float largestAxes) + { + uint multiplier = (uint)Mathf.RoundToInt(1f / precision); + + uint aUint = (uint)Mathf.RoundToInt(Math.Abs(aValue) * multiplier); + uint bUint = (uint)Mathf.RoundToInt(Math.Abs(bValue) * multiplier); + uint cUint = (uint)Mathf.RoundToInt(Math.Abs(cValue) * multiplier); + + writer.WriteUnsignedPackedWhole(aUint); + writer.WriteUnsignedPackedWhole(bUint); + writer.WriteUnsignedPackedWhole(cUint); + + /* Update sign on values. */ + if (aValue < 0f) + flags |= QuaternionPrecisionFlag.AIsNegative; + if (bValue < 0f) + flags |= QuaternionPrecisionFlag.BIsNegative; + if (cValue<= 0f) + flags |= QuaternionPrecisionFlag.CIsNegative; + if (largestAxes < 0f) + flags |= QuaternionPrecisionFlag.DIsNegative; + } + + //Insert flags. + writer.InsertUInt8Unpacked((byte)flags, startPosition); + } + + /// + /// Write a compressed a delta Quaternion using a variable precision. + /// + public static Quaternion Decompress(Reader reader, float precision = 0.001f) + { + /* When using 0.001f or less accurate precision use the classic + * compression. This saves about a byte by send. */ + if (precision >= 0.001f) + return Quaternion32Compression.Decompress(reader, axesFlippingEnabled: false); + + uint multiplier = (uint)Mathf.RoundToInt(1f / precision); + + QuaternionPrecisionFlag flags = (QuaternionPrecisionFlag)reader.ReadUInt8Unpacked(); + + //Unset flags mean something went wrong in writing. + if (flags == QuaternionPrecisionFlag.Unset) + { + NetworkManagerExtensions.LogError($"Unset flags were returned."); + return default; + } + + /* These values will be in order of X Y Z W. + * Whichever value is the highest will be left out. + * + * EG: if Y was the highest then the following will be true... + * a = X + * b = Z + * c = W */ + float aValue = (float)reader.ReadUnsignedPackedWhole() / (float)multiplier; + float bValue = (float)reader.ReadUnsignedPackedWhole() / (float)multiplier; + float cValue = (float)reader.ReadUnsignedPackedWhole() / (float)multiplier; + + //Make values negative if needed. + if (flags.FastContains(QuaternionPrecisionFlag.AIsNegative)) + aValue *= -1f; + if (flags.FastContains(QuaternionPrecisionFlag.BIsNegative)) + bValue *= -1f; + if (flags.FastContains(QuaternionPrecisionFlag.CIsNegative)) + cValue *= -1f; + + float abcMagnitude = GetMagnitude(aValue, bValue, cValue); + + float dValue = 1f - abcMagnitude; + /* NextD should always be positive. But depending on precision + * the calculated result could be negative due to missing decimals. + * When negative make positive so dValue will normalize properly. */ + if (dValue < 0f) + dValue *= -1f; + + dValue = (float)Math.Sqrt(dValue); + + //Get magnitude of all values. + static float GetMagnitude(float a, float b, float c, float d = 0f) => (a * a + b * b + c * c + d * d); + + if (dValue >= 0f && flags.FastContains(QuaternionPrecisionFlag.DIsNegative)) + dValue *= -1f; + + if (!TryNormalize()) + return default; + + //Normalizes next values. + bool TryNormalize() + { + float magnitude = (float)Math.Sqrt(GetMagnitude(aValue, bValue, cValue, dValue)); + if (magnitude < float.Epsilon) + { + NetworkManagerExtensions.LogError($"Magnitude cannot be normalized."); + return false; + } + + aValue /= magnitude; + bValue /= magnitude; + cValue /= magnitude; + dValue /= magnitude; + + return true; + } + + /* Add onto the previous value. */ + if (flags.FastContains(QuaternionPrecisionFlag.LargestIsX)) + return new Quaternion(dValue, aValue, bValue, cValue); + else if (flags.FastContains(QuaternionPrecisionFlag.LargestIsY)) + return new Quaternion(aValue, dValue, bValue, cValue); + else if (flags.FastContains(QuaternionPrecisionFlag.LargestIsZ)) + return new Quaternion(aValue, bValue, dValue, cValue); + else if (flags.FastContains(QuaternionPrecisionFlag.LargestIsW)) + return new Quaternion(aValue, bValue, cValue, dValue); + else + NetworkManagerExtensions.LogError($"Unhandled Largest flag. Received flags are {flags}."); + + return default; + } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs.meta new file mode 100644 index 0000000..69c17a4 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e3df94f7449d53c4b8b5a167b51a93dd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/QuaternionPrecisionCompression.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs b/Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs new file mode 100644 index 0000000..30790f7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs @@ -0,0 +1,166 @@ +using System.Runtime.CompilerServices; +using FishNet.Managing; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Serializing.Helping +{ + /// + /// Used to reserve bytes in a writer for length, then inserts length after data has been written. + /// Reserved values are always written as unsigned. + /// + internal class ReservedLengthWriter : IResettable + { + private Writer _writer; + private int _startPosition; + private byte _reservedBytes; + + /// + /// Number of bytes currently written. + /// + public int Length + { + get { return (_writer == null) ? 0 : (_writer.Position - _startPosition); } + } + + public void Initialize(Writer writer, byte reservedBytes) + { + _writer = writer; + _reservedBytes = reservedBytes; + writer.Skip(reservedBytes); + _startPosition = writer.Position; + } + + /// + /// Writes the amount of data written to the reserved space. + /// This also resets the state of this object. + /// + public void WriteLength() + { + WriteLength((uint)Length); + + ResetState(); + } + + /// + /// Writes the amount of data written to the reserved space. If no data was written the reserved amount is removed. + /// This also resets the state of this object. + /// Returns if length was written. + /// + public bool WriteLengthOrRemove(uint written) + { + if (written == 0) + _writer.Remove(_reservedBytes); + else + WriteLength(written); + + ResetState(); + + return (written > 0); + } + + /// + /// Writes the amount of data written to the reserved space. This overrides Length normally written. + /// This also resets the state of this object. + /// + public void WriteLength(uint written) + { + switch (_reservedBytes) + { + case 1: + _writer.InsertUInt8Unpacked((byte)written, _startPosition - _reservedBytes); + break; + case 2: + _writer.InsertUInt16Unpacked((ushort)written, _startPosition - _reservedBytes); + break; + case 4: + _writer.InsertUInt32Unpacked((uint)written, _startPosition - _reservedBytes); + break; + default: + string errorMsg = $"Reserved bytes value of {_reservedBytes} is unhandled."; + if (_writer != null) + _writer.NetworkManager.LogError(errorMsg); + else + NetworkManagerExtensions.LogError(errorMsg); + break; + } + + ResetState(); + } + + /// + /// Writes the amount of data written to the reserved space. If no data was written the reserved amount is removed. + /// This also resets the state of this object. + /// + public bool WriteLengthOrRemove() + { + //Insert written amount. + int written = (_writer.Position - _startPosition); + + if (written == 0) + _writer.Remove(_reservedBytes); + else + WriteLength((uint)written); + + ResetState(); + + return (written > 0); + } + + /// + /// Returns a length read based on a reserved byte count. + /// + /// True to reset to position before read. + public static uint ReadLength(PooledReader reader, byte reservedBytes, bool resetPosition = false) + { + uint result; + switch (reservedBytes) + { + case 1: + result = reader.ReadUInt8Unpacked(); + break; + case 2: + result = reader.ReadUInt16Unpacked(); + break; + case 4: + result = reader.ReadUInt32Unpacked(); + break; + default: + string errorMsg = $"Reserved bytes value of {reservedBytes} is unhandled."; + if (reader != null) + reader.NetworkManager.LogError(errorMsg); + else + NetworkManagerExtensions.LogError(errorMsg); + return 0; + } + + if (resetPosition) + reader.Position -= (int)result; + + return result; + } + + public void ResetState() + { + _writer = null; + _startPosition = 0; + _reservedBytes = 0; + } + + public void InitializeState() { } + } + + internal static class ReservedWritersExtensions + { + /// + /// Stores to a cache. + /// + public static void Store(this ReservedLengthWriter rlw) => ResettableObjectCaches.Store(rlw); + + /// + /// Retrieves from a cache. + /// + /// + public static ReservedLengthWriter Retrieve() => ResettableObjectCaches.Retrieve(); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs.meta b/Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs.meta new file mode 100644 index 0000000..e6f0516 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b9fd1f1770c15f54da31f3903a8714ab +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/ReservedWriters.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs new file mode 100644 index 0000000..5736854 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs @@ -0,0 +1,38 @@ +using System.Runtime.InteropServices; + +namespace FishNet.Serializing.Helping +{ + [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..8264b9f --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 008e79d0f22a2674189acc7eff64408f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Helping/ValueConversions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Reader.Delta.cs b/Assets/FishNet/Runtime/Serializing/Reader.Delta.cs new file mode 100644 index 0000000..fe25b7e --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Reader.Delta.cs @@ -0,0 +1,468 @@ +using System; +using FishNet.CodeGenerating; +using System.Runtime.CompilerServices; +using FishNet.Managing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Serializing.Helping; +using UnityEngine; + +namespace FishNet.Serializing +{ + public partial class Reader + { + internal double DOUBLE_ACCURACY => Writer.DOUBLE_ACCURACY; + internal decimal DECIMAL_ACCURACY => Writer.DECIMAL_ACCURACY; + + #region Other. + /// + /// Reads a boolean. + /// + [DefaultDeltaReader] + public bool ReadDeltaBoolean(bool valueA) + { + return !valueA; + } + #endregion + + #region Whole values. + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public sbyte ReadDeltaInt8(sbyte valueA) => (sbyte)ReadDifference8_16_32(valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public byte ReadDeltaUInt8(byte valueA) => (byte)ReadDifference8_16_32(valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public short ReadDeltaInt16(short valueA) => (short)ReadDifference8_16_32(valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public ushort ReadDeltaUInt16(ushort valueA) => (ushort)ReadDifference8_16_32(valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public int ReadDeltaInt32(int valueA) => (int)ReadDifference8_16_32(valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public uint ReadDeltaUInt32(uint valueA) => (uint)ReadDifference8_16_32(valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public long ReadDeltaInt64(long valueA) => (long)ReadDeltaUInt64((ulong)valueA); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public ulong ReadDeltaUInt64(ulong valueA) + { + bool bLargerThanA = ReadBoolean(); + ulong diff = ReadUnsignedPackedWhole(); + + return (bLargerThanA) ? (valueA + diff) : (valueA - diff); + } + + /// + /// Returns a new result by reading and applying a difference to a value. + /// + [DefaultDeltaReader] + private long ReadDifference8_16_32(long valueA) + { + long diff = ReadSignedPackedWhole(); + return (valueA + diff); + } + #endregion + + #region Single. + /// + /// Reads a value. + /// + public float ReadDeltaSingle(UDeltaPrecisionType dpt, bool unsigned) + { + if (dpt.FastContains(UDeltaPrecisionType.UInt8)) + { + if (unsigned) + return (ReadUInt8Unpacked() / (float)DOUBLE_ACCURACY); + else + return (ReadInt8Unpacked() / (float)DOUBLE_ACCURACY); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt16)) + { + if (unsigned) + return (ReadUInt16Unpacked() / (float)DOUBLE_ACCURACY); + else + return (ReadInt16Unpacked() / (float)DOUBLE_ACCURACY); + } + //Everything else is unpacked. + else + { + return ReadSingleUnpacked(); + } + } + + /// + /// Reads a difference, appending it onto a value. + /// + public float ReadDeltaSingle(UDeltaPrecisionType dpt, float valueA, bool unsigned) + { + float diff = ReadDeltaSingle(dpt, unsigned); + + if (unsigned) + { + bool bLargerThanA = dpt.FastContains(UDeltaPrecisionType.NextValueIsLarger); + return (bLargerThanA) ? (valueA + diff) : (valueA - diff); + } + else + { + return (valueA + diff); + } + } + + /// + /// Reads a difference, appending it onto a value. + /// + public float ReadDeltaSingle(float valueA) + { + const bool unsigned = false; + UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked(); + + return ReadDeltaSingle(dpt, valueA, unsigned); + } + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public float ReadUDeltaSingle(float valueA) + { + const bool unsigned = true; + UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked(); + + return ReadDeltaSingle(dpt, valueA, unsigned); + } + #endregion + + #region Double. + /// + /// Reads a value. + /// + public double ReadDeltaDouble(UDeltaPrecisionType dpt, bool unsigned) + { + if (dpt.FastContains(UDeltaPrecisionType.UInt8)) + { + if (unsigned) + return (ReadUInt8Unpacked() / DOUBLE_ACCURACY); + else + return (ReadInt8Unpacked() / DOUBLE_ACCURACY); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt16)) + { + if (unsigned) + return (ReadUInt16Unpacked() / DOUBLE_ACCURACY); + else + return (ReadInt16Unpacked() / DOUBLE_ACCURACY); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt32)) + { + if (unsigned) + return (ReadUInt32Unpacked() / DOUBLE_ACCURACY); + else + return (ReadInt32Unpacked() / DOUBLE_ACCURACY); + } + //Unpacked. + else if (dpt.FastContains(UDeltaPrecisionType.Unset)) + { + return ReadDoubleUnpacked(); + } + else + { + NetworkManager.LogError($"Unhandled precision type of {dpt}."); + return 0d; + } + } + + /// + /// Reads a difference, appending it onto a value. + /// + public double ReadDeltaDouble(UDeltaPrecisionType dpt, double valueA, bool unsigned) + { + double diff = ReadDeltaDouble(dpt, unsigned); + //8. + + if (unsigned) + { + bool bLargerThanA = dpt.FastContains(UDeltaPrecisionType.NextValueIsLarger); + return (bLargerThanA) ? (valueA + diff) : (valueA - diff); + } + else + { + return (valueA + diff); + } + } + + /// + /// Reads a difference, appending it onto a value. + /// + public double ReadDeltaDouble(double valueA) + { + const bool unsigned = false; + UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked(); + + return ReadDeltaDouble(dpt, valueA, unsigned); + } + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public double ReadUDeltaDouble(double valueA) + { + const bool unsigned = true; + UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked(); + + return ReadDeltaDouble(dpt, valueA, unsigned); + } + #endregion + + #region Decimal. + /// + /// Reads a value. + /// + public decimal ReadDeltaDecimal(UDeltaPrecisionType dpt, bool unsigned) + { + if (dpt.FastContains(UDeltaPrecisionType.UInt8)) + { + if (unsigned) + return (ReadUInt8Unpacked() / DECIMAL_ACCURACY); + else + return (ReadInt8Unpacked() / DECIMAL_ACCURACY); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt16)) + { + if (unsigned) + return (ReadUInt16Unpacked() / DECIMAL_ACCURACY); + else + return (ReadInt16Unpacked() / DECIMAL_ACCURACY); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt32)) + { + if (unsigned) + return (ReadUInt32Unpacked() / DECIMAL_ACCURACY); + else + return (ReadInt32Unpacked() / DECIMAL_ACCURACY); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt64)) + { + if (unsigned) + return (ReadUInt64Unpacked() / DECIMAL_ACCURACY); + else + return (ReadInt64Unpacked() / DECIMAL_ACCURACY); + } + //Unpacked. + else if (dpt.FastContains(UDeltaPrecisionType.Unset)) + { + return ReadDecimalUnpacked(); + } + else + { + NetworkManager.LogError($"Unhandled precision type of {dpt}."); + return 0m; + } + } + + /// + /// Reads a difference, appending it onto a value. + /// + public decimal ReadDeltaDecimal(UDeltaPrecisionType dpt, decimal valueA, bool unsigned) + { + decimal diff = ReadDeltaDecimal(dpt, unsigned); + + if (unsigned) + { + bool bLargerThanA = dpt.FastContains(UDeltaPrecisionType.NextValueIsLarger); + return (bLargerThanA) ? (valueA + diff) : (valueA - diff); + } + else + { + return (valueA + diff); + } + } + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public decimal ReadDeltaDecimal(decimal valueA) + { + const bool unsigned = false; + UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked(); + + return ReadDeltaDecimal(dpt, valueA, unsigned); + } + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public decimal ReadUDeltaDecimal(decimal valueA) + { + const bool unsigned = true; + UDeltaPrecisionType dpt = (UDeltaPrecisionType)ReadUInt8Unpacked(); + + return ReadDeltaDecimal(dpt, valueA, unsigned); + } + #endregion + + #region FishNet Types. + /// + /// Reads a delta value. + /// + /// True if written. + [DefaultDeltaReader] + public NetworkBehaviour WriteDeltaNetworkBehaviour(NetworkBehaviour valueA) + { + return ReadNetworkBehaviour(); + } + #endregion + + #region Unity. + /// + /// Reads a difference, appending it onto a value. + /// (not really for Quaternion). + /// + [DefaultDeltaReader] + public Quaternion ReadDeltaQuaternion(Quaternion valueA, float precision = Writer.QUATERNION_PRECISION) => QuaternionDeltaPrecisionCompression.Decompress(this, valueA, precision); + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public Vector2 ReadDeltaVector2(Vector2 valueA) + { + byte allFlags = ReadUInt8Unpacked(); + + if ((allFlags & 1) == 1) + valueA.x = ReadUDeltaSingle(valueA.x); + if ((allFlags & 2) == 2) + valueA.y = ReadUDeltaSingle(valueA.y); + + return valueA; + } + + /// + /// Reads a difference, appending it onto a value. + /// + [DefaultDeltaReader] + public Vector3 ReadDeltaVector3(Vector3 valueA) + { + byte allFlags = ReadUInt8Unpacked(); + + if ((allFlags & 1) == 1) + valueA.x = ReadUDeltaSingle(valueA.x); + if ((allFlags & 2) == 2) + valueA.y = ReadUDeltaSingle(valueA.y); + if ((allFlags & 4) == 4) + valueA.z = ReadUDeltaSingle(valueA.z); + + return valueA; + } + #endregion + + #region Prediction. + /// + /// Reads a reconcile. + /// + internal T ReadDeltaReconcile(T lastReconcile) => ReadDelta(lastReconcile); + + /// + /// Reads a replicate. + /// + internal int ReadDeltaReplicate(T lastReadReplicate, ref T[] collection, uint tick) where T : IReplicateData + { + int startRemaining = Remaining; + + //Number of entries written. + int count = (int)ReadUInt8Unpacked(); + 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); + + uint lastReadTick = lastReadReplicate.GetTick(); + + T prev = lastReadReplicate; + for (int i = 0; i < count; i++) + { + //Tick read is for. + uint readTick = (tick + (uint)i); + /* If readTick is equal or lesser than lastReadReplicate + * then there is no reason to process the data other than getting + * it out of the reader. */ + if (readTick <= lastReadTick) + { + ReadDelta(prev); + } + else + { + T value = ReadDelta(prev); + //Apply tick. + value.SetTick(readTick); + //Assign to collection. + collection[i] = value; + //Update previous. + prev = value; + } + } + + return count; + } + #endregion + + #region Generic. + /// + /// Reads a delta of any time. + /// + public T ReadDelta(T prev) + { + Func del = GenericDeltaReader.Read; + + if (del == null) + { + NetworkManager.LogError($"Read delta method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer."); + return default; + } + else + { + return del.Invoke(this, prev); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Reader.Delta.cs.meta b/Assets/FishNet/Runtime/Serializing/Reader.Delta.cs.meta new file mode 100644 index 0000000..0b32860 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Reader.Delta.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 90549173680239a48a3b0b61ecd47a77 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Reader.Delta.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Reader.cs b/Assets/FishNet/Runtime/Serializing/Reader.cs new file mode 100644 index 0000000..2098639 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Reader.cs @@ -0,0 +1,1606 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.CodeGenerating; +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; +using FishNet.Utility.Performance; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using UnityEngine; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] +//Required for internal tests. +[assembly: InternalsVisibleTo(UtilityConstants.TEST_ASSEMBLY_NAME)] + +namespace FishNet.Serializing +{ + /// + /// Reads data from a buffer. + /// + public partial class Reader + { + #region Types. + public enum DataSource + { + Unset = 0, + Server = 1, + Client = 2, + } + #endregion + + #region Public. + /// + /// Which part of the network the data came from. + /// + public DataSource Source = DataSource.Unset; + + /// + /// 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 DEVELOPMENT + /// + /// 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; + /// + /// Used to convert bytes to a string. + /// + private static readonly UTF8Encoding _encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); + /// + /// Used to convert bytes to a GUID. + /// + private static readonly byte[] _guidBuffer = new byte[16]; + #endregion + + public Reader() { } + + public Reader(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) + { + Initialize(bytes, networkManager, networkConnection, source); + } + + public Reader(ArraySegment segment, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) + { + Initialize(segment, networkManager, networkConnection, source); + } + + /// + /// Outputs reader to string. + /// + /// + public override string ToString() => ToString(0, Length); + + /// + /// Outputs reader to string starting at an index. + /// + /// + public string ToString(int offset, int length) + { + return $"Position: {Position:0000}, Length: {Length:0000}, Buffer: {BitConverter.ToString(_buffer, offset, length)}."; + } + + /// + /// Outputs reader to string. + /// + /// + public string RemainingToString() + { + string buffer = (Remaining > 0) ? BitConverter.ToString(_buffer, Position, Remaining) : "null"; + return $"Remaining: {Remaining}, Length: {Length}, Buffer: {buffer}."; + } + + /// + /// Returns remaining data as an ArraySegment. + /// + /// + public ArraySegment GetRemainingData() + { + if (Remaining == 0) + return default; + else + return new(_buffer, Position, Remaining); + } + + /// + /// Initializes this reader with data. + /// + public void Initialize(ArraySegment segment, NetworkManager networkManager, DataSource source = DataSource.Unset) + { + Initialize(segment, networkManager, null, source); + } + + /// + /// Initializes this reader with data. + /// + public void Initialize(ArraySegment segment, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) + { + _buffer = segment.Array; + if (_buffer == null) + _buffer = new byte[0]; + + Position = segment.Offset; + Offset = segment.Offset; + Length = segment.Count; + + NetworkManager = networkManager; + NetworkConnection = networkConnection; + Source = source; + } + + /// + /// Initializes this reader with data. + /// + public void Initialize(byte[] bytes, NetworkManager networkManager, DataSource source = DataSource.Unset) + { + Initialize(new ArraySegment(bytes), networkManager, null, source); + } + + /// + /// Initializes this reader with data. + /// + public void Initialize(byte[] bytes, NetworkManager networkManager, NetworkConnection networkConnection = null, DataSource source = DataSource.Unset) + { + Initialize(new ArraySegment(bytes), networkManager, networkConnection, source); + } + + /// + /// Reads length. This method is used to make debugging easier. + /// + internal int ReadLength() + { + return ReadInt32(); + } + + /// + /// Reads a packetId. + /// + internal PacketId ReadPacketId() + { + return (PacketId)ReadUInt16Unpacked(); + } + + /// + /// Returns a ushort without advancing the reader. + /// + /// + internal PacketId PeekPacketId() + { + int currentPosition = Position; + PacketId result = ReadPacketId(); + Position = currentPosition; + return result; + } + + /// + /// Returns the next byte to be read. + /// + /// + internal byte PeekUInt8() + { + return _buffer[Position]; + } + + /// + /// Skips a number of bytes in the reader. + /// + /// Number of bytes to skip. + public void Skip(int value) + { + if (value < 1 || Remaining < value) + return; + + Position += value; + } + + /// + /// Clears remaining bytes to be read. + /// + public void Clear() + { + if (Remaining > 0) + Skip(Remaining); + } + + /// + /// Returns the buffer as an ArraySegment. + /// + /// + public ArraySegment GetArraySegmentBuffer() + { + return new(_buffer, Offset, Length); + } + + [Obsolete("Use GetBuffer.")] //Remove V5 + public byte[] GetByteBuffer() => GetBuffer(); + + /// + /// Returns the buffer as bytes. This does not trim excessive bytes. + /// + /// + public byte[] GetBuffer() + { + return _buffer; + } + + [Obsolete("Use GetBufferAllocated().")] //Remove V5 + public byte[] GetByteBufferAllocated() => GetBufferAllocated(); + + /// + /// Returns the buffer as bytes and allocates into a new array. + /// + /// + [Obsolete("Use GetBufferAllocated().")] //Remove V5 + public byte[] GetBufferAllocated() + { + byte[] result = new byte[Length]; + Buffer.BlockCopy(_buffer, Offset, result, 0, Length); + return result; + } + + /// + /// BlockCopies data from the reader to target and advances reader. + /// + /// + /// + /// + public void BlockCopy(ref byte[] target, int targetOffset, int count) + { + Buffer.BlockCopy(_buffer, Position, target, targetOffset, count); + Position += count; + } + + [Obsolete("Use ReadUInt8Unpacked.")] //Remove in V5. + public byte ReadByte() => ReadUInt8Unpacked(); + + /// + /// Reads a byte. + /// + /// + [DefaultReader] + public byte ReadUInt8Unpacked() + { + byte r = _buffer[Position]; + Position += 1; + return r; + } + + [Obsolete("Use ReadUInt8ArrayAllocated.")] + public byte[] ReadBytesAllocated(int count) => ReadUInt8ArrayAllocated(count); + + [Obsolete("Use ReadUInt8Array.")] + public void ReadBytes(ref byte[] buffer, int count) => ReadUInt8Array(ref buffer, count); + + /// + /// Read bytes from position into target. + /// + /// Buffer to read bytes into. + /// Number of bytes to read. + public void ReadUInt8Array(ref byte[] buffer, int count) + { + if (buffer == null) + NetworkManager.LogError($"Buffer cannot be null."); + else if (count > buffer.Length) + NetworkManager.LogError($"Count of {count} exceeds target length of {buffer.Length}."); + else + BlockCopy(ref buffer, 0, count); + } + + /// + /// Creates an ArraySegment by reading a number of bytes from position. + /// + /// + /// + public ArraySegment ReadArraySegment(int count) + { + if (count < 0) + { + NetworkManager.Log($"ArraySegment count cannot be less than 0."); + //Purge renaming and return default. + Position += Remaining; + return default; + } + + ArraySegment result = new(_buffer, Position, count); + Position += count; + return result; + } + + [Obsolete("Use ReadInt8Unpacked.")] //Remove in V5. + public sbyte ReadSByte() => ReadInt8Unpacked(); + + /// + /// Reads a sbyte. + /// + /// + [DefaultReader] + public sbyte ReadInt8Unpacked() => (sbyte)ReadUInt8Unpacked(); + + /// + /// Reads a char. + /// + /// + [DefaultReader] + public char ReadChar() => (char)ReadUInt16(); + + /// + /// Reads a boolean. + /// + /// + [DefaultReader] + public bool ReadBoolean() + { + byte result = ReadUInt8Unpacked(); + return (result == 1) ? true : false; + } + + /// + /// Reads an int16. + /// + /// + public ushort ReadUInt16Unpacked() + { + ushort result = 0; + result |= _buffer[Position++]; + result |= (ushort)(_buffer[Position++] << 8); + + return result; + } + + /// + /// Reads an int16. + /// + /// + //todo: should be using ReadPackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. + [DefaultReader] + public ushort ReadUInt16() => ReadUInt16Unpacked(); + + /// + /// Reads a uint16. + /// + /// + //todo: should be using ReadPackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. + public short ReadInt16Unpacked() => (short)ReadUInt16Unpacked(); + + /// + /// Reads a uint16. + /// + /// + //todo: should be using ReadPackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. + [DefaultReader] + public short ReadInt16() => (short)ReadUInt16Unpacked(); + + /// + /// Reads an int32. + /// + /// + public uint ReadUInt32Unpacked() + { + uint result = 0; + result |= _buffer[Position++]; + result |= (uint)_buffer[Position++] << 8; + result |= (uint)_buffer[Position++] << 16; + result |= (uint)_buffer[Position++] << 24; + + return result; + } + + /// + /// Reads an int32. + /// + /// + [DefaultReader] + public uint ReadUInt32() => (uint)ReadUnsignedPackedWhole(); + + /// + /// Reads a uint32. + /// + /// + public int ReadInt32Unpacked() => (int)ReadUInt32Unpacked(); + + /// + /// Reads a uint32. + /// + /// + [DefaultReader] + public int ReadInt32() => (int)ReadSignedPackedWhole(); + + /// + /// Reads a uint64. + /// + /// + public long ReadInt64Unpacked() => (long)ReadUInt64Unpacked(); + + /// + /// Reads a uint64. + /// + /// + [DefaultReader] + public long ReadInt64() => (long)ReadSignedPackedWhole(); + + /// + /// Reads an int64. + /// + /// + public ulong ReadUInt64Unpacked() + { + 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 an int64. + /// + /// + [DefaultReader] + public ulong ReadUInt64() => ReadUnsignedPackedWhole(); + + /// + /// Reads a single. + /// + /// + public float ReadSingleUnpacked() + { + UIntFloat converter = new(); + converter.UIntValue = ReadUInt32Unpacked(); + return converter.FloatValue; + } + + /// + /// Reads a single. + /// + /// + [DefaultReader] + public float ReadSingle() => ReadSingleUnpacked(); + + /// + /// Reads a double. + /// + /// + public double ReadDoubleUnpacked() + { + UIntDouble converter = new(); + converter.LongValue = ReadUInt64Unpacked(); + return converter.DoubleValue; + } + + /// + /// Reads a double. + /// + /// + [DefaultReader] + public double ReadDouble() => ReadDoubleUnpacked(); + + /// + /// Reads a decimal. + /// + /// + public decimal ReadDecimalUnpacked() + { + UIntDecimal converter = new(); + converter.LongValue1 = ReadUInt64Unpacked(); + converter.LongValue2 = ReadUInt64Unpacked(); + return converter.DecimalValue; + } + + /// + /// Reads a decimal. + /// + /// + [DefaultReader] + public decimal ReadDecimal() => ReadDecimalUnpacked(); + + [Obsolete("use ReadStringAllocated.")] + public string ReadString() => ReadStringAllocated(); + /// + /// Reads a string. + /// + /// + [DefaultReader] + public string ReadStringAllocated() + { + int length = ReadInt32(); + //Null string. + if (length == Writer.UNSET_COLLECTION_SIZE_VALUE) + return null; + + if (length == 0) + return string.Empty; + if (!CheckAllocationAttack(length)) + return string.Empty; + + ArraySegment data = ReadArraySegment(length); + return data.Array.ToString(data.Offset, data.Count); + } + + [Obsolete("Use ReadUInt8ArrayAndSizeAllocated.")] + public byte[] ReadBytesAndSizeAllocated() => ReadUInt8ArrayAndSizeAllocated(); + + /// + /// Creates a byte array and reads bytes and size into it. + /// + /// + [DefaultReader] + public byte[] ReadUInt8ArrayAndSizeAllocated() + { + int size = ReadInt32(); + if (size == Writer.UNSET_COLLECTION_SIZE_VALUE) + return null; + + return ReadUInt8ArrayAllocated(size); + } + + [Obsolete("Use ReadUInt8ArrayAndSize.")] + public int ReadBytesAndSize(ref byte[] target) => ReadUInt8ArrayAndSize(ref target); + + /// + /// Reads bytes and size and copies results into target. Returns UNSET if null was written. + /// + /// Bytes read. + public int ReadUInt8ArrayAndSize(ref byte[] target) + { + int size = ReadInt32(); + if (size > 0) + ReadUInt8Array(ref target, size); + + return size; + } + + /// + /// Reads bytes and size and returns as an ArraySegment. + /// + /// + [DefaultReader] + 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) + return default; + + return ReadArraySegment(size); + } + + /// + /// Reads a Vector2. + /// + /// + public Vector2 ReadVector2Unpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked()); + + /// + /// Reads a Vector2. + /// + /// + [DefaultReader] + public Vector2 ReadVector2() => ReadVector2Unpacked(); + + /// + /// Reads a Vector3. + /// + /// + public Vector3 ReadVector3Unpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); + + /// + /// Reads a Vector3. + /// + /// + [DefaultReader] + public Vector3 ReadVector3() => ReadVector3Unpacked(); + + /// + /// Reads a Vector4. + /// + /// + public Vector4 ReadVector4Unpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); + + /// + /// Reads a Vector4. + /// + /// + [DefaultReader] + public Vector4 ReadVector4() => ReadVector4Unpacked(); + + /// + /// Reads a Vector2Int. + /// + /// + public Vector2Int ReadVector2IntUnpacked() => new(ReadInt32Unpacked(), ReadInt32Unpacked()); + + /// + /// Reads a Vector2Int. + /// + /// + [DefaultReader] + public Vector2Int ReadVector2Int() => new((int)ReadSignedPackedWhole(), (int)ReadSignedPackedWhole()); + + /// + /// Reads a Vector3Int. + /// + /// + public Vector3Int ReadVector3IntUnpacked() => new(ReadInt32Unpacked(), ReadInt32Unpacked(), ReadInt32Unpacked()); + + /// + /// Reads a Vector3Int. + /// + /// + [DefaultReader] + public Vector3Int ReadVector3Int() => new((int)ReadSignedPackedWhole(), (int)ReadSignedPackedWhole(), (int)ReadSignedPackedWhole()); + + /// + /// Reads a color. + /// + /// + public Color ReadColorUnpacked() + { + float r = ReadSingleUnpacked(); + float g = ReadSingleUnpacked(); + float b = ReadSingleUnpacked(); + float a = ReadSingleUnpacked(); + + return new(r, g, b, a); + } + + /// + /// Reads a color. + /// + /// + [DefaultReader] + public Color ReadColor() + { + float r = (float)(ReadUInt8Unpacked() / 100f); + float g = (float)(ReadUInt8Unpacked() / 100f); + float b = (float)(ReadUInt8Unpacked() / 100f); + float a = (float)(ReadUInt8Unpacked() / 100f); + + return new(r, g, b, a); + } + + /// + /// Reads a Color32. + /// + /// + [DefaultReader] + public Color32 ReadColor32() => new(ReadUInt8Unpacked(), ReadUInt8Unpacked(), ReadUInt8Unpacked(), ReadUInt8Unpacked()); + + /// + /// Reads a Quaternion. + /// + /// + public Quaternion ReadQuaternionUnpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); + + /// + /// Reads a Quaternion. + /// + /// + public Quaternion ReadQuaternion64() + { + ulong result = ReadUInt64Unpacked(); + return Quaternion64Compression.Decompress(result); + } + + /// + /// Reads a Quaternion. + /// + /// + [DefaultReader] + public Quaternion ReadQuaternion32() + { + return Quaternion32Compression.Decompress(this); + } + + /// + /// Reads a Quaternion. + /// + /// + internal Quaternion ReadQuaternion(AutoPackType autoPackType) + { + switch (autoPackType) + { + case AutoPackType.Packed: + return ReadQuaternion32(); + case AutoPackType.PackedLess: + return ReadQuaternion64(); + default: + return ReadQuaternionUnpacked(); + } + } + + /// + /// Reads a Rect. + /// + /// + public Rect ReadRectUnpacked() => new(ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked(), ReadSingleUnpacked()); + + /// + /// Reads a Rect. + /// + /// + [DefaultReader] + public Rect ReadRect() => ReadRectUnpacked(); + + /// + /// Plane. + /// + /// + public Plane ReadPlaneUnpacked() => new(ReadVector3Unpacked(), ReadSingleUnpacked()); + + /// + /// Plane. + /// + /// + [DefaultReader] + public Plane ReadPlane() => ReadPlaneUnpacked(); + + /// + /// Reads a Ray. + /// + /// + public Ray ReadRayUnpacked() + { + Vector3 position = ReadVector3Unpacked(); + Vector3 direction = ReadVector3Unpacked(); + return new(position, direction); + } + + /// + /// Reads a Ray. + /// + /// + [DefaultReader] + public Ray ReadRay() => ReadRayUnpacked(); + + /// + /// Reads a Ray. + /// + /// + public Ray2D ReadRay2DUnpacked() + { + Vector3 position = ReadVector2Unpacked(); + Vector2 direction = ReadVector2Unpacked(); + return new(position, direction); + } + + /// + /// Reads a Ray. + /// + /// + [DefaultReader] + public Ray2D ReadRay2D() => ReadRay2DUnpacked(); + + /// + /// Reads a Matrix4x4. + /// + /// + public Matrix4x4 ReadMatrix4x4Unpacked() + { + Matrix4x4 result = new() + { + m00 = ReadSingleUnpacked(), + m01 = ReadSingleUnpacked(), + m02 = ReadSingleUnpacked(), + m03 = ReadSingleUnpacked(), + m10 = ReadSingleUnpacked(), + m11 = ReadSingleUnpacked(), + m12 = ReadSingleUnpacked(), + m13 = ReadSingleUnpacked(), + m20 = ReadSingleUnpacked(), + m21 = ReadSingleUnpacked(), + m22 = ReadSingleUnpacked(), + m23 = ReadSingleUnpacked(), + m30 = ReadSingleUnpacked(), + m31 = ReadSingleUnpacked(), + m32 = ReadSingleUnpacked(), + m33 = ReadSingleUnpacked() + }; + + return result; + } + + /// + /// Reads a Matrix4x4. + /// + /// + [DefaultReader] + public Matrix4x4 ReadMatrix4x4() => ReadMatrix4x4Unpacked(); + + /// + /// Creates a new byte array and reads bytes into it. + /// + /// + /// + public byte[] ReadUInt8ArrayAllocated(int count) + { + if (count < 0) + { + NetworkManager.Log($"Bytes count cannot be less than 0."); + //Purge renaming and return default. + Position += Remaining; + return default; + } + + + byte[] bytes = new byte[count]; + ReadUInt8Array(ref bytes, count); + return bytes; + } + + /// + /// Reads a Guid. + /// + /// + [DefaultReader] + public System.Guid ReadGuid() + { + byte[] buffer = _guidBuffer; + ReadUInt8Array(ref buffer, 16); + return new(buffer); + } + + /// + /// Reads a tick without packing. + /// + public uint ReadTickUnpacked() => ReadUInt32Unpacked(); + + /// + /// Reads a GameObject. + /// + /// + [DefaultReader] + public GameObject ReadGameObject() + { + byte writtenType = ReadUInt8Unpacked(); + + GameObject result; + //Do nothing for 0, as it indicates null. + if (writtenType == 0) + { + result = null; + } + //1 indicates a networkObject. + else if (writtenType == 1) + { + NetworkObject nob = ReadNetworkObject(); + result = (nob == null) ? null : nob.gameObject; + } + //2 indicates a networkBehaviour. + else if (writtenType == 2) + { + NetworkBehaviour nb = ReadNetworkBehaviour(); + result = (nb == null) ? null : nb.gameObject; + } + else + { + result = null; + NetworkManager.LogError($"Unhandled ReadGameObject type of {writtenType}."); + } + + return result; + } + + /// + /// Reads a Transform. + /// + /// + [DefaultReader] + public Transform ReadTransform() + { + NetworkObject nob = ReadNetworkObject(); + return (nob == null) ? null : nob.transform; + } + + /// + /// Reads a NetworkObject. + /// + /// + [DefaultReader] + public NetworkObject ReadNetworkObject() => ReadNetworkObject(out _); + + /// + /// Reads a NetworkObject. + /// + /// + public NetworkObject ReadNetworkObject(bool logException) => ReadNetworkObject(out _, null, logException); + + /// + /// Reads a NetworkObject. + /// + /// Objects which have been read to be spawned this tick, but may not have spawned yet. + /// + public NetworkObject ReadNetworkObject(out int objectOrPrefabId, HashSet readSpawningObjects = null, bool logException = true) + { +#if DEVELOPMENT + 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); + + if (result == null && !isServer) + { + if (logException && (readSpawningObjects == null || !readSpawningObjects.Contains(objectOrPrefabId))) + NetworkManager.LogWarning($"Spawned NetworkObject was expected to exist but does not for Id {objectOrPrefabId}. This may occur if you sent a NetworkObject reference which does not exist, be it destroyed or if the client does not have visibility."); + } + } + //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 DEVELOPMENT + LastNetworkObject = result; +#endif + return result; + } + + /// + /// Reads a NetworkObjectId and nothing else. + /// + /// + public int ReadNetworkObjectId() => (int)ReadSignedPackedWhole(); + + /// + /// Reads the Id for a NetworkObject and outputs spawn settings. + /// + /// + internal int ReadNetworkObjectForSpawn(out int initializeOrder, out ushort collectionid) + { + int objectId = ReadNetworkObjectId(); + collectionid = ReadUInt16(); + initializeOrder = ReadInt32(); + + return objectId; + } + + /// + /// Reads the Id for a NetworkObject and outputs despawn settings. + /// + /// + internal int ReadNetworkObjectForDespawn(out DespawnType dt) + { + int objectId = ReadNetworkObjectId(); + dt = (DespawnType)ReadUInt8Unpacked(); + return objectId; + } + + /// + /// Reads a NetworkBehaviourId and ObjectId. + /// + /// + internal byte ReadNetworkBehaviourId(out int objectId) + { + objectId = ReadNetworkObjectId(); + if (objectId != NetworkObject.UNSET_OBJECTID_VALUE) + return ReadUInt8Unpacked(); + else + return 0; + } + + /// + /// Reads a NetworkBehaviour. + /// + /// Objects which have been read to be spawned this tick, but may not have spawned yet. + /// + public NetworkBehaviour ReadNetworkBehaviour(out int objectId, out byte componentIndex, HashSet readSpawningObjects = null, bool logException = true) + { + NetworkObject nob = ReadNetworkObject(out objectId, readSpawningObjects, logException); + componentIndex = ReadUInt8Unpacked(); + + NetworkBehaviour result; + if (nob == null) + { + result = null; + } + else + { + if (componentIndex >= nob.NetworkBehaviours.Count) + { + 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 DEVELOPMENT + LastNetworkBehaviour = result; +#endif + return result; + } + + /// + /// Reads a NetworkBehaviour. + /// + /// + [DefaultReader] + public NetworkBehaviour ReadNetworkBehaviour() + { + return ReadNetworkBehaviour(out _, out _); + } + + public NetworkBehaviour ReadNetworkBehaviour(bool logException) + { + return ReadNetworkBehaviour(out _, out _, null, logException); + } + + /// + /// Reads a NetworkBehaviourId. + /// + public byte ReadNetworkBehaviourId() => ReadUInt8Unpacked(); + + /// + /// Reads a DateTime. + /// + /// + [DefaultReader] + public DateTime ReadDateTime() + { + long value = (long)ReadSignedPackedWhole(); + DateTime result = DateTime.FromBinary(value); + return result; + } + + /// + /// Reads a transport channel. + /// + /// + [DefaultReader] + public Channel ReadChannel() + { + return (Channel)ReadUInt8Unpacked(); + } + + /// + /// Reads the Id for a NetworkConnection. + /// + /// + public int ReadNetworkConnectionId() => (int)ReadSignedPackedWhole(); + + /// + /// Reads a LayerMask. + /// + /// + [DefaultReader] + public LayerMask ReadLayerMask() + { + int layerValue = (int)ReadSignedPackedWhole(); + return (LayerMask)layerValue; + } + + /// + /// Reads a NetworkConnection. + /// + /// + [DefaultReader] + public NetworkConnection ReadNetworkConnection() + { + int value = ReadNetworkConnectionId(); + if (value == NetworkConnection.UNSET_CLIENTID_VALUE) + { + return FishNet.Managing.NetworkManager.EmptyConnection; + } + else + { + //Prefer server. + if (NetworkManager.IsServerStarted) + { + NetworkConnection result; + if (NetworkManager.ServerManager.Clients.TryGetValueIL2CPP(value, out result)) + { + return result; + } + //If also client then try client side data. + else if (NetworkManager.IsClientStarted) + { + //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(NetworkManager, value, -1, 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(NetworkManager, value, -1, true); + } + } + } + + /// + /// Reads TransformProperties. + /// + [DefaultReader] + public TransformProperties ReadTransformProperties() + { + Vector3 position = ReadVector3(); + Quaternion rotation = ReadQuaternion32(); + Vector3 scale = ReadVector3(); + + return new(position, rotation, scale); + } + + /// + /// Checks if the size could possibly be an allocation attack. + /// + /// + 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; + } + + /// + /// Reads a state update packet. + /// + /// + internal void ReadStateUpdatePacket(out uint clientTick) + { + clientTick = ReadTickUnpacked(); + } + + #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 and applies zigzag decoding. + /// + public long ReadSignedPackedWhole() => (long)ZigZagDecode(ReadUnsignedPackedWhole()); + + /// + /// Reads a packed whole number. + /// + public ulong ReadUnsignedPackedWhole() + { + int shift = 0; + ulong value = 0; + /* Maximum number of bytes for ulong. + * Prevents endless loop. Should not be neccessary but is a nice precaution. */ + int maximumIterations = 10; + int iterations = 0; + int bufferLength = GetBuffer().Length; + + + while (iterations < maximumIterations) + { + if (Position >= bufferLength) + { + NetworkManager.LogError($"Read position of {Position} is beyond reader's buffer length of {bufferLength}."); + return 0; + } + + byte currentByte = _buffer[Position++]; + value |= (ulong)(currentByte & 0x7F) << shift; + + if ((currentByte & 0x80) == 0) + break; + + shift += 7; + iterations++; + } + + return value; + } + #endregion + + #region Generators. + /// + /// Reads a reconcile. + /// + /// + /// + internal T ReadReconcile() => Read(); + + /// + /// Reads a replicate along with it's past replicates into a collection. + /// + internal List> ReadReplicate(uint tick) where T : IReplicateData, new() + { + List> collection = CollectionCaches>.RetrieveList(); + + //Number of entries written. + int count = (int)ReadUInt8Unpacked(); + if (count <= 0) + { + NetworkManager.Log($"Replicate count cannot be 0 or less."); + //Purge renaming and return default. + Position += Remaining; + return collection; + } + /* 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); + + for (int i = 0; i < count; i++) + { + ReplicateDataContainer value = ReadReplicateData(tick + (uint)i); + //Assign to collection. + collection.Add(value); + } + + return collection; + } + + /// + /// Reads a ReplicateData and applies tick and channel. + /// + private ReplicateDataContainer ReadReplicateData(uint tick) where T : IReplicateData, new() + { + T data = Read(); + Channel c = ReadChannel(); + return new(data, c, tick, isCreated: true); + } + + /// + /// Reads a collection using a collection from caches. + /// + public Dictionary ReadDictionary() + { + int count = (int)ReadSignedPackedWhole(); + + //Null collection. + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + return null; + + Dictionary collection = CollectionCaches.RetrieveDictionary(); + ReadDictionary(count, collection); + + return collection; + } + + /// + /// Reads a collection. + /// + [Obsolete("Use ReadDictionary.")] + public Dictionary ReadDictionaryAllocated() => ReadDictionary(); + + /// + /// 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. + public int ReadDictionary(ref Dictionary collection, bool allowNullification = false) + { + int count = (int)ReadSignedPackedWhole(); + + //Null collection. + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + { + if (allowNullification) + collection = null; + + return count; + } + + ReadDictionary(count, collection); + + return count; + } + + /// + /// Reads into a collection. + /// + private void ReadDictionary(int count, Dictionary collection) + { + if (count < 0) + { + NetworkManager.LogError($"Collection count cannot be less than 0."); + //Purge renaming and return default. + Position += Remaining; + + return; + } + + if (collection == null) + collection = new(count); + else + collection.Clear(); + + for (int i = 0; i < count; i++) + { + TKey key = Read(); + TValue value = Read(); + collection.Add(key, value); + } + } + + /// + /// Reads a collection using a collection from caches. + /// + public List ReadList() + { + int count = (int)ReadSignedPackedWhole(); + + //Null collection. + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + return null; + + List result = CollectionCaches.RetrieveList(); + ReadList(count, result); + + return result; + } + + /// + /// Reads a collection with allocations. + /// + [Obsolete("Use ReadList.")] + public List ReadListAllocated() => ReadList(); + + /// + /// 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. + public int ReadList(ref List collection, bool allowNullification = false) + { + int count = (int)ReadSignedPackedWhole(); + + //Null collection. + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + { + if (allowNullification) + collection = null; + + return count; + } + + ReadList(count, collection); + + return count; + } + + /// + /// Reads into a collection. + /// + private void ReadList(int count, List collection) + { + if (count < 0) + { + NetworkManager.LogError($"List count cannot be less than 0."); + //Purge renaming and return default. + Position += Remaining; + + return; + } + + if (collection == null) + collection = new(count); + else + collection.Clear(); + + + for (int i = 0; i < count; i++) + collection.Add(Read()); + } + + /// + /// Reads a collection. + /// + public T[] ReadArrayAllocated() + { + T[] result = null; + ReadArray(ref result); + + return result; + } + + /// + /// Reads into collection and returns amount read. + /// + /// + /// + /// + public int ReadArray(ref T[] collection) + { + int count = (int)ReadSignedPackedWhole(); + + if (count == Writer.UNSET_COLLECTION_SIZE_VALUE) + return 0; + + if (count == 0) + { + if (collection == null) + collection = new T[0]; + + return 0; + } + + if (count < 0) + { + NetworkManager.Log($"Array count cannot be less than 0."); + //Purge renaming and return default. + Position += Remaining; + return default; + } + + //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 as packed. + /// + public T Read() + { + Func del = GenericReader.Read; + + if (del == null) + { + NetworkManager.LogError($"Read method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer."); + return default; + } + else + { + return del.Invoke(this); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Reader.cs.meta b/Assets/FishNet/Runtime/Serializing/Reader.cs.meta new file mode 100644 index 0000000..aff79de --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Reader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 680939c6cee93b64ba149da2029f4308 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Reader.cs + uploadId: 762203 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..06cff39 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: abcc77fe436138b4082ee27da3055bb3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/ReaderExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/ReaderPool.cs b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs new file mode 100644 index 0000000..bf86952 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs @@ -0,0 +1,82 @@ +using FishNet.Managing; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using GameKit.Dependencies.Utilities; + +namespace FishNet.Serializing +{ + /// + /// Reader which is reused to save on garbage collection and performance. + /// + public sealed class PooledReader : Reader//, IResettable + { + public PooledReader() { } + internal PooledReader(byte[] bytes, NetworkManager networkManager, Reader.DataSource source = Reader.DataSource.Unset) : base(bytes, networkManager, null, source) { } + internal PooledReader(ArraySegment segment, NetworkManager networkManager, Reader.DataSource source = Reader.DataSource.Unset) : base(segment, networkManager, null, source) { } + public void Store() => ReaderPool.Store(this); + + [Obsolete("Use Clear instead.")] + public void ResetState() => base.Clear(); + [Obsolete("This does not function.")] + public void InitializeState() { } + } + + /// + /// Collection of PooledReader. Stores and gets PooledReader. + /// + public static class ReaderPool + { + #region Private. + /// + /// Pool of readers. + /// + private static readonly Stack _pool = new(); + #endregion + + /// + /// Get the next reader in the pool + /// If pool is empty, creates a new Reader + /// + + public static PooledReader Retrieve(byte[] bytes, NetworkManager networkManager, Reader.DataSource source = Reader.DataSource.Unset) + { + return Retrieve(new ArraySegment(bytes), networkManager, source); + } + + /// + /// Get the next reader in the pool or creates a new one if none are available. + /// + public static PooledReader Retrieve(ArraySegment segment, NetworkManager networkManager, Reader.DataSource source = Reader.DataSource.Unset) + { + PooledReader result; + if (_pool.TryPop(out result)) + result.Initialize(segment, networkManager, source); + else + result = new(segment, networkManager, source); + + return result; + } + + + /// + /// Puts reader back into pool + /// + public static void Store(PooledReader reader) + { + _pool.Push(reader); + } + + /// + /// Puts reader back into pool if not null, and nullifies source reference. + /// + public static void StoreAndDefault(ref PooledReader reader) + { + if (reader != null) + { + _pool.Push(reader); + reader = null; + } + } + } +} diff --git a/Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta new file mode 100644 index 0000000..0a2f09f --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 318b117dd2ebd1b4b9e2021796b45eee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/ReaderPool.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs new file mode 100644 index 0000000..f62afdf --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs @@ -0,0 +1 @@ +//Remove in V5 \ 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..de85b45 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/ReaderStatics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c1f98beaf8f697d4b8bb1e6b6ef32d42 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/ReaderStatics.cs + uploadId: 762203 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..658cd4b --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SceneComparer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 161dbbe3995ff53479fc4e259f86549d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/SceneComparer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/SubStream.meta b/Assets/FishNet/Runtime/Serializing/SubStream.meta new file mode 100644 index 0000000..f38de49 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 70577ba991466c849818d6af55bb4f9f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs b/Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs new file mode 100644 index 0000000..5944971 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs @@ -0,0 +1,26 @@ +namespace FishNet.Serializing +{ + + public partial class Reader + { + /// + /// Reads a substream. Start reading from it with StartReading method. + /// + /// Returns SubStream + public SubStream ReadSubStream() + { + // read length of subStream + int streamLength = ReadInt32(); + + // if length is -1, it is invalid + if (streamLength == SubStream.UNINITIALIZED_LENGTH) + { + // returns Uninitialized SubStream + return SubStream.GetUninitialized(); + } + + return SubStream.CreateFromReader(this, streamLength); + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs.meta b/Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs.meta new file mode 100644 index 0000000..8c78a02 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 286543df642a48448ae2ef943576e242 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/SubStream/Reader.SubStream.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs b/Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs new file mode 100644 index 0000000..a03a588 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs @@ -0,0 +1,241 @@ +using System; +using FishNet.Managing; +using GameKit.Dependencies.Utilities; + +namespace FishNet.Serializing +{ + + /// + /// Special reader/writer buffer struct that can be used in Fishnet RPCs or Broadcasts, as arguments or part of structs + /// + /// Use cases: + /// - replacement for stream sort of + /// - instead of always allocating some arrays T[] and sending that over RPCs/Broadcast, you can use SubStream + /// - you can pass SubStream into objects via reference 'ref', and those objects write/read state, useful for dynamic length reconcile (items, inventory, buffs, etc...) + /// - sending data inside OnServerSpawn to clients via TargetRPC + /// - instead of writting custom serializers for big struct, you can use SubStream inside RPCs/Broadcasts + /// + /// Pros: + /// - reading is zero copy, reads directly from FishNet buffers + /// - everything is pooled + /// - ease of use + /// - SubStream can also be left uninitialized (default) + /// - Can work safely with multiple receivers in Broadcasts, as long as you read data in the same order + /// Cons: + /// - no reading over length protection, you have to know how much data you are reading, due to buffer being red can be larger than substreams buffer + /// - writing buffers are also pooled, but there is a copy (since you write into it, then what is written is copied into fishnet internal buffer, but it's byte copy (fast) + /// - have to use Dispose() to return buffers to pool, or it may result in memory leak + /// - reading in multiple receiver methods (for same client) in Broadcasts, you have extra deserialization processing per each method + /// - might be unsafe to use this to send from clients (undefined data length), but so is sending T[] or List from clients + /// - not to be used for IReplicateData/input structs, because underlying reading buffer may be changed where as IReplicateData structs are stored internally in replay buffer (substream buffer is not) + /// + /// Note: + /// - If you write/read custom structs ONLY via SubStream, automatic serializer will not pick those up. Mark those custom structs with [FishNet.CodeGenerating.IncludeSerialization]. + /// Codegen detects only custom structs that are used in RPC/Broadcast methods, not in SubStream. + /// + /// + public struct SubStream : IResettable + { + /// + /// Is Substream initialized (can be read from or written to) + /// + public bool Initialized { get; private set; } + + /// + /// Returns Length of substream data + /// + public int Length + { + get + { + if (_writer != null) + return _writer.Length; + if (_reader != null) + return _reader.Length; + + return UNINITIALIZED_LENGTH; + } + } + + /// + /// Returns remaining bytes to read from substream + /// + public int Remaining => (_reader != null) ? _reader.Remaining : UNINITIALIZED_LENGTH; + + /// + /// Returns NetworkManager that Substream was initialized with + /// + public NetworkManager NetworkManager + { + get + { + if (_writer != null) + return _writer.NetworkManager; + if (_reader != null) + return _reader.NetworkManager; + + return null; + } + } + + private PooledReader _reader; + private int _startPosition; + private PooledWriter _writer; + private bool _disposed; + + /// + /// Length to use when SubStream is not initialized. + /// + public const int UNINITIALIZED_LENGTH = -1; + + /// + /// Creates SubStream for writing, use this before sending into RPC or Broadcast + /// + /// Need to include network manager for handling of networked IDs + /// Minimum expected length of data, that will be written + /// Returns writer of SubStream + public static SubStream StartWriting(NetworkManager manager, out PooledWriter writer, int minimumLength = 0) + { + if (minimumLength == 0) + writer = WriterPool.Retrieve(manager); + else + writer = WriterPool.Retrieve(manager, minimumLength); + + SubStream stream = new() + { + _writer = writer, + Initialized = true, + }; + + return stream; + } + + /// + /// Starts reading from substream via Reader class. Do not forget do Dispose() after reading + /// + /// Reader to read data from + /// Returns true, if SubStream is initialized else false + public bool StartReading(out Reader reader) + { + if (Initialized) + { + // reset reader, in case we are reading in multiple broadcasts delegates/events + _reader.Position = _startPosition; + reader = _reader; + return true; + } + reader = null; + return false; + } + + public static SubStream CreateFromReader(Reader originalReader, int subStreamLength) + { + if (subStreamLength < 0) + { + NetworkManagerExtensions.LogError("SubStream length cannot be less than 0"); + return default; + } + + byte[] originalReaderBuffer = originalReader.GetBuffer(); + + // inherits reading buffer directly from fishnet reader + ArraySegment arraySegment = new(originalReaderBuffer, originalReader.Position, subStreamLength); + + PooledReader newReader = ReaderPool.Retrieve(arraySegment, originalReader.NetworkManager); + + // advance original reader by length of substream data + originalReader.Skip(subStreamLength); + + return new() + { + _startPosition = newReader.Position, + _reader = newReader, + _writer = null, + _disposed = false, + Initialized = true, + }; + } + + /// + /// Resets reader to start position, so you can read data again from start of substream. + /// + /// + public void ResetReaderToStartPosition() + { + if (_reader != null) + _reader.Position = _startPosition; + else + NetworkManager.LogError("SubStream was not initialized as reader!"); + } + + /// + /// Used internally to get writer of SubStream + /// + /// + internal PooledWriter GetWriter() + { + if (!Initialized) + NetworkManager.LogError("SubStream was not initialized, it has to be initialized properly either localy or remotely!"); + else if (_writer == null) + NetworkManager.LogError($"GetWriter() requires SubStream to be initialized as writer! You have to create SubStream with {nameof(StartWriting)}()!"); + + return _writer; + } + + internal PooledReader GetReader() + { + if (!Initialized) + NetworkManager.LogError("SubStream was not initialized, it has to be initialized properly either localy or remotely!"); + if (_reader == null) + NetworkManager.LogError($"GetReader() requires SubStream to be initialized as reader!"); + + return _reader; + } + + /// + /// Returns uninitialized SubStream. Can send safely over network, but cannot be read from (StartReading will return false). + /// You can also use 'var stream = default;' instead. + /// + /// Empty SubStream + internal static SubStream GetUninitialized() + { + return new() + { + Initialized = false, + }; + } + + /// + /// Do not forget to call this after: + /// - you stopped writing to Substream AND already sent it via RPCs/Broadcasts + /// - you stoped reading from it inside RPCs/Broadcast receive event + /// - if you use it in Reconcile method, you have dispose SubStream inside Dispose() of IReconcileData struct + /// + public void ResetState() + { + if (!_disposed) // dispose reader only once + { + _disposed = true; + + if (_reader != null) + { + _reader.Store(); + _reader = null; + } + } + + if (_writer != null) + { + if (_writer.Length < WriterPool.LENGTH_BRACKET) // 1000 is LENGTH_BRACKET + _writer.Store(); + else + _writer.StoreLength(); + + _writer = null; + } + } + + public void InitializeState() { } + } + +} diff --git a/Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs.meta b/Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs.meta new file mode 100644 index 0000000..019059b --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 384099d97a6ecd148a73ef36bed6461e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/SubStream/SubStream.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs b/Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs new file mode 100644 index 0000000..38e5ce3 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs @@ -0,0 +1,28 @@ +namespace FishNet.Serializing +{ + + public partial class Writer + { + /// + /// Writes a SubStream. + /// + /// Substream + public void WriteSubStream(SubStream value) + { + // Uninitialized substream, write Length as -1 + if (!value.Initialized) + { + WriteInt32(SubStream.UNINITIALIZED_LENGTH); + } + else + { + PooledWriter bufferWriter = value.GetWriter(); + + // Write length and data + WriteInt32(bufferWriter.Length); + WriteUInt8Array(bufferWriter.GetBuffer(), 0, bufferWriter.Length); + } + } + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs.meta b/Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs.meta new file mode 100644 index 0000000..880f853 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f86fddaee91a2f246b421c95475e700f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/SubStream/Writer.SubStream.cs + uploadId: 762203 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..b096a5f --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/TransformPackingData.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 80a80dabe02daf6428cce0f16ea49877 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/TransformPackingData.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics.meta new file mode 100644 index 0000000..7d2409e --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4aaaca3090257be40b80f33b9e955446 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs new file mode 100644 index 0000000..5cebeb9 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs @@ -0,0 +1,470 @@ +#if UNITYMATHEMATICS + +using Unity.Mathematics; + +namespace FishNet.Serializing { + + public partial class Writer { + + public void Writebool2(bool2 value) { + + byte b = 0; + + if (value.x) + b |= 1; + if (value.y) + b |= 2; + + WriteUInt8Unpacked(b); + } + + public void Writebool3(bool3 value) { + + byte b = 0; + + if (value.x) + b |= 1; + if (value.y) + b |= 2; + if (value.z) + b |= 4; + + WriteUInt8Unpacked(b); + } + + public void Writebool4(bool4 value) { + byte b = 0; + + if (value.x) + b |= 1; + if (value.y) + b |= 2; + if (value.z) + b |= 4; + if (value.w) + b |= 8; + + WriteUInt8Unpacked(b); + } + + public void Writebool2x2(bool2x2 value) { + + byte b = 0; + + if (value.c0.x) + b |= 1; + if (value.c0.y) + b |= 2; + if (value.c1.x) + b |= 4; + if (value.c1.y) + b |= 8; + + WriteUInt8Unpacked(b); + } + + public void Writebool2x3(bool2x3 value) { + byte b = 0; + + if (value.c0.x) + b |= 1; + if (value.c0.y) + b |= 2; + if (value.c1.x) + b |= 4; + if (value.c1.y) + b |= 8; + if (value.c2.x) + b |= 16; + if (value.c2.y) + b |= 32; + + WriteUInt8Unpacked(b); + } + + public void Writebool2x4(bool2x4 value) { + byte b = 0; + + if (value.c0.x) + b |= 1; + if (value.c0.y) + b |= 2; + if (value.c1.x) + b |= 4; + if (value.c1.y) + b |= 8; + if (value.c2.x) + b |= 16; + if (value.c2.y) + b |= 32; + if (value.c3.x) + b |= 64; + if (value.c3.y) + b |= 128; + + WriteUInt8Unpacked(b); + } + + public void Writebool3x2(bool3x2 value) { + byte b = 0; + + if (value.c0.x) + b |= 1; + if (value.c0.y) + b |= 2; + if (value.c0.z) + b |= 4; + if (value.c1.x) + b |= 8; + if (value.c1.y) + b |= 16; + if (value.c1.z) + b |= 32; + + WriteUInt8Unpacked(b); + } + + public void Writebool3x3(bool3x3 value) { + ushort s = 0; + + if (value.c0.x) + s |= 1; + if (value.c0.y) + s |= 2; + if (value.c0.z) + s |= 4; + if (value.c1.x) + s |= 8; + if (value.c1.y) + s |= 16; + if (value.c1.z) + s |= 32; + if (value.c2.x) + s |= 64; + if (value.c2.y) + s |= 128; + if (value.c2.z) + s |= 256; + + WriteUInt16(s); + } + + public void Writebool3x4(bool3x4 value) { + ushort s = 0; + + if (value.c0.x) + s |= 1; + if (value.c0.y) + s |= 2; + if (value.c0.z) + s |= 4; + if (value.c1.x) + s |= 8; + if (value.c1.y) + s |= 16; + if (value.c1.z) + s |= 32; + if (value.c2.x) + s |= 64; + if (value.c2.y) + s |= 128; + if (value.c2.z) + s |= 256; + if (value.c3.x) + s |= 512; + if (value.c3.y) + s |= 1024; + if (value.c3.z) + s |= 2048; + + WriteUInt16(s); + } + + public void Writebool4x2(bool4x2 value) { + byte b = 0; + + if (value.c0.x) + b |= 1; + if (value.c0.y) + b |= 2; + if (value.c0.z) + b |= 4; + if (value.c0.w) + b |= 8; + if (value.c1.x) + b |= 16; + if (value.c1.y) + b |= 32; + if (value.c1.z) + b |= 64; + if (value.c1.w) + b |= 128; + + WriteUInt8Unpacked(b); + } + + public void Writebool4x3(bool4x3 value) { + ushort s = 0; + + if (value.c0.x) + s |= 1; + if (value.c0.y) + s |= 2; + if (value.c0.z) + s |= 4; + if (value.c0.w) + s |= 8; + if (value.c1.x) + s |= 16; + if (value.c1.y) + s |= 32; + if (value.c1.z) + s |= 64; + if (value.c1.w) + s |= 128; + if (value.c2.x) + s |= 256; + if (value.c2.y) + s |= 512; + if (value.c2.z) + s |= 1024; + if (value.c2.w) + s |= 2048; + + WriteUInt16(s); + } + + public void Writebool4x4(bool4x4 value) { + ushort s = 0; + + if (value.c0.x) + s |= 1; + if (value.c0.y) + s |= 2; + if (value.c0.z) + s |= 4; + if (value.c0.w) + s |= 8; + if (value.c1.x) + s |= 16; + if (value.c1.y) + s |= 32; + if (value.c1.z) + s |= 64; + if (value.c1.w) + s |= 128; + if (value.c2.x) + s |= 256; + if (value.c2.y) + s |= 512; + if (value.c2.z) + s |= 1024; + if (value.c2.w) + s |= 2048; + if (value.c3.x) + s |= 4096; + if (value.c3.y) + s |= 8192; + if (value.c3.z) + s |= 16384; + if (value.c3.w) + s |= 32768; + + WriteUInt16(s); + } + } + + public partial class Reader { + public bool2 Readbool2() { + + byte b = ReadUInt8Unpacked(); + + return new bool2() { x = (b & 1) != 0, y = (b & 2) != 0 }; + } + + public bool3 Readbool3() { + + byte b = ReadUInt8Unpacked(); + + return new bool3() { + x = (b & 1) != 0, + y = (b & 2) != 0, + z = (b & 4) != 0 + }; + } + + public bool4 Readbool4() { + byte b = ReadUInt8Unpacked(); + + return new bool4 { + x = (b & 1) != 0, + y = (b & 2) != 0, + z = (b & 4) != 0, + w = (b & 8) != 0 + }; + } + + public bool2x2 Readbool2x2() { + byte b = ReadUInt8Unpacked(); + + bool2x2 value = default; + + value.c0.x = (b & 1) != 0; + value.c0.y = (b & 2) != 0; + value.c1.x = (b & 4) != 0; + value.c1.y = (b & 8) != 0; + + return value; + } + + public bool2x3 Readbool2x3() { + byte b = ReadUInt8Unpacked(); + + bool2x3 value = default; + + value.c0.x = (b & 1) != 0; + value.c0.y = (b & 2) != 0; + value.c1.x = (b & 4) != 0; + value.c1.y = (b & 8) != 0; + value.c2.x = (b & 16) != 0; + value.c2.y = (b & 32) != 0; + + return value; + } + + public bool2x4 Readbool2x4() { + byte b = ReadUInt8Unpacked(); + + bool2x4 value = default; + + value.c0.x = (b & 1) != 0; + value.c0.y = (b & 2) != 0; + value.c1.x = (b & 4) != 0; + value.c1.y = (b & 8) != 0; + value.c2.x = (b & 16) != 0; + value.c2.y = (b & 32) != 0; + value.c3.x = (b & 64) != 0; + value.c3.y = (b & 128) != 0; + + return value; + } + + public bool3x2 Readbool3x2() { + byte b = ReadUInt8Unpacked(); + + bool3x2 value = default; + + value.c0.x = (b & 1) != 0; + value.c0.y = (b & 2) != 0; + value.c0.z = (b & 4) != 0; + value.c1.x = (b & 8) != 0; + value.c1.y = (b & 16) != 0; + value.c1.z = (b & 32) != 0; + + return value; + } + + public bool3x3 Readbool3x3() { + ushort s = ReadUInt16(); + + bool3x3 value = default; + value.c0.x = (s & 1) != 0; + value.c0.y = (s & 2) != 0; + value.c0.z = (s & 4) != 0; + value.c1.x = (s & 8) != 0; + value.c1.y = (s & 16) != 0; + value.c1.z = (s & 32) != 0; + value.c2.x = (s & 64) != 0; + value.c2.y = (s & 128) != 0; + value.c2.z = (s & 256) != 0; + + return value; + } + + public bool3x4 Readbool3x4() { + ushort s = ReadUInt16(); + + bool3x4 value = default; + + value.c0.x = (s & 1) != 0; + value.c0.y = (s & 2) != 0; + value.c0.z = (s & 4) != 0; + value.c1.x = (s & 8) != 0; + value.c1.y = (s & 16) != 0; + value.c1.z = (s & 32) != 0; + value.c2.x = (s & 64) != 0; + value.c2.y = (s & 128) != 0; + value.c2.z = (s & 256) != 0; + value.c3.x = (s & 512) != 0; + value.c3.y = (s & 1024) != 0; + value.c3.z = (s & 2048) != 0; + + return value; + } + + public bool4x2 Readbool4x2() { + byte b = ReadUInt8Unpacked(); + + bool4x2 value = default; + + value.c0.x = (b & 1) != 0; + value.c0.y = (b & 2) != 0; + value.c0.z = (b & 4) != 0; + value.c0.w = (b & 8) != 0; + value.c1.x = (b & 16) != 0; + value.c1.y = (b & 32) != 0; + value.c1.z = (b & 64) != 0; + value.c1.w = (b & 128) != 0; + + return value; + } + + public bool4x3 Readbool4x3() { + ushort s = ReadUInt16(); + + bool4x3 value = default; + + value.c0.x = (s & 1) != 0; + value.c0.y = (s & 2) != 0; + value.c0.z = (s & 4) != 0; + value.c0.w = (s & 8) != 0; + value.c1.x = (s & 16) != 0; + value.c1.y = (s & 32) != 0; + value.c1.z = (s & 64) != 0; + value.c1.w = (s & 128) != 0; + value.c2.x = (s & 256) != 0; + value.c2.y = (s & 512) != 0; + value.c2.z = (s & 1024) != 0; + value.c2.w = (s & 2048) != 0; + + return value; + } + + public bool4x4 Readbool4x4() { + ushort s = ReadUInt16(); + + bool4x4 value = default; + + value.c0.x = (s & 1) != 0; + value.c0.y = (s & 2) != 0; + value.c0.z = (s & 4) != 0; + value.c0.w = (s & 8) != 0; + value.c1.x = (s & 16) != 0; + value.c1.y = (s & 32) != 0; + value.c1.z = (s & 64) != 0; + value.c1.w = (s & 128) != 0; + value.c2.x = (s & 256) != 0; + value.c2.y = (s & 512) != 0; + value.c2.z = (s & 1024) != 0; + value.c2.w = (s & 2048) != 0; + value.c3.x = (s & 4096) != 0; + value.c3.y = (s & 8192) != 0; + value.c3.z = (s & 16384) != 0; + value.c3.w = (s & 32768) != 0; + + return value; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs.meta new file mode 100644 index 0000000..f49f07c --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 254b9133ed0260b4685ea1d28bd15df1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsBoolean.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs new file mode 100644 index 0000000..361a3f1 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs @@ -0,0 +1,194 @@ +#if UNITYMATHEMATICS + +using System.Runtime.CompilerServices; + +using Unity.Mathematics; + +namespace FishNet.Serializing { + + public partial class Writer { + + + public void Writedouble2(double2 value) { + WriteDouble(value.x); + WriteDouble(value.y); + } + + + public void Writedouble3(double3 value) { + WriteDouble(value.x); + WriteDouble(value.y); + WriteDouble(value.z); + } + + + + public void Writedouble4(double4 value) { + WriteDouble(value.x); + WriteDouble(value.y); + WriteDouble(value.z); + WriteDouble(value.w); + } + + public void Writedouble2x2(double2x2 value) { + Writedouble2(value.c0); + Writedouble2(value.c1); + } + + public void Writedouble2x3(double2x3 value) { + Writedouble2(value.c0); + Writedouble2(value.c1); + Writedouble2(value.c2); + } + + public void Writedouble2x4(double2x4 value) { + Writedouble2(value.c0); + Writedouble2(value.c1); + Writedouble2(value.c2); + Writedouble2(value.c3); + } + + public void Writedouble3x2(double3x2 value) { + Writedouble3(value.c0); + Writedouble3(value.c1); + } + + public void Writedouble4x2(double4x2 value) { + Writedouble4(value.c0); + Writedouble4(value.c1); + } + + public void Writedouble3x4(double3x4 value) { + Writedouble3(value.c0); + Writedouble3(value.c1); + Writedouble3(value.c2); + Writedouble3(value.c3); + } + + public void Writedouble4x3(double4x3 value) { + Writedouble4(value.c0); + Writedouble4(value.c1); + Writedouble4(value.c2); + } + + public void Writedouble3x3(double3x3 value) { + Writedouble3(value.c0); + Writedouble3(value.c1); + Writedouble3(value.c2); + } + public void Writedouble4x4(double4x4 value) { + Writedouble4(value.c0); + Writedouble4(value.c1); + Writedouble4(value.c2); + Writedouble4(value.c3); + } + + } + + public partial class Reader { + + + public double2 Readdouble2() { + return new double2 { + x = ReadDouble(), + y = ReadDouble() + }; + } + + + public double3 Readdouble3() { + return new double3() { + x = ReadDouble(), + y = ReadDouble(), + z = ReadDouble() + }; + } + + + public double4 Readdouble4() { + return new double4() { + x = ReadDouble(), + y = ReadDouble(), + z = ReadDouble(), + w = ReadDouble() + }; + } + + public double2x2 Readdouble2x2() { + return new double2x2() { + c0 = Readdouble2(), + c1 = Readdouble2() + }; + } + + + public double2x3 Readdouble2x3() { + return new double2x3() { + c0 = Readdouble2(), + c1 = Readdouble2(), + c2 = Readdouble2() + }; + } + + public double2x4 Readdouble2x4() { + return new double2x4() { + c0 = Readdouble2(), + c1 = Readdouble2(), + c2 = Readdouble2(), + c3 = Readdouble2() + }; + } + + public double3x2 Readdouble3x2() { + return new double3x2() { + c0 = Readdouble3(), + c1 = Readdouble3() + }; + } + + public double4x2 Readdouble4x2() { + return new double4x2() { + c0 = Readdouble4(), + c1 = Readdouble4() + }; + } + + public double3x4 Readdouble3x4() { + return new double3x4() { + c0 = Readdouble3(), + c1 = Readdouble3(), + c2 = Readdouble3(), + c3 = Readdouble3() + }; + } + + + public double4x3 Readdouble4x3() { + return new double4x3() { + c0 = Readdouble4(), + c1 = Readdouble4(), + c2 = Readdouble4() + }; + } + public double3x3 Readdouble3x3() { + return new double3x3() { + c0 = Readdouble3(), + c1 = Readdouble3(), + c2 = Readdouble3() + }; + } + + + public double4x4 Readdouble4x4() { + return new double4x4() { + c0 = Readdouble4(), + c1 = Readdouble4(), + c2 = Readdouble4(), + c3 = Readdouble4() + }; + } + + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs.meta new file mode 100644 index 0000000..e85ab13 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5b02bb31d2808f94695dfa971bbe624b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsDouble.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs new file mode 100644 index 0000000..4846e4c --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs @@ -0,0 +1,192 @@ +#if UNITYMATHEMATICS + +using System.Runtime.CompilerServices; + +using Unity.Mathematics; + +namespace FishNet.Serializing { + + public partial class Writer { + + + public void Writefloat2(float2 value) { + WriteSingle(value.x); + WriteSingle(value.y); + } + + + public void Writefloat3(float3 value) { + WriteSingle(value.x); + WriteSingle(value.y); + WriteSingle(value.z); + } + + + public void Writefloat4(float4 value) { + WriteSingle(value.x); + WriteSingle(value.y); + WriteSingle(value.z); + WriteSingle(value.w); + } + + public void Writefloat2x2(float2x2 value) { + Writefloat2(value.c0); + Writefloat2(value.c1); + } + + public void Writefloat2x3(float2x3 value) { + Writefloat2(value.c0); + Writefloat2(value.c1); + Writefloat2(value.c2); + } + + public void Writefloat2x4(float2x4 value) { + Writefloat2(value.c0); + Writefloat2(value.c1); + Writefloat2(value.c2); + Writefloat2(value.c3); + } + + public void Writefloat3x2(float3x2 value) { + Writefloat3(value.c0); + Writefloat3(value.c1); + } + + public void Writefloat3x3(float3x3 value) { + Writefloat3(value.c0); + Writefloat3(value.c1); + Writefloat3(value.c2); + } + + public void Writefloat3x4(float3x4 value) { + Writefloat3(value.c0); + Writefloat3(value.c1); + Writefloat3(value.c2); + Writefloat3(value.c3); + } + + public void Writefloat4x2(float4x2 value) { + Writefloat4(value.c0); + Writefloat4(value.c1); + } + + public void Writefloat4x3(float4x3 value) { + Writefloat4(value.c0); + Writefloat4(value.c1); + Writefloat4(value.c2); + } + + public void Writefloat4x4(float4x4 value) { + Writefloat4(value.c0); + Writefloat4(value.c1); + Writefloat4(value.c2); + Writefloat4(value.c3); + } + + } + + public partial class Reader { + + + public float2 Readfloat2() { + return new float2 { + x = ReadSingle(), + y = ReadSingle() + }; + } + + + public float3 Readfloat3() { + return new float3() { + x = ReadSingle(), + y = ReadSingle(), + z = ReadSingle() + }; + } + + + public float4 Readfloat4() { + return new float4() { + x = ReadSingle(), + y = ReadSingle(), + z = ReadSingle(), + w = ReadSingle() + }; + } + + public float2x2 Readfloat2x2() { + return new float2x2() { + c0 = Readfloat2(), + c1 = Readfloat2() }; + } + + public float2x3 Readfloat2x3() { + return new float2x3() { + c0 = Readfloat2(), + c1 = Readfloat2(), + c2 = Readfloat2() + }; + } + + public float2x4 Readfloat2x4() { + return new float2x4() { + c0 = Readfloat2(), + c1 = Readfloat2(), + c2 = Readfloat2(), + c3 = Readfloat2() + }; + } + + public float3x2 Readfloat3x2() { + return new float3x2() { + c0 = Readfloat3(), + c1 = Readfloat3() + }; + } + + public float3x3 Readfloat3x3() { + return new float3x3() { + c0 = Readfloat3(), + c1 = Readfloat3(), + c2 = Readfloat3() + }; + } + + public float3x4 Readfloat3x4() { + return new float3x4() { + c0 = Readfloat3(), + c1 = Readfloat3(), + c2 = Readfloat3(), + c3 = Readfloat3() + }; + } + + public float4x2 Readfloat4x2() { + return new float4x2() { + c0 = Readfloat4(), + c1 = Readfloat4() + }; + } + + public float4x3 Readfloat4x3() { + return new float4x3() { + c0 = Readfloat4(), + c1 = Readfloat4(), + c2 = Readfloat4() + }; + } + + public float4x4 Readfloat4x4() { + return new float4x4() { + c0 = Readfloat4(), + c1 = Readfloat4(), + c2 = Readfloat4(), + c3 = Readfloat4() + }; + } + + } +} + + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs.meta new file mode 100644 index 0000000..c6f61c5 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 501c2baf76f23f64fadc0fd46e2d95fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsFloat.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs new file mode 100644 index 0000000..8da80b7 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs @@ -0,0 +1,84 @@ +#if UNITYMATHEMATICS + +using System.Runtime.CompilerServices; + +using Unity.Mathematics; + +namespace FishNet.Serializing { + + public partial class Writer { + + + public void Writehalf(half value) { + WriteUInt16(value.value); + } + + + public void Writehalf2(half2 value) { + WriteUInt16(value.x.value); + WriteUInt16(value.y.value); + } + + + public void Writehalf3(half3 value) { + WriteUInt16(value.x.value); + WriteUInt16(value.y.value); + WriteUInt16(value.z.value); + } + + + public void Writehalf4(half4 value) { + + WriteUInt16(value.x.value); + WriteUInt16(value.y.value); + WriteUInt16(value.z.value); + WriteUInt16(value.w.value); + } + } + + public partial class Reader { + + + public half Readhalf() { + return new half { value = ReadUInt16() }; + } + + + public half2 Readhalf2() { + + half2 h = default; + + h.x.value = ReadUInt16(); + h.y.value = ReadUInt16(); + + return h; + } + + + public half3 Readhalf3() { + + half3 h = default; + + h.x.value = ReadUInt16(); + h.y.value = ReadUInt16(); + h.z.value = ReadUInt16(); + + return h; + } + + + public half4 Readhalf4() { + + half4 h = default; + + h.x.value = ReadUInt16(); + h.y.value = ReadUInt16(); + h.z.value = ReadUInt16(); + h.w.value = ReadUInt16(); + + return h; + } + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs.meta new file mode 100644 index 0000000..cd0afe6 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: eeb83ce2d1531bc4ea314600a3978c79 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsHalf.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs new file mode 100644 index 0000000..2fa0525 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs @@ -0,0 +1,190 @@ +#if UNITYMATHEMATICS + +using System.Runtime.CompilerServices; +using Unity.Mathematics; + +namespace FishNet.Serializing { + + public partial class Writer { + + + public void Writeint2(int2 value) { + WriteInt32(value.x); + WriteInt32(value.y); + } + + + public void Writeint3(int3 value) { + WriteInt32(value.x); + WriteInt32(value.y); + WriteInt32(value.z); + } + + + public void Writeint4(int4 value) { + WriteInt32(value.x); + WriteInt32(value.y); + WriteInt32(value.z); + WriteInt32(value.w); + } + + public void Writeint2x2(int2x2 value) { + Writeint2(value.c0); + Writeint2(value.c1); + } + + public void Writeint2x3(int2x3 value) { + Writeint2(value.c0); + Writeint2(value.c1); + Writeint2(value.c2); + } + + public void Writeint2x4(int2x4 value) { + Writeint2(value.c0); + Writeint2(value.c1); + Writeint2(value.c2); + Writeint2(value.c3); + } + + public void Writeint3x2(int3x2 value) { + Writeint3(value.c0); + Writeint3(value.c1); + } + + public void Writeint3x3(int3x3 value) { + Writeint3(value.c0); + Writeint3(value.c1); + Writeint3(value.c2); + } + + public void Writeint3x4(int3x4 value) { + Writeint3(value.c0); + Writeint3(value.c1); + Writeint3(value.c2); + Writeint3(value.c3); + } + + public void Writeint4x2(int4x2 value) { + Writeint4(value.c0); + Writeint4(value.c1); + } + + public void Writeint4x3(int4x3 value) { + Writeint4(value.c0); + Writeint4(value.c1); + Writeint4(value.c2); + } + + public void Writeint4x4(int4x4 value) { + Writeint4(value.c0); + Writeint4(value.c1); + Writeint4(value.c2); + Writeint4(value.c3); + } + + } + + public partial class Reader { + + + public int2 Readint2() { + return new int2 { + x = ReadInt32(), + y = ReadInt32() + }; + } + + + public int3 Readint3() { + return new int3() { + x = ReadInt32(), + y = ReadInt32(), + z = ReadInt32() + }; + } + + + public int4 Readint4() { + return new int4() { + x = ReadInt32(), + y = ReadInt32(), + z = ReadInt32(), + w = ReadInt32() + }; + } + + public int2x2 Readint2x2() { + return new int2x2() { + c0 = Readint2(), + c1 = Readint2() }; + } + + public int2x3 Readint2x3() { + return new int2x3() { + c0 = Readint2(), + c1 = Readint2(), + c2 = Readint2() + }; + } + + public int2x4 Readint2x4() { + return new int2x4() { + c0 = Readint2(), + c1 = Readint2(), + c2 = Readint2(), + c3 = Readint2() + }; + } + + public int3x2 Readint3x2() { + return new int3x2() { + c0 = Readint3(), + c1 = Readint3() + }; + } + + public int3x3 Readint3x3() { + return new int3x3() { + c0 = Readint3(), + c1 = Readint3(), + c2 = Readint3() + }; + } + + public int3x4 Readint3x4() { + return new int3x4() { + c0 = Readint3(), + c1 = Readint3(), + c2 = Readint3(), + c3 = Readint3() + }; + } + + public int4x2 Readint4x2() { + return new int4x2() { + c0 = Readint4(), + c1 = Readint4() + }; + } + + public int4x3 Readint4x3() { + return new int4x3() { + c0 = Readint4(), + c1 = Readint4(), + c2 = Readint4() + }; + } + + public int4x4 Readint4x4() { + return new int4x4() { + c0 = Readint4(), + c1 = Readint4(), + c2 = Readint4(), + c3 = Readint4() + }; + } + + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs.meta new file mode 100644 index 0000000..9b07f59 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 74baebf467113dd4ca83dd656a4abb67 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsInt.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs new file mode 100644 index 0000000..daf3159 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs @@ -0,0 +1,86 @@ +#if UNITYMATHEMATICS + +using System.Runtime.CompilerServices; +using Unity.Mathematics; +using Unity.Mathematics.Geometry; + +namespace FishNet.Serializing { + + public partial class Writer { + + + public void Writequaternion(quaternion value) { + Writefloat4(value.value); + } + + + public void Writerandom(Unity.Mathematics.Random random) { + WriteUInt32(random.state); + } + + public void WriteRigidTransform(RigidTransform value) { + + Writequaternion(value.rot); + Writefloat3(value.pos); + } +#if UNITYMATHEMATICS_131 + public void WriteAffineTransform(AffineTransform value) { + + Writefloat3x3(value.rs); + Writefloat3(value.t); + } +#endif + +#if UNITYMATHEMATICS_132 + + public void ReadMinMaxAABB(MinMaxAABB minMaxAABB) { + Writefloat3(minMaxAABB.Min); + Writefloat3(minMaxAABB.Max); + } + +#endif + } + + public partial class Reader { + + + public quaternion Readquaternion() { + return new quaternion(Readfloat4()); + } + + + public Random Readrandom() { + return new Random() { state = ReadUInt32() }; + } + + public RigidTransform ReadRigidTransform() { + return new RigidTransform() { + rot = Readquaternion(), + pos = Readfloat3(), + }; + } + +#if UNITYMATHEMATICS_131 + public AffineTransform ReadAffineTransform() { + + return new AffineTransform() { + rs = Readfloat3x3(), + t = Readfloat3(), + }; + } +#endif + +#if UNITYMATHEMATICS_132 + + public MinMaxAABB ReadMinMaxAABB() { + return new MinMaxAABB() { + + Min = Readfloat3(), + Max = Readfloat3() + }; + } +#endif + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs.meta new file mode 100644 index 0000000..934a458 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 34d28436e6d87044fa5dcd3b0b7fd097 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsMisc.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs new file mode 100644 index 0000000..f187105 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs @@ -0,0 +1,190 @@ +#if UNITYMATHEMATICS + +using System.Runtime.CompilerServices; +using Unity.Mathematics; + +namespace FishNet.Serializing { + + public partial class Writer { + + + public void Writeuint2(uint2 value) { + WriteUInt32(value.x); + WriteUInt32(value.y); + } + + + public void Writeuint3(uint3 value) { + WriteUInt32(value.x); + WriteUInt32(value.y); + WriteUInt32(value.z); + } + + + public void Writeuint4(uint4 value) { + WriteUInt32(value.x); + WriteUInt32(value.y); + WriteUInt32(value.z); + WriteUInt32(value.w); + } + + public void Writeuint2x2(uint2x2 value) { + Writeuint2(value.c0); + Writeuint2(value.c1); + } + + public void Writeuint2x3(uint2x3 value) { + Writeuint2(value.c0); + Writeuint2(value.c1); + Writeuint2(value.c2); + } + + public void Writeuint2x4(uint2x4 value) { + Writeuint2(value.c0); + Writeuint2(value.c1); + Writeuint2(value.c2); + Writeuint2(value.c3); + } + + public void Writeuint3x2(uint3x2 value) { + Writeuint3(value.c0); + Writeuint3(value.c1); + } + + public void Writeuint3x3(uint3x3 value) { + Writeuint3(value.c0); + Writeuint3(value.c1); + Writeuint3(value.c2); + } + + public void Writeuint3x4(uint3x4 value) { + Writeuint3(value.c0); + Writeuint3(value.c1); + Writeuint3(value.c2); + Writeuint3(value.c3); + } + + public void Writeuint4x2(uint4x2 value) { + Writeuint4(value.c0); + Writeuint4(value.c1); + } + + public void Writeuint4x3(uint4x3 value) { + Writeuint4(value.c0); + Writeuint4(value.c1); + Writeuint4(value.c2); + } + + public void Writeuint4x4(uint4x4 value) { + Writeuint4(value.c0); + Writeuint4(value.c1); + Writeuint4(value.c2); + Writeuint4(value.c3); + } + + } + + public partial class Reader { + + + public uint2 Readuint2() { + return new uint2 { + x = ReadUInt32(), + y = ReadUInt32() + }; + } + + + public uint3 Readuint3() { + return new uint3() { + x = ReadUInt32(), + y = ReadUInt32(), + z = ReadUInt32() + }; + } + + + public uint4 Readuint4() { + return new uint4() { + x = ReadUInt32(), + y = ReadUInt32(), + z = ReadUInt32(), + w = ReadUInt32() + }; + } + + public uint2x2 Readuint2x2() { + return new uint2x2() { + c0 = Readuint2(), + c1 = Readuint2() }; + } + + public uint2x3 Readuint2x3() { + return new uint2x3() { + c0 = Readuint2(), + c1 = Readuint2(), + c2 = Readuint2() + }; + } + + public uint2x4 Readuint2x4() { + return new uint2x4() { + c0 = Readuint2(), + c1 = Readuint2(), + c2 = Readuint2(), + c3 = Readuint2() + }; + } + + public uint3x2 Readuint3x2() { + return new uint3x2() { + c0 = Readuint3(), + c1 = Readuint3() + }; + } + + public uint3x3 Readuint3x3() { + return new uint3x3() { + c0 = Readuint3(), + c1 = Readuint3(), + c2 = Readuint3() + }; + } + + public uint3x4 Readuint3x4() { + return new uint3x4() { + c0 = Readuint3(), + c1 = Readuint3(), + c2 = Readuint3(), + c3 = Readuint3() + }; + } + + public uint4x2 Readuint4x2() { + return new uint4x2() { + c0 = Readuint4(), + c1 = Readuint4() + }; + } + + public uint4x3 Readuint4x3() { + return new uint4x3() { + c0 = Readuint4(), + c1 = Readuint4(), + c2 = Readuint4() + }; + } + + public uint4x4 Readuint4x4() { + return new uint4x4() { + c0 = Readuint4(), + c1 = Readuint4(), + c2 = Readuint4(), + c3 = Readuint4() + }; + } + + } +} + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs.meta b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs.meta new file mode 100644 index 0000000..103baad --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5640c6f5fc37ed3419f18024866c816c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/UnityMathmatics/Serializers.UnityMathmaticsUInt.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Writer.Delta.cs b/Assets/FishNet/Runtime/Serializing/Writer.Delta.cs new file mode 100644 index 0000000..58a1fd4 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Writer.Delta.cs @@ -0,0 +1,830 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FishNet.CodeGenerating; +using System.Runtime.CompilerServices; +using FishNet.Component.Transforming; +using FishNet.Managing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Serializing.Helping; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Serializing +{ + public partial class Writer + { + #region Types. + [System.Flags] + internal enum UnsignedVector3DeltaFlag : int + { + Unset = 0, + More = (1 << 0), + X1 = (1 << 1), + NextXIsLarger = (1 << 2), + Y1 = (1 << 3), + NextYIsLarger = (1 << 4), + Z1 = (1 << 5), + NextZIsLarger = (1 << 6), + X2 = (1 << 8), + X4 = (1 << 9), + Y2 = (1 << 10), + Y4 = (1 << 11), + Z2 = (1 << 12), + Z4 = (1 << 13), + } + #endregion + + + + /// + /// Used to insert length for delta flags. + /// + private ReservedLengthWriter _reservedLengthWriter = new(); + + private const double LARGEST_DELTA_PRECISION_INT8 = (sbyte.MaxValue / DOUBLE_ACCURACY); + private const double LARGEST_DELTA_PRECISION_INT16 = (short.MaxValue / DOUBLE_ACCURACY); + private const double LARGEST_DELTA_PRECISION_INT32 = (int.MaxValue / DOUBLE_ACCURACY); + private const double LARGEST_DELTA_PRECISION_INT64 = (long.MaxValue / DOUBLE_ACCURACY); + + private const double LARGEST_DELTA_PRECISION_UINT8 = (byte.MaxValue / DOUBLE_ACCURACY); + private const double LARGEST_DELTA_PRECISION_UINT16 = (ushort.MaxValue / DOUBLE_ACCURACY); + private const double LARGEST_DELTA_PRECISION_UINT32 = (uint.MaxValue / DOUBLE_ACCURACY); + private const double LARGEST_DELTA_PRECISION_UINT64 = (ulong.MaxValue / DOUBLE_ACCURACY); + internal const double DOUBLE_ACCURACY = 1000d; + internal const double DOUBLE_ACCURACY_PRECISION = (1f / DOUBLE_ACCURACY); + internal const decimal DECIMAL_ACCURACY = 1000m; + + internal const float QUATERNION_PRECISION = 0.0001f; + + #region Other. + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaBoolean(bool valueA, bool valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + bool valuesMatch = (valueA == valueB); + if (valuesMatch && option == DeltaSerializerOption.Unset) + return false; + + WriteBoolean(valueB); + + return true; + } + #endregion + + #region Whole values. + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaInt8(sbyte valueA, sbyte valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + /// + /// Writes a delta value. + /// + /// True if written. + public bool WriteDeltaUInt8(byte valueA, byte valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaInt16(short valueA, short valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaUInt16(ushort valueA, ushort valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaInt32(int valueA, int valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaUInt32(uint valueA, uint valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDifference8_16_32(valueA, valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaInt64(long valueA, long valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDeltaUInt64((ulong)valueA, (ulong)valueB, option); + + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaUInt64(ulong valueA, ulong valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + bool unchangedValue = (valueA == valueB); + if (unchangedValue && option == DeltaSerializerOption.Unset) return false; + + bool bLargerThanA = (valueB > valueA); + ulong next = (bLargerThanA) ? (valueB - valueA) : (valueA - valueB); + + WriteBoolean(bLargerThanA); + WriteUnsignedPackedWhole(next); + + return true; + } + + /// + /// Writes the difference between two values for signed and unsigned shorts and ints. + /// + private bool WriteDifference8_16_32(long valueA, long valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + bool unchangedValue = (valueA == valueB); + if (unchangedValue && option == DeltaSerializerOption.Unset) return false; + + long next = (valueB - valueA); + WriteSignedPackedWhole(next); + + return true; + } + #endregion + + #region Single. + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteUDeltaSingle(float valueA, float valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + UDeltaPrecisionType dpt = GetUDeltaPrecisionType(valueA, valueB, out float unsignedDifference); + + if (dpt == UDeltaPrecisionType.Unset && option == DeltaSerializerOption.Unset) + return false; + + WriteUInt8Unpacked((byte)dpt); + WriteDeltaSingle(dpt, unsignedDifference, unsigned: true); + + return true; + } + + /// + /// Writes a delta value using a compression type. + /// + private void WriteDeltaSingle(UDeltaPrecisionType dpt, float value, bool unsigned) + { + if (dpt.FastContains(UDeltaPrecisionType.UInt8)) + { + if (unsigned) + WriteUInt8Unpacked((byte)Math.Floor(value * DOUBLE_ACCURACY)); + else + WriteInt8Unpacked((sbyte)Math.Floor(value * DOUBLE_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt16)) + { + if (unsigned) + WriteUInt16Unpacked((ushort)Math.Floor(value * DOUBLE_ACCURACY)); + else + WriteInt16Unpacked((short)Math.Floor(value * DOUBLE_ACCURACY)); + } + //Anything else is unpacked. + else + { + WriteSingleUnpacked(value); + } + } + + /// + /// Returns DeltaPrecisionType for the difference of two values. + /// Value returned should be written as signed. + /// + public UDeltaPrecisionType GetSDeltaPrecisionType(float valueA, float valueB, out float signedDifference) + { + signedDifference = (valueB - valueA); + float posValue = (signedDifference < 0f) ? (signedDifference * -1f) : signedDifference; + + return GetDeltaPrecisionType(posValue, unsigned: false); + } + + /// + /// Returns DeltaPrecisionType for the difference of two values. + /// + public UDeltaPrecisionType GetUDeltaPrecisionType(float valueA, float valueB, out float unsignedDifference) + { + bool bIsLarger = (valueB > valueA); + if (bIsLarger) + unsignedDifference = (valueB - valueA); + else + unsignedDifference = (valueA - valueB); + + UDeltaPrecisionType result = GetDeltaPrecisionType(unsignedDifference, unsigned: true); + //If result is set then set if bIsLarger. + if (bIsLarger && result != UDeltaPrecisionType.Unset) + result |= UDeltaPrecisionType.NextValueIsLarger; + + return result; + } + + /// + /// Returns DeltaPrecisionType for a value. + /// + public UDeltaPrecisionType GetDeltaPrecisionType(float positiveValue, bool unsigned) + { + if (unsigned) + { + return positiveValue switch + { + < (float)DOUBLE_ACCURACY_PRECISION => UDeltaPrecisionType.Unset, + < (float)LARGEST_DELTA_PRECISION_UINT8 => UDeltaPrecisionType.UInt8, + < (float)LARGEST_DELTA_PRECISION_UINT16 => UDeltaPrecisionType.UInt16, + < (float)LARGEST_DELTA_PRECISION_UINT32 => UDeltaPrecisionType.UInt32, + _ => UDeltaPrecisionType.Unset, + }; + } + else + { + return positiveValue switch + { + < (float)(DOUBLE_ACCURACY_PRECISION / 2d) => UDeltaPrecisionType.Unset, + < (float)LARGEST_DELTA_PRECISION_INT8 => UDeltaPrecisionType.UInt8, + < (float)LARGEST_DELTA_PRECISION_INT16 => UDeltaPrecisionType.UInt16, + < (float)LARGEST_DELTA_PRECISION_INT32 => UDeltaPrecisionType.UInt32, + _ => UDeltaPrecisionType.Unset, + }; + } + } + #endregion + + #region Double. + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteUDeltaDouble(double valueA, double valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + UDeltaPrecisionType dpt = GetUDeltaPrecisionType(valueA, valueB, out double positiveDifference); + + if (dpt == UDeltaPrecisionType.Unset && option == DeltaSerializerOption.Unset) return false; + + WriteUInt8Unpacked((byte)dpt); + WriteDeltaDouble(dpt, positiveDifference, unsigned: true); + + return true; + } + + /// + /// Writes a double using DeltaPrecisionType. + /// + private void WriteDeltaDouble(UDeltaPrecisionType dpt, double value, bool unsigned) + { + if (dpt.FastContains(UDeltaPrecisionType.UInt8)) + { + if (unsigned) + WriteUInt8Unpacked((byte)Math.Floor(value * DOUBLE_ACCURACY)); + else + WriteInt8Unpacked((sbyte)Math.Floor(value * DOUBLE_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt16)) + { + if (unsigned) + WriteUInt16Unpacked((ushort)Math.Floor(value * DOUBLE_ACCURACY)); + else + WriteInt16Unpacked((short)Math.Floor(value * DOUBLE_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt32)) + { + if (unsigned) + WriteUInt32Unpacked((uint)Math.Floor(value * DOUBLE_ACCURACY)); + else + WriteInt32Unpacked((int)Math.Floor(value * DOUBLE_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.Unset)) + { + WriteDoubleUnpacked(value); + } + else + { + NetworkManagerExtensions.LogError($"Unhandled precision type of {dpt}."); + } + } + + /// + /// Returns DeltaPrecisionType for the difference of two values. + /// + public UDeltaPrecisionType GetSDeltaPrecisionType(double valueA, double valueB, out double signedDifference) + { + signedDifference = (valueB - valueA); + double posValue = (signedDifference < 0d) ? (signedDifference * -1d) : signedDifference; + + return GetDeltaPrecisionType(posValue, unsigned: false); + } + + /// + /// Returns DeltaPrecisionType for the difference of two values. + /// + public UDeltaPrecisionType GetUDeltaPrecisionType(double valueA, double valueB, out double unsignedDifference) + { + bool bIsLarger = (valueB > valueA); + if (bIsLarger) + unsignedDifference = (valueB - valueA); + else + unsignedDifference = (valueA - valueB); + + UDeltaPrecisionType result = GetDeltaPrecisionType(unsignedDifference, unsigned: true); + if (bIsLarger && result != UDeltaPrecisionType.Unset) + result |= UDeltaPrecisionType.NextValueIsLarger; + + return result; + } + + /// + /// Returns DeltaPrecisionType for a value. + /// + public UDeltaPrecisionType GetDeltaPrecisionType(double positiveValue, bool unsigned) + { + if (unsigned) + { + return positiveValue switch + { + < LARGEST_DELTA_PRECISION_UINT8 => UDeltaPrecisionType.UInt8, + < LARGEST_DELTA_PRECISION_UINT16 => UDeltaPrecisionType.UInt16, + < LARGEST_DELTA_PRECISION_UINT32 => UDeltaPrecisionType.UInt32, + _ => UDeltaPrecisionType.Unset, + }; + } + else + { + return positiveValue switch + { + < LARGEST_DELTA_PRECISION_INT8 => UDeltaPrecisionType.UInt8, + < LARGEST_DELTA_PRECISION_INT16 => UDeltaPrecisionType.UInt16, + < LARGEST_DELTA_PRECISION_INT32 => UDeltaPrecisionType.UInt32, + _ => UDeltaPrecisionType.Unset, + }; + } + } + #endregion + + #region Decimal + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteUDeltaDecimal(decimal valueA, decimal valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + UDeltaPrecisionType dpt = GetUDeltaPrecisionType(valueA, valueB, out decimal positiveDifference); + + if (dpt == UDeltaPrecisionType.Unset && option == DeltaSerializerOption.Unset) return false; + + WriteUInt8Unpacked((byte)dpt); + WriteDeltaDecimal(dpt, positiveDifference, unsigned: true); + + return true; + } + + /// + /// Writes a double using DeltaPrecisionType. + /// + private void WriteDeltaDecimal(UDeltaPrecisionType dpt, decimal value, bool unsigned) + { + if (dpt.FastContains(UDeltaPrecisionType.UInt8)) + { + if (unsigned) + WriteUInt8Unpacked((byte)Math.Floor(value * DECIMAL_ACCURACY)); + else + WriteInt8Unpacked((sbyte)Math.Floor(value * DECIMAL_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt16)) + { + if (unsigned) + WriteUInt16Unpacked((ushort)Math.Floor(value * DECIMAL_ACCURACY)); + else + WriteInt16Unpacked((short)Math.Floor(value * DECIMAL_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt32)) + { + if (unsigned) + WriteUInt32Unpacked((uint)Math.Floor(value * DECIMAL_ACCURACY)); + else + WriteInt32Unpacked((int)Math.Floor(value * DECIMAL_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.UInt64)) + { + if (unsigned) + WriteUInt64Unpacked((ulong)Math.Floor(value * DECIMAL_ACCURACY)); + else + WriteInt64Unpacked((long)Math.Floor(value * DECIMAL_ACCURACY)); + } + else if (dpt.FastContains(UDeltaPrecisionType.Unset)) + { + WriteDecimalUnpacked(value); + } + else + { + NetworkManagerExtensions.LogError($"Unhandled precision type of {dpt}."); + } + } + + /// + /// Returns DeltaPrecisionType for the difference of two values. + /// + public UDeltaPrecisionType GetSDeltaPrecisionType(decimal valueA, decimal valueB, out decimal signedDifference) + { + signedDifference = (valueB - valueA); + decimal posValue = (signedDifference < 0m) ? (signedDifference * -1m) : signedDifference; + + return GetDeltaPrecisionType(posValue, unsigned: false); + } + + /// + /// Returns DeltaPrecisionType for the difference of two values. + /// + public UDeltaPrecisionType GetUDeltaPrecisionType(decimal valueA, decimal valueB, out decimal unsignedDifference) + { + bool bIsLarger = (valueB > valueA); + if (bIsLarger) + unsignedDifference = (valueB - valueA); + else + unsignedDifference = (valueA - valueB); + + UDeltaPrecisionType result = GetDeltaPrecisionType(unsignedDifference, unsigned: true); + if (bIsLarger && result != UDeltaPrecisionType.Unset) + result |= UDeltaPrecisionType.NextValueIsLarger; + + return result; + } + + /// + /// Returns DeltaPrecisionType for a value. + /// + public UDeltaPrecisionType GetDeltaPrecisionType(decimal positiveValue, bool unsigned) + { + if (unsigned) + { + return positiveValue switch + { + < (decimal)LARGEST_DELTA_PRECISION_UINT8 => UDeltaPrecisionType.UInt8, + < (decimal)LARGEST_DELTA_PRECISION_UINT16 => UDeltaPrecisionType.UInt16, + < (decimal)LARGEST_DELTA_PRECISION_UINT32 => UDeltaPrecisionType.UInt32, + < (decimal)LARGEST_DELTA_PRECISION_UINT64 => UDeltaPrecisionType.UInt64, + _ => UDeltaPrecisionType.Unset, + }; + } + else + { + return positiveValue switch + { + < (decimal)LARGEST_DELTA_PRECISION_INT8 => UDeltaPrecisionType.UInt8, + < (decimal)LARGEST_DELTA_PRECISION_INT16 => UDeltaPrecisionType.UInt16, + < (decimal)LARGEST_DELTA_PRECISION_INT32 => UDeltaPrecisionType.UInt32, + < (decimal)LARGEST_DELTA_PRECISION_INT64 => UDeltaPrecisionType.UInt64, + _ => UDeltaPrecisionType.Unset, + }; + } + } + #endregion + + #region FishNet Types. + /// + /// Writes a delta value. + /// + /// True if written. + [DefaultDeltaWriter] + public bool WriteDeltaNetworkBehaviour(NetworkBehaviour valueA, NetworkBehaviour valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + bool unchangedValue = (valueA == valueB); + if (unchangedValue && option == DeltaSerializerOption.Unset) return false; + + WriteNetworkBehaviour(valueB); + return true; + } + #endregion + + #region Unity. + /// + /// Writes delta position, rotation, and scale of a transform. + /// + public bool WriteDeltaTransformProperties(TransformProperties valueA, TransformProperties valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + int startPosition = Position; + Skip(1); + + byte allFlags = 0; + + if (WriteDeltaVector3(valueA.Position, valueB.Position)) + allFlags |= 1; + if (WriteDeltaQuaternion(valueA.Rotation, valueB.Rotation)) + allFlags |= 2; + if (WriteDeltaVector3(valueA.Scale, valueB.Scale)) + allFlags |= 4; + + if (allFlags != 0 || option != DeltaSerializerOption.Unset) + { + InsertUInt8Unpacked(allFlags, startPosition); + return true; + } + else + { + Position = startPosition; + return false; + } + } + + /// + /// Writes a delta quaternion. + /// + [DefaultDeltaWriter] + public bool WriteDeltaQuaternion(Quaternion valueA, Quaternion valueB, float precision = QUATERNION_PRECISION, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + bool changed = (option != DeltaSerializerOption.Unset || IsQuaternionChanged(valueA, valueB)); + + if (!changed) + return false; + + QuaternionDeltaPrecisionCompression.Compress(this, valueA, valueB, precision); + + return true; + } + + /// + /// Returns if quaternion values differ. + /// + private bool IsQuaternionChanged(Quaternion valueA, Quaternion valueB) + { + const float minimumChange = 0.0025f; + + if (Mathf.Abs(valueA.x - valueB.x) > minimumChange) + return true; + else if (Mathf.Abs(valueA.y - valueB.y) > minimumChange) + return true; + else if (Mathf.Abs(valueA.z - valueB.z) > minimumChange) + return true; + else if (Mathf.Abs(valueA.w - valueB.w) > minimumChange) + return true; + + return false; + } + + /// + /// Writes a delta value. + /// + [DefaultDeltaWriter] + public bool WriteDeltaVector2(Vector2 valueA, Vector2 valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + //TODO Fit as many flags into a byte as possible for pack levels of each axis rather than 1 per axis. + byte allFlags = 0; + + int startPosition = Position; + Skip(1); + + if (WriteUDeltaSingle(valueA.x, valueB.x)) + allFlags += 1; + if (WriteUDeltaSingle(valueA.y, valueB.y)) + allFlags += 2; + + if (allFlags != 0 || option != DeltaSerializerOption.Unset) + { + InsertUInt8Unpacked(allFlags, startPosition); + return true; + } + + Position = startPosition; + return false; + } + + [DefaultDeltaWriter] + public bool WriteDeltaVector3(Vector3 valueA, Vector3 valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + //TODO Fit as many flags into a byte as possible for pack levels of each axis rather than 1 per axis. + byte allFlags = 0; + + int startPosition = Position; + Skip(1); + + if (WriteUDeltaSingle(valueA.x, valueB.x)) + allFlags += 1; + if (WriteUDeltaSingle(valueA.y, valueB.y)) + allFlags += 2; + if (WriteUDeltaSingle(valueA.z, valueB.z)) + allFlags += 4; + + if (allFlags != 0 || option != DeltaSerializerOption.Unset) + { + InsertUInt8Unpacked(allFlags, startPosition); + return true; + } + + Position = startPosition; + return false; + } + + /// + /// Writes a delta value. + /// + //[DefaultDeltaWriter] + public bool WriteDeltaVector3_New(Vector3 valueA, Vector3 valueB, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + UnsignedVector3DeltaFlag flags = UnsignedVector3DeltaFlag.Unset; + + //Get precision type and out values. + UDeltaPrecisionType xDpt = GetUDeltaPrecisionType(valueA.x, valueB.x, out float xUnsignedDifference); + UDeltaPrecisionType yDpt = GetUDeltaPrecisionType(valueA.y, valueB.y, out float yUnsignedDifference); + UDeltaPrecisionType zDpt = GetUDeltaPrecisionType(valueA.z, valueB.z, out float zUnsignedDifference); + + byte unsetDpt = (byte)UDeltaPrecisionType.Unset; + bool flagsAreUnset = ((byte)xDpt == unsetDpt && (byte)yDpt > unsetDpt && (byte)zDpt > unsetDpt); + + //No change, can exit early. + if (flagsAreUnset && option == DeltaSerializerOption.Unset) + return false; + + //No change but must write there's no change. + if (flagsAreUnset && option != DeltaSerializerOption.Unset) + { + WriteUInt8Unpacked((byte)UnsignedVector3DeltaFlag.Unset); + return true; + } + + /* If here there is change. */ + int startPosition = Position; + + /* If x, y, or z dpt doesn't contain uint8 then it must contain a higher value. + * We already exited early if all values were unset, so there's no reason to + * check for unset here. */ + bool areFlagsMultipleBytes = (!xDpt.FastContains(UDeltaPrecisionType.UInt8) || !yDpt.FastContains(UDeltaPrecisionType.UInt8) || !zDpt.FastContains(UDeltaPrecisionType.UInt8)); + + if (areFlagsMultipleBytes) + { + Skip(2); + flags |= UnsignedVector3DeltaFlag.More; + } + else + { + Skip(1); + } + + //Write X. + if (xDpt != UDeltaPrecisionType.Unset) + { + flags |= GetShiftedFlag(xDpt, shift: 0); + WriteDeltaSingle(xDpt, xUnsignedDifference, unsigned: true); + } + + //Write Y. + if (yDpt != UDeltaPrecisionType.Unset) + { + flags |= GetShiftedFlag(yDpt, shift: 2); + WriteDeltaSingle(yDpt, yUnsignedDifference, unsigned: true); + } + + //Write Z. + if (zDpt != UDeltaPrecisionType.Unset) + { + flags |= GetShiftedFlag(zDpt, shift: 4); + WriteDeltaSingle(zDpt, zUnsignedDifference, unsigned: true); + } + + //Returns flags to add onto delta flags using precisionType and shift. + UnsignedVector3DeltaFlag GetShiftedFlag(UDeltaPrecisionType precisionType, int shift) + { + int result; + if (precisionType.FastContains(UDeltaPrecisionType.UInt8)) + { + result = ((int)UnsignedVector3DeltaFlag.X1 << shift); + // Debug.Log($"Axes {axes}. X1 {(int)UnsignedVector3DeltaFlag.X1}. Shifted {result}. Shift {shift}."); + } + else if (precisionType.FastContains(UDeltaPrecisionType.UInt16)) + result = ((int)UnsignedVector3DeltaFlag.X2 << shift); + else + result = ((int)UnsignedVector3DeltaFlag.X4 << shift); + + if (precisionType.FastContains(UDeltaPrecisionType.NextValueIsLarger)) + result |= ((int)UnsignedVector3DeltaFlag.NextXIsLarger << shift); + + return (UnsignedVector3DeltaFlag)result; + } + + /* Do another check for if one byte or two, then write flags. */ + + //Multiple bytes. + if (areFlagsMultipleBytes) + { + int flagsValue = (int)flags; + + int firstByte = (flagsValue & 0xff); + InsertUInt8Unpacked((byte)firstByte, startPosition); + int secondByte = (flagsValue >> 8); + InsertUInt8Unpacked((byte)secondByte, startPosition + 1); + } + //One byte. + else + { + InsertUInt8Unpacked((byte)flags, startPosition); + } + + return true; + } + #endregion + + #region Prediction. + /// + /// Writes a delta reconcile. + /// + internal void WriteDeltaReconcile(T lastReconcile, T value, DeltaSerializerOption option = DeltaSerializerOption.Unset) => WriteDelta(lastReconcile, value, option); + + /// + /// Writes a delta replicate using a list. + /// + internal void WriteDeltaReplicate(List values, int offset, DeltaSerializerOption option = DeltaSerializerOption.Unset) where T : IReplicateData + { + 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); + WriteUInt8Unpacked(count); + + T prev; + //Set previous if not full and if enough room in the collection to go back. + if (option != DeltaSerializerOption.FullSerialize && collectionCount > count) + prev = values[offset - 1]; + else + prev = default; + + for (int i = offset; i < collectionCount; i++) + { + T v = values[i]; + WriteDelta(prev, v, option); + + prev = v; + //After the first loop the deltaOption can be set to root, if not already. + option = DeltaSerializerOption.RootSerialize; + } + } + + /// + /// Writes a delta replicate using a BasicQueue. + /// + internal void WriteDeltaReplicate(BasicQueue values, int redundancyCount, DeltaSerializerOption option = DeltaSerializerOption.Unset) where T : IReplicateData + { + int collectionCount = values.Count; + //Replicate list will never be null, no need to write null check. + //Number of entries being written. + byte count = (byte)redundancyCount; + WriteUInt8Unpacked(count); + + int offset = (collectionCount - redundancyCount); + T prev; + //Set previous if not full and if enough room in the collection to go back. + if (option != DeltaSerializerOption.FullSerialize && collectionCount > count) + prev = values[offset - 1]; + else + prev = default; + + for (int i = offset; i < collectionCount; i++) + { + T v = values[i]; + WriteDelta(prev, v, option); + + prev = v; + //After the first loop the deltaOption can be set to root, if not already. + option = DeltaSerializerOption.RootSerialize; + } + } + #endregion + + #region Generic. + public bool WriteDelta(T prev, T next, DeltaSerializerOption option = DeltaSerializerOption.Unset) + { + Func del = GenericDeltaWriter.Write; + + if (del == null) + { + NetworkManager.LogError($"Write delta method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer."); + + return false; + } + else + { + return del.Invoke(this, prev, next, option); + } + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Writer.Delta.cs.meta b/Assets/FishNet/Runtime/Serializing/Writer.Delta.cs.meta new file mode 100644 index 0000000..1326f40 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Writer.Delta.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 03955a9bd0c9a6f44886554887435672 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Writer.Delta.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/Writer.cs b/Assets/FishNet/Runtime/Serializing/Writer.cs new file mode 100644 index 0000000..f642929 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Writer.cs @@ -0,0 +1,1298 @@ +using FishNet.CodeGenerating; +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Serializing.Helping; +using FishNet.Transporting; +using FishNet.Utility; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using GameKit.Dependencies.Utilities.Types; +using UnityEngine; + +[assembly: InternalsVisibleTo(UtilityConstants.GENERATED_ASSEMBLY_NAME)] + +namespace FishNet.Serializing +{ + /// + /// Writes data to a buffer. + /// + public partial 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 + + /// + /// Outputs writer to string. + /// + /// + public override string ToString() => ToString(0, Length); + + /// + /// Outputs writer to string starting at an index. + /// + /// + public string ToString(int offset, int length) + { + return $"Position: {Position:0000}, Length: {Length:0000}, Buffer: {BitConverter.ToString(_buffer, offset, length)}."; + } + + [Obsolete("Use Clear(NetworkManager) instead.")] + public void Reset(NetworkManager newManager = null) => Clear(newManager); + + /// + /// Resets written data. + /// + public void Clear() + { + Length = 0; + Position = 0; + } + + /// + /// Resets written data and sets the NetworkManager. + /// + public void Clear(NetworkManager newManager) + { + Clear(); + NetworkManager = newManager; + } + + /// + /// 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. + /// + /// + 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(_buffer, 0, Length); + } + + /// + /// Reserves a number of bytes from current position. + /// + /// + [Obsolete("Use Skip.")] + public void Reserve(int count) => Skip(count); + + /// + /// Skips a number of bytes from current position. + /// + /// + public void Skip(int count) + { + EnsureBufferLength(count); + Position += count; + Length = Math.Max(Length, Position); + } + + /// + /// Sets size variables back an amount. + /// + /// + internal void Remove(int count) + { + Position -= count; + Length -= count; + } + + /// + /// Sends a packetId. + /// + /// + internal void WritePacketIdUnpacked(PacketId pid) + { + WriteUInt16Unpacked((ushort)pid); + } + + /// + /// Inserts a packetId. + /// + internal void InsertPacketIdUnpacked(PacketId packetId, int index) + { + ushort pId = (ushort)packetId; + InsertUInt16Unpacked(pId, index); + } + + /// + /// Inserts value at index within the buffer. + /// This method does not perform error checks nor increases Length, Position. + /// + [Obsolete("Use InsertUInt8Unpacked.")] + public void FastInsertUInt8Unpacked(byte value, int index) => InsertUInt8Unpacked(value, index); + + /// + /// Inserts value at index within the buffer. + /// This method does not perform error checks nor increases Length, Position. + /// + public void InsertUInt8Unpacked(byte value, int index) + { + _buffer[index] = value; + } + + /// + /// Inserts value at index within the buffer. + /// This method does not perform error checks nor increases Length, Position. + /// + public void InsertUInt16Unpacked(ushort value, int index) + { + _buffer[index++] = (byte)value; + _buffer[index] = (byte)(value >> 8); + } + + /// + /// Inserts value at index within the buffer. + /// This method does not perform error checks nor increases Length, Position. + /// + public void InsertInt32Unpacked(int value, int index) => InsertUInt32Unpacked((uint)value, index); + + /// + /// Inserts value at index within the buffer. + /// This method does not perform error checks nor increases Length, Position. + /// + public void InsertUInt32Unpacked(uint value, int index) + { + _buffer[index++] = (byte)value; + _buffer[index++] = (byte)(value >> 8); + _buffer[index++] = (byte)(value >> 16); + _buffer[index] = (byte)(value >> 24); + } + + [Obsolete("Use WriteUInt8Unpacked.")] + public void WriteByte(byte value) => WriteUInt8Unpacked(value); + + /// + /// Writes a byte. + /// + /// + [DefaultWriter] + public void WriteUInt8Unpacked(byte value) + { + EnsureBufferLength(1); + _buffer[Position++] = value; + + Length = Math.Max(Length, Position); + } + + [Obsolete("Use WriteUInt8Array.")] + public void WriteBytes(byte[] value, int offset, int count) => WriteUInt8Array(value, offset, count); + + /// + /// Writes bytes. + /// + /// + /// + /// + public void WriteUInt8Array(byte[] value, int offset, int count) + { + EnsureBufferLength(count); + Buffer.BlockCopy(value, offset, _buffer, Position, count); + Position += count; + Length = Math.Max(Length, Position); + } + + [Obsolete("Use WriteUInt8ArrayAndSize.")] + public void WriteBytesAndSize(byte[] value, int offset, int count) => WriteUInt8ArrayAndSize(value, offset, count); + + /// + /// Writes bytes and length of bytes. + /// + /// + /// + /// + public void WriteUInt8ArrayAndSize(byte[] value, int offset, int count) + { + if (value == null) + { + WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); + } + else + { + WriteInt32(count); + WriteUInt8Array(value, offset, count); + } + } + + [Obsolete("Use WriteUInt8ArrayAndSize.")] + public void WriteBytesAndSize(byte[] value) => WriteUInt8ArrayAndSize(value); + + /// + /// Writes all bytes in value and length of bytes. + /// + /// + public void WriteUInt8ArrayAndSize(byte[] value) + { + int size = (value == null) ? 0 : value.Length; + // buffer might be null, so we can't use .Length in that case + WriteUInt8ArrayAndSize(value, 0, size); + } + + [Obsolete("Use WriteInt8Unpacked.")] + public void WriteSByte(sbyte value) => WriteInt8Unpacked(value); + + /// + /// Writes a sbyte. + /// + [DefaultWriter] + public void WriteInt8Unpacked(sbyte value) => WriteUInt8Unpacked((byte)value); + + /// + /// Writes a char. + /// + /// + [DefaultWriter] + public void WriteChar(char value) + { + EnsureBufferLength(2); + _buffer[Position++] = (byte)value; + _buffer[Position++] = (byte)(value >> 8); + Length = Math.Max(Length, Position); + } + + /// + /// Writes a boolean. + /// + /// + [DefaultWriter] + public void WriteBoolean(bool value) + { + EnsureBufferLength(1); + _buffer[Position++] = (value) ? (byte)1 : (byte)0; + Length = Math.Max(Length, Position); + } + + /// + /// Writes a uint16 unpacked. + /// + /// + public void WriteUInt16Unpacked(ushort value) + { + EnsureBufferLength(2); + _buffer[Position++] = (byte)value; + _buffer[Position++] = (byte)(value >> 8); + Length = Math.Max(Length, Position); + } + + /// + /// Writes a uint16. + /// + /// + //todo: should be using WritePackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. + [DefaultWriter] + public void WriteUInt16(ushort value) => WriteUInt16Unpacked(value); + + /// + /// Writes a int16 unpacked. + /// + /// + //todo: should be WritePackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. + public void WriteInt16Unpacked(short value) => WriteUInt16Unpacked((ushort)value); + + /// + /// Writes a int16. + /// + /// + //todo: should be WritePackedWhole but something relying on unpacked short/ushort is being written packed, corrupting packets. + [DefaultWriter] + public void WriteInt16(short value) => WriteUInt16Unpacked((ushort)value); + + /// + /// Writes a int32. + /// + /// + public void WriteInt32Unpacked(int value) => WriteUInt32Unpacked((uint)value); + + /// + /// Writes an int32. + /// + /// + [DefaultWriter] + public void WriteInt32(int value) => WriteSignedPackedWhole(value); + + /// + /// Writes value to dst without error checking. + /// + internal static void WriteUInt32Unpacked(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 a uint32. + /// + /// + public void WriteUInt32Unpacked(uint value) + { + EnsureBufferLength(4); + WriteUInt32Unpacked(_buffer, value, ref Position); + Length = Math.Max(Length, Position); + } + + /// + /// Writes a uint32. + /// + /// + [DefaultWriter] + public void WriteUInt32(uint value) => WriteUnsignedPackedWhole(value); + + /// + /// Writes a uint64. + /// + /// + public void WriteUInt64Unpacked(ulong value) + { + 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); + } + + /// + /// Writes a uint64. + /// + /// + [DefaultWriter] + public void WriteUInt64(ulong value) => WriteUnsignedPackedWhole(value); + + /// + /// Writes a int64. + /// + /// + public void WriteInt64Unpacked(long value) => WriteUInt64((ulong)value); + + /// + /// Writes an int64. + /// + /// + [DefaultWriter] + public void WriteInt64(long value) => WriteSignedPackedWhole(value); + + /// + /// Writes a single (float). + /// + /// + public void WriteSingleUnpacked(float value) + { + EnsureBufferLength(4); + UIntFloat converter = new() { FloatValue = value }; + WriteUInt32Unpacked(converter.UIntValue); + } + + /// + /// Writes a single (float). + /// + /// + [DefaultWriter] + public void WriteSingle(float value) => WriteSingleUnpacked(value); + + /// + /// Writes a double. + /// + /// + public void WriteDoubleUnpacked(double value) + { + UIntDouble converter = new() { DoubleValue = value }; + WriteUInt64Unpacked(converter.LongValue); + } + + /// + /// Writes a double. + /// + /// + [DefaultWriter] + public void WriteDouble(double value) => WriteDoubleUnpacked(value); + + /// + /// Writes a decimal. + /// + /// + public void WriteDecimalUnpacked(decimal value) + { + UIntDecimal converter = new() { DecimalValue = value }; + WriteUInt64Unpacked(converter.LongValue1); + WriteUInt64Unpacked(converter.LongValue2); + } + + /// + /// Writes a decimal. + /// + /// + [DefaultWriter] + public void WriteDecimal(decimal value) => WriteDecimalUnpacked(value); + + /// + /// Writes a string. + /// + /// + [DefaultWriter] + public void WriteString(string value) + { + if (value == null) + { + WriteInt32(Writer.UNSET_COLLECTION_SIZE_VALUE); + return; + } + + byte[] buffer = Strings.Buffer; + int length = value.ToBytes(ref buffer); + + WriteInt32(length); + + //Nothing to write. + if (length == 0) + return; + + WriteUInt8Array(buffer, 0, length); + } + + /// + /// Writes a byte ArraySegment and it's size. + /// + /// + [DefaultWriter] + public void WriteArraySegmentAndSize(ArraySegment value) => WriteUInt8ArrayAndSize(value.Array, value.Offset, value.Count); + + /// + /// Writes an ArraySegment without size. + /// + /// + public void WriteArraySegment(ArraySegment value) => WriteUInt8Array(value.Array, value.Offset, value.Count); + + /// + /// Writes a Vector2. + /// + /// + public void WriteVector2Unpacked(Vector2 value) + { + WriteSingleUnpacked(value.x); + WriteSingleUnpacked(value.y); + } + + /// + /// Writes a Vector2. + /// + /// + [DefaultWriter] + public void WriteVector2(Vector2 value) => WriteVector2Unpacked(value); + + /// + /// Writes a Vector3 + /// + /// + public void WriteVector3Unpacked(Vector3 value) + { + WriteSingleUnpacked(value.x); + WriteSingleUnpacked(value.y); + WriteSingleUnpacked(value.z); + } + + /// + /// Writes a Vector3 + /// + /// + [DefaultWriter] + public void WriteVector3(Vector3 value) => WriteVector3Unpacked(value); + + /// + /// Writes a Vector4. + /// + /// + public void WriteVector4Unpacked(Vector4 value) + { + WriteSingleUnpacked(value.x); + WriteSingleUnpacked(value.y); + WriteSingleUnpacked(value.z); + WriteSingleUnpacked(value.w); + } + + /// + /// Writes a Vector4. + /// + /// + [DefaultWriter] + public void WriteVector4(Vector4 value) => WriteVector4Unpacked(value); + + /// + /// Writes a Vector2Int. + /// + /// + public void WriteVector2IntUnpacked(Vector2Int value) + { + WriteInt32Unpacked(value.x); + WriteInt32Unpacked(value.y); + } + + /// + /// Writes a Vector2Int. + /// + /// + [DefaultWriter] + public void WriteVector2Int(Vector2Int value) + { + WriteSignedPackedWhole(value.x); + WriteSignedPackedWhole(value.y); + } + + /// + /// Writes a Vector3Int. + /// + /// + public void WriteVector3IntUnpacked(Vector3Int value) + { + WriteInt32Unpacked(value.x); + WriteInt32Unpacked(value.y); + WriteInt32Unpacked(value.z); + } + + /// + /// Writes a Vector3Int. + /// + /// + [DefaultWriter] + public void WriteVector3Int(Vector3Int value) + { + WriteSignedPackedWhole(value.x); + WriteSignedPackedWhole(value.y); + WriteSignedPackedWhole(value.z); + } + + /// + /// Writes a Color. + /// + /// + public void WriteColorUnpacked(Color value) + { + WriteSingleUnpacked(value.r); + WriteSingleUnpacked(value.g); + WriteSingleUnpacked(value.b); + WriteSingleUnpacked(value.a); + } + + /// + /// Writes a Color. + /// + /// + [DefaultWriter] + public void WriteColor(Color value) + { + 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. + /// + /// + [DefaultWriter] + 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. + /// + /// + public void WriteQuaternionUnpacked(Quaternion value) + { + WriteSingleUnpacked(value.x); + WriteSingleUnpacked(value.y); + WriteSingleUnpacked(value.z); + WriteSingleUnpacked(value.w); + } + + /// + /// Writes a Quaternion. + /// + /// + public void WriteQuaternion64(Quaternion value) + { + ulong result = Quaternion64Compression.Compress(value); + WriteUInt64Unpacked(result); + } + + /// + /// Writes a Quaternion. + /// + /// + [DefaultWriter] + public void WriteQuaternion32(Quaternion value) + { + Quaternion32Compression.Compress(this, value); + } + + /// + /// Reads a Quaternion. + /// + /// + internal void WriteQuaternion(Quaternion value, AutoPackType autoPackType) + { + switch (autoPackType) + { + case AutoPackType.Packed: + WriteQuaternion32(value); + ; + break; + case AutoPackType.PackedLess: + WriteQuaternion64(value); + break; + default: + WriteQuaternionUnpacked(value); + break; + } + } + + /// + /// Writes a rect. + /// + /// + public void WriteRectUnpacked(Rect value) + { + WriteSingleUnpacked(value.xMin); + WriteSingleUnpacked(value.yMin); + WriteSingleUnpacked(value.width); + WriteSingleUnpacked(value.height); + } + + /// + /// Writes a rect. + /// + /// + [DefaultWriter] + public void WriteRect(Rect value) => WriteRectUnpacked(value); + + /// + /// Writes a plane. + /// + /// + public void WritePlaneUnpacked(Plane value) + { + WriteVector3Unpacked(value.normal); + WriteSingleUnpacked(value.distance); + } + + /// + /// Writes a plane. + /// + /// + [DefaultWriter] + public void WritePlane(Plane value) => WritePlaneUnpacked(value); + + /// + /// Writes a Ray. + /// + /// + public void WriteRayUnpacked(Ray value) + { + WriteVector3Unpacked(value.origin); + WriteVector3Unpacked(value.direction); + } + + /// + /// Writes a Ray. + /// + /// + [DefaultWriter] + public void WriteRay(Ray value) => WriteRayUnpacked(value); + + /// + /// Writes a Ray2D. + /// + /// + public void WriteRay2DUnpacked(Ray2D value) + { + WriteVector2Unpacked(value.origin); + WriteVector2Unpacked(value.direction); + } + + /// + /// Writes a Ray2D. + /// + /// + [DefaultWriter] + public void WriteRay2D(Ray2D value) => WriteRay2DUnpacked(value); + + /// + /// Writes a Matrix4x4. + /// + /// + public void WriteMatrix4x4Unpacked(Matrix4x4 value) + { + WriteSingleUnpacked(value.m00); + WriteSingleUnpacked(value.m01); + WriteSingleUnpacked(value.m02); + WriteSingleUnpacked(value.m03); + WriteSingleUnpacked(value.m10); + WriteSingleUnpacked(value.m11); + WriteSingleUnpacked(value.m12); + WriteSingleUnpacked(value.m13); + WriteSingleUnpacked(value.m20); + WriteSingleUnpacked(value.m21); + WriteSingleUnpacked(value.m22); + WriteSingleUnpacked(value.m23); + WriteSingleUnpacked(value.m30); + WriteSingleUnpacked(value.m31); + WriteSingleUnpacked(value.m32); + WriteSingleUnpacked(value.m33); + } + + /// + /// Writes a Matrix4x4. + /// + /// + [DefaultWriter] + public void WriteMatrix4x4(Matrix4x4 value) => WriteMatrix4x4Unpacked(value); + + /// + /// Writes a Guid. + /// + /// + [DefaultWriter] + public void WriteGuidAllocated(System.Guid value) + { + byte[] data = value.ToByteArray(); + WriteUInt8Array(data, 0, data.Length); + } + + /// + /// Writes a tick without packing. + /// + /// + public void WriteTickUnpacked(uint value) => WriteUInt32Unpacked(value); + + /// + /// Writes a GameObject. GameObject must be spawned over the network already or be a prefab with a NetworkObject attached. + /// + /// + [DefaultWriter] + public void WriteGameObject(GameObject go) + { + //There needs to be a header to indicate if null, nob, or nb. + if (go == null) + { + WriteUInt8Unpacked(0); + } + else + { + //Try to write the NetworkObject first. + if (go.TryGetComponent(out NetworkObject nob)) + { + WriteUInt8Unpacked(1); + WriteNetworkObject(nob); + } + //If there was no nob try to write a NetworkBehaviour. + else if (go.TryGetComponent(out NetworkBehaviour nb)) + { + WriteUInt8Unpacked(2); + WriteNetworkBehaviour(nb); + } + //Object cannot be serialized so write null. + else + { + WriteUInt8Unpacked(0); + NetworkManager.LogError($"GameObject {go.name} cannot be serialized because it does not have a NetworkObject nor NetworkBehaviour."); + } + } + } + + /// + /// Writes a Transform. Transform must be spawned over the network already or be a prefab with a NetworkObject attached. + /// + /// + [DefaultWriter] + public void WriteTransform(Transform t) + { + if (t == null) + { + WriteNetworkObject(null); + } + else + { + NetworkObject nob = t.GetComponent(); + WriteNetworkObject(nob); + } + } + + /// + /// Writes a NetworkObject.ObjectId. + /// + /// + public void WriteNetworkObjectId(NetworkObject nob) + { + int id = (nob == null) ? NetworkObject.UNSET_OBJECTID_VALUE : nob.ObjectId; + WriteNetworkObjectId(id); + } + + /// + /// Writes a NetworkObject while optionally including the initialization order. + /// + [DefaultWriter] + public void WriteNetworkObject(NetworkObject nob) + { + if (nob == null) + { + WriteNetworkObjectId(NetworkObject.UNSET_OBJECTID_VALUE); + } + else + { + bool spawned = nob.IsSpawned; + + if (spawned) + WriteNetworkObjectId(nob.ObjectId); + else + WriteNetworkObjectId(nob.PrefabId); + + /* Spawned is written after because it's only needed if nob + * is not null. If it were written before it would also have + * to be written when nob == null.*/ + WriteBoolean(spawned); + } + } + + /// + /// Writes a spawned networkObject. + /// + internal void WriteSpawnedNetworkObject(NetworkObject nob) + { + WriteNetworkObjectId(nob.ObjectId); + WriteUInt16(nob.SpawnableCollectionId); + WriteInt32(nob.GetInitializeOrder()); + } + + /// + /// Writes a NetworkObject for a despawn message. + /// + /// + /// + internal void WriteNetworkObjectForDespawn(NetworkObject nob, DespawnType dt) + { + WriteNetworkObjectId(nob.ObjectId); + WriteUInt8Unpacked((byte)dt); + } + + /// + /// Writes an objectId. + /// + public void WriteNetworkObjectId(int objectId) => WriteSignedPackedWhole(objectId); + + /// + /// Writes a NetworkBehaviour. + /// + /// + [DefaultWriter] + public void WriteNetworkBehaviour(NetworkBehaviour nb) + { + if (nb == null) + { + WriteNetworkObject(null); + WriteUInt8Unpacked(0); + } + else + { + WriteNetworkObject(nb.NetworkObject); + WriteUInt8Unpacked(nb.ComponentIndex); + } + } + + /// + /// Writes a NetworkBehaviourId. + /// + public void WriteNetworkBehaviourId(NetworkBehaviour nb) + { + if (nb == null) + WriteUInt8Unpacked(NetworkBehaviour.UNSET_NETWORKBEHAVIOUR_ID); + else + WriteUInt8Unpacked(nb.ComponentIndex); + } + + /// + /// Writes a DateTime. + /// + [DefaultWriter] + public void WriteDateTime(DateTime dt) => WriteSignedPackedWhole(dt.ToBinary()); + + /// + /// Writes a transport channel. + /// + /// + [DefaultWriter] + public void WriteChannel(Channel channel) => WriteUInt8Unpacked((byte)channel); + + /// + /// Writers a LayerMask. + /// + /// + [DefaultWriter] + public void WriteLayerMask(LayerMask value) => WriteSignedPackedWhole(value.value); + + /// + /// Writes a NetworkConnection. + /// + /// + [DefaultWriter] + public void WriteNetworkConnection(NetworkConnection connection) + { + int value = (connection == null) ? NetworkConnection.UNSET_CLIENTID_VALUE : connection.ClientId; + WriteNetworkConnectionId(value); + } + + /// + /// Writes TransformProperties. + /// + [DefaultWriter] + public void WriteTransformProperties(TransformProperties value) + { + WriteVector3(value.Position); + WriteQuaternion32(value.Rotation); + WriteVector3(value.Scale); + } + + /// + /// Writes a short for a connectionId. + /// + /// + public void WriteNetworkConnectionId(int id) => WriteSignedPackedWhole(id); + + /// + /// Writes a dictionary. + /// + public void WriteDictionary(Dictionary dict) + { + if (dict == null) + { + WriteSignedPackedWhole(Writer.UNSET_COLLECTION_SIZE_VALUE); + return; + } + else + { + WriteSignedPackedWhole(dict.Count); + } + + foreach (KeyValuePair item in dict) + { + Write(item.Key); + Write(item.Value); + } + } + + /// + /// Writes a list. + /// + /// Collection to write. + public void WriteList(List value) + { + int count = (value == null) ? 0 : value.Count; + WriteList(value, 0, count); + } + + /// + /// Writes a state update packet. + /// + /// + internal void WriteStateUpdatePacket(uint lastPacketTick) => WriteTickUnpacked(lastPacketTick); + + #region Packed writers. + /// + /// ZigZag encode an integer. Move the sign bit to the right. + /// + public ulong ZigZagEncode(ulong value) + { + if (value >> 63 > 0) + return ~(value << 1) | 1; + return value << 1; + } + + /// + /// Writes a packed whole number. + /// + /// + public void WriteSignedPackedWhole(long value) => WriteUnsignedPackedWhole(ZigZagEncode((ulong)value)); + + /// + /// Writes a packed whole number. + /// + /// + /// + /// Writes a packed whole number. + /// + /// + public void WriteUnsignedPackedWhole(ulong value) + { + EnsureBufferLength(9); + while (value > 127) + { + _buffer[Position++] = (byte)((value & 0x7F) | 0x80); + value >>= 7; + } + + _buffer[Position++] = (byte)(value & 0x7F); + Length = Math.Max(Length, Position); + } + #endregion + + #region Generators. + /// + /// Writes a list. + /// + /// Collection to write. + /// Offset to begin at. + /// Entries to write. + public void WriteList(List value, int offset, int count) + { + if (value == null) + { + WriteSignedPackedWhole(Writer.UNSET_COLLECTION_SIZE_VALUE); + } + else + { + //Make sure values cannot cause out of bounds. + if ((offset + count > value.Count)) + count = 0; + + WriteSignedPackedWhole(count); + for (int i = 0; i < count; i++) + Write(value[i + offset]); + } + } + + /// + /// Writes a list. + /// + /// Collection to write. + /// Offset to begin at. + public void WriteList(List value, int offset) + { + int count = (value == null) ? 0 : value.Count; + WriteList(value, offset, count - offset); + } + + + /// + /// Writes an array. + /// + /// Collection to write. + public void WriteArray(T[] value) + { + int count = (value == null) ? 0 : value.Length; + WriteArray(value, 0, count); + } + + /// + /// Writes an array. + /// + /// Collection to write. + /// Offset to begin at. + public void WriteArray(T[] value, int offset) + { + int count = (value == null) ? 0 : value.Length; + WriteArray(value, offset, count - offset); + } + + /// + /// Writes an array. + /// + /// Collection to write. + /// Offset to begin at. + /// Entries to write. + public void WriteArray(T[] value, int offset, int count) + { + if (value == null) + { + WriteSignedPackedWhole(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)) + { + WriteSignedPackedWhole(0); + } + else + { + WriteSignedPackedWhole(count); + for (int i = offset; i < count; i++) + Write(value[i]); + } + } + } + /// + /// Writes a reconcile. + /// + internal void WriteReconcile(T data) + { + Write(data); + } + + /// + /// Writes a replication to the server. + /// + internal void WriteReplicate(RingBuffer> values, int offset) where T : IReplicateData, new() + { + /* 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); + WriteUInt8Unpacked(count); + + for (int i = offset; i < collectionCount; i++) + WriteReplicateDataContainer(values[i]); + } + + internal void WriteReplicate(BasicQueue> values, int redundancyCount) where T : IReplicateData, new() + { + /* 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)redundancyCount; + WriteUInt8Unpacked(count); + + for (int i = (collectionCount - redundancyCount); i < collectionCount; i++) + WriteReplicateDataContainer(values[i]); + } + + /// + /// Reads a ReplicateData and applies tick and channel. + /// + private void WriteReplicateDataContainer(ReplicateDataContainer value) where T : IReplicateData , new() + { + Write(value.Data); + WriteChannel(value.Channel); + } + + + /// + /// Writes any supported type using packing. + /// + public void Write(T value) + { + Action del = GenericWriter.Write; + if (del == null) + NetworkManager.LogError($"Write method not found for {typeof(T).FullName}. Use a supported type or create a custom serializer."); + else + del.Invoke(this, value); + } + #endregion + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Serializing/Writer.cs.meta b/Assets/FishNet/Runtime/Serializing/Writer.cs.meta new file mode 100644 index 0000000..f3e71b3 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/Writer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e2633f927065d9d43b8a4da09240266c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/Writer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs new file mode 100644 index 0000000..6c4c8b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs @@ -0,0 +1,90 @@ +//using FishNet.CodeGenerating; //Remove on V5 +//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(short)); +// DefaultPackedTypes.Add(typeof(ushort)); +// DefaultPackedTypes.Add(typeof(int)); +// DefaultPackedTypes.Add(typeof(uint)); +// DefaultPackedTypes.Add(typeof(long)); +// DefaultPackedTypes.Add(typeof(ulong)); +// DefaultPackedTypes.Add(typeof(Color)); +// DefaultPackedTypes.Add(typeof(Quaternion)); +// DefaultPackedTypes.Add(typeof(Vector2Int)); +// DefaultPackedTypes.Add(typeof(Vector3Int)); +// DefaultPackedTypes.Add(typeof(Quaternion)); +// } + + +// //public static void WriteDictionary(this Writer writer, Dictionary dict) => writer.WriteDictionary(dict); +// //public static void WriteByte(this Writer writer, byte value) => writer.WriteByte(value); +// // +// //public static void WriteBytes(this Writer writer, byte[] buffer, int offset, int count) => writer.WriteBytes(buffer, offset, count); +// // +// //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); +// // +// //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); +// // +// //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..28e9775 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterExtensions.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 493e880d63e9372449c0f0e63890aaef +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/WriterExtensions.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/WriterPool.cs b/Assets/FishNet/Runtime/Serializing/WriterPool.cs new file mode 100644 index 0000000..b1a34d4 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterPool.cs @@ -0,0 +1,210 @@ +using FishNet.Managing; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using GameKit.Dependencies.Utilities; + +namespace FishNet.Serializing +{ + /// + /// Writer which is reused to save on garbage collection and performance. + /// + public sealed class PooledWriter : Writer + { + public void Store() => WriterPool.Store(this); + public void StoreLength() => WriterPool.StoreLength(this); + + [Obsolete("Use Clear instead.")] + public void ResetState() => base.Clear(); + [Obsolete("This does not function.")] + public void InitializeState() { } + } + + /// + /// 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(); + /// + /// Pool of writers where length is of minimum key and may be increased at runtime. + /// + private static readonly Dictionary> _lengthPool = new(); + #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 Retrieve(NetworkManager networkManager) + { + PooledWriter result; + if (!_pool.TryPop(out result)) + result = new(); + + result.Clear(networkManager); + return result; + } + /// Gets a writer from the pool. + /// + + public static PooledWriter Retrieve() + { + return Retrieve(null); + } + + /// + /// Gets the next writer in the pool of minimum length. + /// + /// Minimum length the writer buffer must be. + + public static PooledWriter Retrieve(int length) + { + return Retrieve(null, length); + } + /// + /// Gets the next writer in the pool of minimum length. + /// + /// Minimum length the writer buffer must be. + + public static PooledWriter Retrieve(NetworkManager networkManager, int length) + { + /* 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 index = GetDictionaryIndex(length); + Stack stack; + PooledWriter result; + //There is already one pooled. + if (_lengthPool.TryGetValue(index, out stack) && stack.TryPop(out result)) + { + result.Clear(networkManager); + } + //Not pooled yet or failed to pop. + else + { + //Get any ol' writer. + result = Retrieve(networkManager); + /* Ensure length to fill it's bracket. + * Increase index by 1 since 0 index would + * just return 0 as the capacity. */ + int requiredCapacity = (index + 1) * LENGTH_BRACKET; + result.EnsureBufferCapacity(requiredCapacity); + } + + return result; + } + + /// + /// 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 StoreLength(PooledWriter writer) + { + int index = GetDictionaryIndex(writer); + Stack stack; + if (!_lengthPool.TryGetValue(index, out stack)) + { + stack = new(); + _lengthPool[index] = stack; + } + + stack.Push(writer); + } + + /// + /// Returns a writer to the pool. + /// + public static void Store(PooledWriter writer) + { + _pool.Push(writer); + } + + /// + /// Puts writer back into pool if not null, and nullifies source reference. + /// + public static void StoreAndDefault(ref PooledWriter writer) + { + if (writer != null) + { + _pool.Push(writer); + writer = null; + } + } + #region Dictionary indexes. + /// + /// Gets which index to use for length when retrieving a writer. + /// + private static int GetDictionaryIndex(int length) + { + /* 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. So to accomodate 1200 + * length index 1 must be used as 0 has a maximum of 1000. */ + + /* Examples if length_bracket is 1000, using floor: + * 800 / 1000 = 0. + * 1200 / 1000 = 1. + * 1000 / 1000 = 1. But has 0 remainder so is reduced by 1, resulting in 0. + */ + int index = UnityEngine.Mathf.FloorToInt(length / LENGTH_BRACKET); + if (index > 0 && length % LENGTH_BRACKET == 0) + index--; + + //UnityEngine.Debug.Log($"Returning length {length} from index {index}"); + return index; + } + + /// + /// Gets which index to use for length when storing a writer. + /// + private static int GetDictionaryIndex(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); + } + + /* Since capacity is set to minimum of length_bracket + * capacity / length_bracket will always be at least 1. + * + * Here are some result examples using floor: + * 1000 / 1000 = 1. + * 1200 / 1000 = 1. + * 2400 / 1000 = 2. + */ + int index = UnityEngine.Mathf.FloorToInt(capacity / LENGTH_BRACKET); + /* As mentioned the index will always be a minimum of 1. Because of this + * we can safely reduce index by 1 and it not be negative. + * This reduction also ensures the writer ends up in the proper pool. + * Since index 0 ensures minimum of 1000, 1000-1999 would go there. + * Just as 2000-2999 would go into 1. */ + index--; + + //UnityEngine.Debug.Log($"Storing capacity {capacity} at index {index}"); + return index; + } + #endregion + + } +} diff --git a/Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta b/Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta new file mode 100644 index 0000000..9d9cad8 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 99b13ccd24eb7264abf67780ef86e44a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/WriterPool.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Serializing/WriterStatics.cs b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs new file mode 100644 index 0000000..9aa0892 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs @@ -0,0 +1 @@ +//Remove on V5 \ 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..b3d2751 --- /dev/null +++ b/Assets/FishNet/Runtime/Serializing/WriterStatics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 264951af6d87de34e8809b550a7956b1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Serializing/WriterStatics.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting.meta b/Assets/FishNet/Runtime/Transporting.meta new file mode 100644 index 0000000..7cf8134 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cd437bc70be1b084a991333055bbe475 +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..a75f6ee --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Channels.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7cd503d67a974984385164c53bd3e518 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Channels.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs new file mode 100644 index 0000000..6aae7f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs @@ -0,0 +1,59 @@ +namespace FishNet.Transporting +{ + + /// + /// States the local connection can be in. + /// + [System.Flags] + public enum LocalConnectionState : int + { + /// + /// Connection is fully stopped. + /// + Stopped = (1 << 0), + /// + /// Connection is stopping. + /// + Stopping = (1 << 1), + /// + /// Connection is starting but not yet established. + /// + Starting = (1 << 2), + /// + /// Connection is established. + /// + Started = (1 << 3), + + // StoppedError = (1 << 4), + // StoppedClosed = (1 << 5), + } + + public static class LocalConnectionStateExtensions + { + /// + /// True if the connection state is stopped or stopping. + /// + public static bool IsStoppedOrStopping(this LocalConnectionState connectionState) => (connectionState == LocalConnectionState.Stopped || connectionState == LocalConnectionState.Stopping); + /// + /// True if the connection state is started or starting. + /// + public static bool IsStartedOrStarting(this LocalConnectionState connectionState) => (connectionState == LocalConnectionState.Started || connectionState == LocalConnectionState.Starting); + } + + /// + /// 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..895c87a --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/ConnectionStates.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 45640e2b3919981499b359ecc2154d3f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/ConnectionStates.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/EventStructures.cs b/Assets/FishNet/Runtime/Transporting/EventStructures.cs new file mode 100644 index 0000000..1fa2158 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/EventStructures.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; + +namespace FishNet.Transporting +{ + + /// + /// Container for connected clients state for a client. + /// + public struct ConnectedClientsArgs + { + /// + /// Collection of client ids connected to the server. + /// + public List ClientIds { get; private set; } + + public ConnectedClientsArgs(List clientIds) + { + ClientIds = clientIds; + } + } + + + + /// + /// 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..5980fee --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/EventStructures.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 718b9d27800e70848b50b2c7b0117e5c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/EventStructures.cs + uploadId: 762203 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..508e7eb --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/IPAddressType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 11a2c7610ce4ce34a915683bd4607714 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/IPAddressType.cs + uploadId: 762203 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..37da52f --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0d359d6ef33641f41a2ae67d1abdfdd3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/NetworkReaderLoop.cs + uploadId: 762203 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..3a6cc60 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2fed05d526ab23949bac6cd2bf041c35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/NetworkWriterLoop.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/PacketId.cs b/Assets/FishNet/Runtime/Transporting/PacketId.cs new file mode 100644 index 0000000..b0121a3 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/PacketId.cs @@ -0,0 +1,35 @@ +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, + SyncType = 7, + ServerRpc = 8, + ObserversRpc = 9, + TargetRpc = 10, + OwnershipChange = 11, + Broadcast = 12, + UNUSED = 13, + PingPong = 14, + Replicate = 15, + Reconcile = 16, + Disconnect = 17, + TimingUpdate = 18, + UNUSED2 = 19, + StateUpdate = 20, + Version = 21, + } + +} \ 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..57ca245 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/PacketId.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3f3b7256982245b46a2925e2b94ce149 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/PacketId.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transport.cs b/Assets/FishNet/Runtime/Transporting/Transport.cs new file mode 100644 index 0000000..fbefdef --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transport.cs @@ -0,0 +1,264 @@ +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); + + /// + /// Returns packet loss percentage. Not all transports support this feature. + /// + /// True to return packet loss on the server, false to return packet loss on the client. + public virtual float GetPacketLoss(bool asServer) => 0f; + #endregion + + #region Iterating. + /// + /// Processes data received by the socket. + /// + /// True to read data from clients, false to read data from the server. + public abstract void IterateIncoming(bool asServer); + + /// + /// Processes data to be sent by the socket. + /// + /// True to send data from the local server to clients, false to send from the local client to server. + public abstract void IterateOutgoing(bool asServer); + #endregion + + #region Configuration. + /// + /// Returns if the transport is only run locally, offline. + /// While true several security checks are disabled. + /// + /// Optional connectionId to check against. + 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."; + 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."; + 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 + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transport.cs.meta b/Assets/FishNet/Runtime/Transporting/Transport.cs.meta new file mode 100644 index 0000000..182859e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transport.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 78aba14618b37ea4bb067fa95ede84e0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transport.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/TransportConsts.cs b/Assets/FishNet/Runtime/Transporting/TransportConsts.cs new file mode 100644 index 0000000..ebb009e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/TransportConsts.cs @@ -0,0 +1,16 @@ +using FishNet.Managing; +using System; +using UnityEngine; + +namespace FishNet.Transporting +{ + + public static class TransportConsts + { + /// + /// Value used when a transport index is not known or set for a connection. + /// + public const int UNSET_TRANSPORT_INDEX = -1; + + } +} diff --git a/Assets/FishNet/Runtime/Transporting/TransportConsts.cs.meta b/Assets/FishNet/Runtime/Transporting/TransportConsts.cs.meta new file mode 100644 index 0000000..9db6bb0 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/TransportConsts.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 490a5b566793e5a4e8a97a583eab55e8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/TransportConsts.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports.meta b/Assets/FishNet/Runtime/Transporting/Transports.meta new file mode 100644 index 0000000..83abb71 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 91d2fe16f6fe54e479292316bafa8f87 +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..0637c70 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a3fe0c14d0e92e342a72a5c3c47eded6 +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..072b501 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 9b339dd67a0ce7f458236a3ad1d97322 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/CHANGELOG.txt + uploadId: 762203 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..7318357 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs @@ -0,0 +1,1083 @@ +#if UNITY_EDITOR || DEVELOPMENT_BUILD +#define DEVELOPMENT +#endif +using FishNet.Connection; +using FishNet.Managing; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Transporting.Multipass +{ + [AddComponentMenu("FishNet/Transport/Multipass")] + public class Multipass : Transport + { + #region Types. + public struct ClientTransportData : IEquatable + { + /// + /// Transport index this connection is on. + /// + public int TransportIndex; + /// + /// ConnectionId assigned by the transport. + /// + public int TransportId; + /// + /// Connection Id assigned by multipass. This Id is the one communicated to the NetworkManager. + /// + public int MultipassId; + /// + /// Cached hashcode for values. + /// + private int _hashCode; + + public ClientTransportData(int transportIndex, int transportId, int multipassId) + { + TransportIndex = transportIndex; + TransportId = transportId; + MultipassId = multipassId; + _hashCode = (transportIndex, transportId, multipassId).GetHashCode(); + } + + public bool Equals(ClientTransportData other) + { + return (_hashCode == other._hashCode); + } + } + #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(); + /// + /// Transports to use. + /// + public IReadOnlyList Transports => _transports; + #endregion + + #region Private. + /// + /// An unset/invalid ClientTransportData. + /// + private readonly ClientTransportData INVALID_CLIENTTRANSPORTDATA = new(int.MinValue, int.MinValue, int.MinValue); + /// + /// MultipassId lookup. + /// + private Dictionary _multpassIdLookup = new(); + /// + /// TransportId lookup. Each index within the list is the same as the transport index. + /// + private List> _transportIdLookup = new(); + /// + /// Ids available to new connections. + /// + private Queue _availableMultipassIds = new(); + /// + /// Last Id added to availableMultipassIds. + /// + private int _lastAvailableMultipassId = 0; + #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(); + _transportIdLookup.Add(dict); + //Initialize transports and callbacks. + _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(); + + ResetLookupCollections(); + } + + #region ClientIds. + /// + /// Resets lookup collections and caches potential garbage. + /// + private void ResetLookupCollections() + { + _multpassIdLookup.Clear(); + + for (int i = 0; i < _transportIdLookup.Count; i++) + _transportIdLookup[i].Clear(); + } + + /// + /// 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; + } + } + + ResetLookupCollections(); + CreateAvailableIds(true); + } + + /// + /// Gets the Multipass connectionId using a transport connectionid. + /// + private ClientTransportData GetDataFromTransportId(int transportIndex, int transportId, bool log) + { + Dictionary dict = _transportIdLookup[transportIndex]; + if (dict.TryGetValueIL2CPP(transportId, out ClientTransportData ctd)) + return ctd; + + //Fall through/fail. + if (log) + base.NetworkManager.LogError($"Multipass connectionId could not be found for transportIndex {transportIndex}, transportId of {transportId}."); + return INVALID_CLIENTTRANSPORTDATA; + } + + /// + /// Gets the TransportIdData using a Multipass connectionId. + /// + private ClientTransportData GetDataFromMultipassId(int multipassId) + { + if (_multpassIdLookup.TryGetValueIL2CPP(multipassId, out ClientTransportData ctd)) + return ctd; + + //Fall through/fail. + base.NetworkManager.LogError($"TransportIdData could not be found for Multipass connectionId of {multipassId}."); + return INVALID_CLIENTTRANSPORTDATA; + } + #endregion + + #region ConnectionStates. + /// + /// Gets the IP address of a remote connectionId. + /// + public override string GetConnectionAddress(int multipassId) + { + ClientTransportData ctd = GetDataFromMultipassId(multipassId); + if (ctd.Equals(INVALID_CLIENTTRANSPORTDATA)) + return string.Empty; + + return _transports[ctd.TransportIndex].GetConnectionAddress(ctd.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. + 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. + public LocalConnectionState GetConnectionState(bool server, int transportIndex) + { + if (!IndexInRange(transportIndex, true)) + return LocalConnectionState.Stopped; + + return _transports[transportIndex].GetConnectionState(server); + } + + /// + /// Gets the current ConnectionState of a remote client on the server. + /// + /// ConnectionId to get ConnectionState for. + public override RemoteConnectionState GetConnectionState(int multipassId) + { + ClientTransportData ctd = GetDataFromMultipassId(multipassId); + if (ctd.Equals(INVALID_CLIENTTRANSPORTDATA)) + return RemoteConnectionState.Stopped; + + return _transports[ctd.TransportIndex].GetConnectionState(ctd.TransportId); + } + + /// + /// Gets the current ConnectionState of a remote client on the server of the transport on index. + /// + /// ConnectionId to get ConnectionState for. + 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 transportConnectionId = connectionStateArgs.ConnectionId; + /* MultipassId is set to a new value when connecting + * or discovered value when disconnecting. */ + int multipassId; + Dictionary transportToMultipass = _transportIdLookup[transportIndex]; + + //Started. + if (connectionStateArgs.ConnectionState == RemoteConnectionState.Started) + { + if (_availableMultipassIds.Count == 0) + { + bool addedIds = CreateAvailableIds(false); + if (!addedIds) + { + base.NetworkManager.Log($"There are no more available connectionIds to use. Connection {transportConnectionId} has been kicked."); + _transports[transportIndex].StopConnection(transportConnectionId, true); + return; + } + } + //Get a multipassId for new connections. + multipassId = _availableMultipassIds.Dequeue(); + + //Get and update a clienttransportdata. + ClientTransportData ctd = new(transportIndex, transportConnectionId, multipassId); + //Assign the lookup for transportId/index. + transportToMultipass[transportConnectionId] = ctd; + //Assign the lookup for multipassId. + _multpassIdLookup[multipassId] = ctd; + + //Update args to use multipassId before invoking. + connectionStateArgs.ConnectionId = multipassId; + OnRemoteConnectionState?.Invoke(connectionStateArgs); + } + //Stopped. + else + { + Transport transport = _transports[transportIndex]; + //Only log if the server is started for the specified transport. + bool log = (transport.GetConnectionState(server: true) == LocalConnectionState.Started); + + ClientTransportData ctd = GetDataFromTransportId(transportIndex, transportConnectionId, log); + /* If CTD could not be found then the connection + * is not stored/known. Nothing further can be done; the event cannot + * invoke either since Id is unknown. */ + if (ctd.Equals(INVALID_CLIENTTRANSPORTDATA)) + return; + + //Add the multipassId back to the queue. + _availableMultipassIds.Enqueue(ctd.MultipassId); + transportToMultipass.Remove(transportConnectionId); + _multpassIdLookup.Remove(ctd.MultipassId); +#if DEVELOPMENT + //Remove packets held for connection from latency simulator. + base.NetworkManager.TransportManager.LatencySimulator.RemovePendingForConnection(ctd.MultipassId); +#endif + + //Update args to use multipassId before invoking. + connectionStateArgs.ConnectionId = ctd.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 asServer) + { + foreach (Transport t in _transports) + t.IterateIncoming(asServer); + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to send data from the local server to clients, false to send from the local client to server. + public override void IterateOutgoing(bool asServer) + { + foreach (Transport t in _transports) + t.IterateOutgoing(asServer); + } + #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) + { + ClientTransportData ctd = GetDataFromTransportId(receivedDataArgs.TransportIndex, receivedDataArgs.ConnectionId, log: true); + if (ctd.Equals(INVALID_CLIENTTRANSPORTDATA)) + return; + + receivedDataArgs.ConnectionId = ctd.MultipassId; + OnServerReceivedData?.Invoke(receivedDataArgs); + } + #endregion + + #region Sending. + /// + /// Sends to the server on ClientTransport. + /// + /// Channel to use. + /// /// Data to send. + public override void SendToServer(byte channelId, ArraySegment segment) + { + if (ClientTransport != null) + ClientTransport.SendToServer(channelId, segment); + } + + /// + /// Sends data to a client. + /// + public override void SendToClient(byte channelId, ArraySegment segment, int multipassId) + { + ClientTransportData ctd = GetDataFromMultipassId(multipassId); + if (ctd.Equals(INVALID_CLIENTTRANSPORTDATA)) + return; + + _transports[ctd.TransportIndex].SendToClient(channelId, segment, ctd.TransportId); + } + + /// + /// Sends data to a client. + /// + /// TransportIndex the client is using. + public void SendToClient(byte channelId, ArraySegment segment, int transportId, int transportIndex) + { + _transports[transportIndex].SendToClient(channelId, segment, 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. + /// + /// True if at least 1 Id was added. + private bool CreateAvailableIds(bool reset) + { + if (reset) + { + _lastAvailableMultipassId = 0; + _availableMultipassIds.Clear(); + } + //Add in blocks of 1000. + int added = 0; + while ((_lastAvailableMultipassId <= NetworkConnection.MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE) && (added < 1000)) + { + added++; + _availableMultipassIds.Enqueue(_lastAvailableMultipassId); + _lastAvailableMultipassId++; + } + + return (added > 0); + } + + /// + /// 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 first transport is a local transport, optionally checking against connectionId. + /// While true several security checks are disabled. + /// + public override bool IsLocalTransport(int connectionId) + { + foreach (Transport item in _transports) + return item.IsLocalTransport(connectionId); + + return false; + } + + /// + /// Returns if the transportId is a local transport, optionally checking against connectionId. + /// While true several security checks are disabled. + /// + public bool IsLocalTransport(int transportId, int connectionId) + { + if (!IndexInRange(transportId, true)) + return false; + + return _transports[transportId].IsLocalTransport(connectionId); + } + + /// + /// 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. + /// This method is not supported. Use GetMaximumClients(transportIndex) instead. + /// + /// + 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. + /// + /// + 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 for every transport. + /// + /// + public override void SetMaximumClients(int value) + { + foreach (Transport t in _transports) + t.SetMaximumClients(value); + } + + /// + /// 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); + } + + /// + /// Sets which address the client will connect to. + /// + /// + /// Transport index to set for. + public void SetClientAddress(string address, int index) + { + if (!IndexInRange(index, true)) + return; + + _transports[index].SetClientAddress(address); + } + + /// + /// Sets which address the server will bind to. + /// This will set the address for every transport. + /// + public override void SetServerBindAddress(string address, IPAddressType addressType) + { + foreach (Transport t in _transports) + t.SetServerBindAddress(address, addressType); + } + + /// 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. + /// This will set the port for every transport. + /// + public override void SetPort(ushort port) + { + foreach (Transport t in _transports) + t.SetPort(port); + } + + /// + /// 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); + } + + /// + /// Gets the first transports port. + /// + /// + public override ushort GetPort() + { + foreach (Transport t in _transports) + return t.GetPort(); + + return base.GetPort(); + } + #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 dictionary for transportIndex. + Dictionary dict = _transportIdLookup[transportIndex]; + //Create an array containing all multipass Ids for transportIndex. + int[] multipassIds = new int[dict.Count]; + int index = 0; + foreach (ClientTransportData item in dict.Values) + multipassIds[index++] = item.MultipassId; + //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(asServer: 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 multipassId, bool immediately) + { + ClientTransportData ctd = GetDataFromMultipassId(multipassId); + if (ctd.Equals(INVALID_CLIENTTRANSPORTDATA)) + return false; + + return _transports[ctd.TransportIndex].StopConnection(ctd.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..088248c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 314b449d3505bd24487ba69b61c2fda5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/Multipass.cs + uploadId: 762203 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..6a05e85 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: e4f9d944e2ca8484587859cf4ec80b6c +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Multipass/VERSION.txt + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta new file mode 100644 index 0000000..3348740 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4f3b3f854a379684a92d54e1034c3a1a +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..b1eb7b1 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e13f31983d04611469a690fc65e81b02 +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..46229ea --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs @@ -0,0 +1,279 @@ +using FishNet.Managing; +using FishNet.Managing.Logging; +using LiteNetLib; +using LiteNetLib.Layers; +using System; +using System.Collections.Concurrent; +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. + /// + /// Inbound messages which need to be handled. + /// + private ConcurrentQueue _incoming = new(); + /// + /// Outbound messages which need to be handled. + /// + private Queue _outgoing = new(); + #endregion + /// + /// How long in seconds until client times from server. + /// + private int _timeout; + /// + /// PacketLayer to use with LiteNetLib. + /// + private PacketLayerBase _packetLayer; + #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(base.NetManager, timeout); + } + + /// + /// Polls the socket for new data. + /// + + internal void PollSocket() + { + base.PollSocket(base.NetManager); + } + + /// + /// Threaded operation to process client actions. + /// + private void ThreadedSocket() + { + EventBasedNetListener listener = new(); + listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent; + listener.PeerConnectedEvent += Listener_PeerConnectedEvent; + listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent; + + base.NetManager = new(listener, _packetLayer, false); + base.NetManager.DontRoute = ((Tugboat)base.Transport).DontRoute; + base.NetManager.MtuOverride = (_mtu + NetConstants.FragmentedHeaderTotalSize); + + UpdateTimeout(_timeout); + + base.LocalConnectionStates.Enqueue(LocalConnectionState.Starting); + base.NetManager.Start(); + base.NetManager.Connect(_address, _port, string.Empty); + } + + /// + /// Starts the client connection. + /// + internal bool StartConnection(string address, ushort port) + { + //Force a stop just in case the socket did not clean up. + if (base.GetConnectionState() != LocalConnectionState.Stopped) + base.StopSocket(); + //Enqueue starting. + base.LocalConnectionStates.Enqueue(LocalConnectionState.Starting); + //Iterate to cause state changes to invoke. + IterateIncoming(); + + //Assign properties. + _port = port; + _address = address; + + ResetQueues(); + 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); + base.StopSocket(); + return true; + } + + /// + /// Resets queues. + /// + + private void ResetQueues() + { + base.ClearGenericQueue(ref base.LocalConnectionStates); + 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) + { + base.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 (base.NetManager != null) + peer = base.NetManager.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() + { + /* 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 (base.LocalConnectionStates.TryDequeue(out LocalConnectionState result)) + base.SetConnectionState(result, 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) + { + base.StopSocket(); + return; + } + } + + /* Incoming. */ + while (_incoming.TryDequeue(out Packet incoming)) + { + ClientReceivedDataArgs dataArgs = new( + 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..05e1d2b --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7944de5e4da77594db036e276174ee60 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ClientSocket.cs + uploadId: 762203 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..1a7d229 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs @@ -0,0 +1,216 @@ +using FishNet.Utility.Performance; +using LiteNetLib; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace FishNet.Transporting.Tugboat +{ + + public abstract class CommonSocket + { + + #region Internal. + /// + /// 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(connectionState, Transport.Index)); + else + Transport.HandleClientConnectionState(new(connectionState, Transport.Index)); + } + #endregion + + #region Internal. + /// + /// NetManager for this socket. + /// + internal NetManager NetManager; + #endregion + + #region Protected. + /// + /// Changes to the sockets local connection state. + /// + protected ConcurrentQueue LocalConnectionStates = new(); + /// + /// Transport controlling this socket. + /// + protected Transport Transport; + #endregion + + #region Private. + /// + /// Locks the NetManager to stop it. + /// + private readonly object _stopLock = new(); + #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(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 ConcurrentQueue of any type. + /// + internal void ClearGenericQueue(ref ConcurrentQueue queue) + { + while (queue.TryDequeue(out _)) { } + } + + /// + /// 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(ConcurrentQueue 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(id, data, dataLen, channel); + queue.Enqueue(packet); + //Recycle reader. + reader.Recycle(); + } + + internal void PollSocket(NetManager nm) + { + nm?.PollEvents(); + } + + /// + /// Stops the socket and updates local connection state. + /// + protected void StopSocket() + { + if (NetManager == null) + return; + + bool threaded; + if (Transport is Tugboat tb) + threaded = tb.StopSocketsOnThread; + else + threaded = false; + + //If using a thread. + if (threaded) + { + Task.Run(() => + { + lock (_stopLock) + { + NetManager?.Stop(); + NetManager = null; + } + + //If not stopped yet also enqueue stop. + if (GetConnectionState() != LocalConnectionState.Stopped) + LocalConnectionStates.Enqueue(LocalConnectionState.Stopped); + }); + } + //Not using a thread. + else + { + NetManager?.Stop(); + NetManager = null; + //If not stopped yet also enqueue stop. + if (GetConnectionState() != LocalConnectionState.Stopped) + LocalConnectionStates.Enqueue(LocalConnectionState.Stopped); + } + } + + /// + /// Returns the port from the socket if active, otherwise returns null. + /// + /// + internal ushort? GetPort() + { + if (NetManager == null || !NetManager.IsRunning) + return null; + + int port = NetManager.LocalPort; + if (port < 0) + port = 0; + else if (port > ushort.MaxValue) + port = ushort.MaxValue; + + return (ushort)port; + } + + } + +} \ 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..30727ed --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 329169cdf51866c43a8c42e8aeb291fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/CommonSocket.cs + uploadId: 762203 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..16ad1a6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs @@ -0,0 +1,502 @@ +using FishNet.Connection; +using FishNet.Managing; +using FishNet.Managing.Logging; +using LiteNetLib; +using LiteNetLib.Layers; +using System; +using System.Collections.Concurrent; +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. + /// + /// Inbound messages which need to be handled. + /// + private ConcurrentQueue _incoming = new(); + /// + /// Outbound messages which need to be handled. + /// + private Queue _outgoing = new(); + /// + /// ConnectionEvents which need to be handled. + /// + private ConcurrentQueue _remoteConnectionEvents = new(); + #endregion + + /// + /// How long in seconds until client times from server. + /// + private int _timeout; + /// + /// 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; + /// + /// IPv6 is enabled only on demand, by default LiteNetLib always listens on IPv4 AND IPv6 which causes problems + /// if IPv6 is disabled on host. This can be the case in Linux environments + /// + private bool _enableIPv6; + #endregion + + ~ServerSocket() + { + StopConnection(); + } + + /// + /// Initializes this for use. + /// + /// + internal void Initialize(Transport t, int unreliableMTU, PacketLayerBase packetLayer, bool enableIPv6) + { + base.Transport = t; + _mtu = unreliableMTU; + _packetLayer = packetLayer; + _enableIPv6 = enableIPv6; + } + + /// + /// Updates the Timeout value as seconds. + /// + internal void UpdateTimeout(int timeout) + { + _timeout = timeout; + base.UpdateTimeout(base.NetManager, timeout); + } + + /// + /// Polls the socket for new data. + /// + internal void PollSocket() + { + base.PollSocket(base.NetManager); + } + + /// + /// Threaded operation to process server actions. + /// + private void ThreadedSocket() + { + EventBasedNetListener listener = new(); + listener.ConnectionRequestEvent += Listener_ConnectionRequestEvent; + listener.PeerConnectedEvent += Listener_PeerConnectedEvent; + listener.NetworkReceiveEvent += Listener_NetworkReceiveEvent; + listener.PeerDisconnectedEvent += Listener_PeerDisconnectedEvent; + + base.NetManager = new(listener, _packetLayer, false); + base.NetManager.DontRoute = ((Tugboat)base.Transport).DontRoute; + base.NetManager.ReuseAddress = ((Tugboat)base.Transport).ReuseAddress; + base.NetManager.MtuOverride = (_mtu + NetConstants.FragmentedHeaderTotalSize); + + UpdateTimeout(_timeout); + + //Set bind addresses. + IPAddress ipv4 = null; + IPAddress ipv6 = null; + + //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); + } + + if (_enableIPv6 && !string.IsNullOrEmpty(_ipv6BindAddress)) + { + //Set ipv6 if protocol is enabled. + 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 = (_enableIPv6 && 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; + } + + base.NetManager.IPv6Enabled = _enableIPv6; + bool startResult = base.NetManager.Start(ipv4, ipv6, _port); + //If started succcessfully. + if (startResult) + { + base.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(); + } + } + + /// + /// Gets the address of a remote connection Id. + /// + /// + /// Returns string.empty if Id is not found. + internal string GetConnectionAddress(int connectionId) + { + if (GetConnectionState() != LocalConnectionState.Started) + { + NetworkManager nm = (Transport == null) ? null : Transport.NetworkManager; + string msg = "Server socket is not started."; + nm.LogWarning(msg); + return string.Empty; + } + + NetPeer peer = GetNetPeer(connectionId, false); + if (peer == null) + { + Transport.NetworkManager.LogWarning($"Connection Id {connectionId} returned a null NetPeer."); + return string.Empty; + } + + return peer.Address.ToString(); + } + + /// + /// Returns a NetPeer for connectionId. + /// + /// + /// + private NetPeer GetNetPeer(int connectionId, bool connectedOnly) + { + if (base.NetManager != null) + { + NetPeer peer = base.NetManager.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) + { + //Force a stop just in case the socket did not clean up. + if (base.GetConnectionState() != LocalConnectionState.Stopped) + base.StopSocket(); + //Enqueue starting. + base.LocalConnectionStates.Enqueue(LocalConnectionState.Starting); + //Iterate to cause state changes to invoke. + IterateIncoming(); + + //Assign properties. + _port = port; + _maximumClients = maximumClients; + _ipv4BindAddress = ipv4BindAddress; + _ipv6BindAddress = ipv6BindAddress; + ResetQueues(); + + Task.Run(ThreadedSocket); + + return true; + } + + /// + /// Stops the local socket. + /// + internal bool StopConnection() + { + if (base.NetManager == null || base.GetConnectionState() == LocalConnectionState.Stopped || base.GetConnectionState() == LocalConnectionState.Stopping) + return false; + + base.LocalConnectionStates.Enqueue(LocalConnectionState.Stopping); + base.StopSocket(); + 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 (base.NetManager == 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() + { + base.ClearGenericQueue(ref base.LocalConnectionStates); + base.ClearPacketQueue(ref _incoming); + base.ClearPacketQueue(ref _outgoing); + base.ClearGenericQueue(ref _remoteConnectionEvents); + } + + /// + /// Called when a peer disconnects or times out. + /// + private void Listener_PeerDisconnectedEvent(NetPeer peer, DisconnectInfo disconnectInfo) + { + _remoteConnectionEvents.Enqueue(new(false, peer.Id)); + } + + /// + /// Called when a peer completes connection. + /// + private void Listener_PeerConnectedEvent(NetPeer peer) + { + _remoteConnectionEvents.Enqueue(new(true, peer.Id)); + } + + /// + /// Called when data is received from a peer. + /// + private void Listener_NetworkReceiveEvent(NetPeer fromPeer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod) + { + //If over the MTU. + if (reader.AvailableBytes > _mtu) + { + _remoteConnectionEvents.Enqueue(new(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 (base.NetManager == null) + return; + + //At maximum peers. + if (base.NetManager.ConnectedPeersCount >= _maximumClients) + { + request.Reject(); + return; + } + + request.AcceptIfKey(key: string.Empty); + } + + /// + /// Dequeues and processes outgoing. + /// + private void DequeueOutgoing() + { + if (base.GetConnectionState() != LocalConnectionState.Started || base.NetManager == 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) + { + base.NetManager.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. + /// + internal void IterateIncoming() + { + /* 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 (base.LocalConnectionStates.TryDequeue(out LocalConnectionState result)) + base.SetConnectionState(result, true); + + //Not yet started. + LocalConnectionState localState = base.GetConnectionState(); + if (localState != LocalConnectionState.Started) + { + ResetQueues(); + //If stopped try to kill task. + if (localState == LocalConnectionState.Stopped) + { + base.StopSocket(); + return; + } + } + + //Handle connection and disconnection events. + while (_remoteConnectionEvents.TryDequeue(out RemoteConnectionEvent connectionEvent)) + { + RemoteConnectionState state = (connectionEvent.Connected) ? RemoteConnectionState.Started : RemoteConnectionState.Stopped; + base.Transport.HandleRemoteConnectionState(new(state, connectionEvent.ConnectionId, base.Transport.Index)); + } + + //Handle packets. + while (_incoming.TryDequeue(out Packet incoming)) + { + //Make sure peer is still connected. + NetPeer peer = GetNetPeer(incoming.ConnectionId, true); + if (peer != null) + { + ServerReceivedDataArgs dataArgs = new(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. + /// + 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 Math.Min(_maximumClients, NetworkConnection.MAXIMUM_CLIENTID_WITHOUT_SIMULATED_VALUE); + } + + /// + /// Sets the MaximumClients value. + /// + /// + internal void SetMaximumClients(int value) + { + _maximumClients = value; + } + } +} \ No newline at end of file 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..9bf0494 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5c6703e8024041e45ae92566123865ad +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/ServerSocket.cs + uploadId: 762203 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..c248a36 --- /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(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..68bef1a --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 80c810a1a6a8f3345bb48abfb75c804a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Core/Supporting.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor.meta new file mode 100644 index 0000000..dd29fbc --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a537e10eb38767b4085a7bec020f9675 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs new file mode 100644 index 0000000..c38c899 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs @@ -0,0 +1,94 @@ +#if UNITY_EDITOR +using FishNet.Object; +using UnityEditor; +using UnityEngine; + +namespace FishNet.Transporting.Tugboat.Editing +{ + + [CustomEditor(typeof(Tugboat), true)] + [CanEditMultipleObjects] + public class TugboatEditor : Editor + { + private SerializedProperty _stopSocketsOnThread; + private SerializedProperty _dontRoute; + private SerializedProperty _reuseAddress; + private SerializedProperty _unreliableMtu; + + private SerializedProperty _ipv4BindAddress; + private SerializedProperty _enableIpv6; + private SerializedProperty _ipv6BindAddress; + private SerializedProperty _port; + private SerializedProperty _maximumClients; + + private SerializedProperty _clientAddress; + + + protected virtual void OnEnable() + { + _stopSocketsOnThread = serializedObject.FindProperty(nameof(_stopSocketsOnThread)); + _dontRoute = serializedObject.FindProperty(nameof(_dontRoute)); + _reuseAddress = serializedObject.FindProperty(nameof(_reuseAddress)); + _unreliableMtu = serializedObject.FindProperty(nameof(_unreliableMtu)); + _ipv4BindAddress = serializedObject.FindProperty(nameof(_ipv4BindAddress)); + _enableIpv6 = serializedObject.FindProperty(nameof(_enableIpv6)); + _ipv6BindAddress = serializedObject.FindProperty(nameof(_ipv6BindAddress)); + _port = serializedObject.FindProperty(nameof(_port)); + _maximumClients = serializedObject.FindProperty(nameof(_maximumClients)); + _clientAddress = serializedObject.FindProperty(nameof(_clientAddress)); + } + + public override void OnInspectorGUI() + { + serializedObject.Update(); + Tugboat tb = (Tugboat)target; + + GUI.enabled = false; + EditorGUILayout.ObjectField("Script:", MonoScript.FromMonoBehaviour(tb), typeof(Tugboat), false); + GUI.enabled = true; + + EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_stopSocketsOnThread); + EditorGUILayout.PropertyField(_dontRoute); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Channels", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_unreliableMtu); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Server", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_reuseAddress); + EditorGUILayout.PropertyField(_ipv4BindAddress); + EditorGUILayout.PropertyField(_enableIpv6); + if (_enableIpv6.boolValue == true) + { + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_ipv6BindAddress); + EditorGUI.indentLevel--; + } + EditorGUILayout.PropertyField(_port); + EditorGUILayout.PropertyField(_maximumClients); + EditorGUI.indentLevel--; + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Client", EditorStyles.boldLabel); + EditorGUI.indentLevel++; + EditorGUILayout.PropertyField(_clientAddress); + EditorGUI.indentLevel--; + + serializedObject.ApplyModifiedProperties(); + } + + + + } + +} + + +#endif \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs.meta new file mode 100644 index 0000000..86d8eb6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 578d8bb2922ac0648a90b969512b9779 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Editor/TugboatEditor.cs + uploadId: 762203 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..8b54f81 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 12f412d6d520d5c45bb49d177690e507 +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..1fe4458 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Threading; + +namespace LiteNetLib +{ + internal abstract class BaseChannel + { + protected readonly NetPeer Peer; + protected readonly Queue OutgoingQueue = new(NetConstants.DefaultWindowSize); + private int _isAddedToPeerChannelSendQueue; + + public int PacketsInQueue => OutgoingQueue.Count; + + protected BaseChannel(NetPeer peer) + { + Peer = peer; + } + + public void AddToQueue(NetPacket packet) + { + lock (OutgoingQueue) + { + 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..f487e18 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 70b7c357a9f57f5479c5d94550d26280 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/BaseChannel.cs + uploadId: 762203 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..69503d1 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5e609855f95e9034889c882b51aaec68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ConnectionRequest.cs + uploadId: 762203 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..f5f9b00 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 79436ed5864cf48418ac341ea3c70a6b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/INetEventListener.cs + uploadId: 762203 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..cd42639 --- /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(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(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..82dde08 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b8582459906515843a2f2adb010c3fd7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/InternalPackets.cs + uploadId: 762203 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..2343bc6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5fa8ca1b7ce0a0041a2ddfebeef8c8fe +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..dc07fe1 --- /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 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, 0, 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..24b61ad --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d52aafc1486ec842a8d133ef41dfd39 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/Crc32cLayer.cs + uploadId: 762203 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..f2e4c88 --- /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 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..49f14f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bddd0b5590cce7e42918c72429b84bde +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/PacketLayerBase.cs + uploadId: 762203 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..a3a130b --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs @@ -0,0 +1,59 @@ +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 length) + { + if (_byteKey == null) + return; + for (int i = 0; i < length; i++) + { + data[i] = (byte)(data[i] ^ _byteKey[i % _byteKey.Length]); + } + } + + public override void ProcessOutBoundPacket(ref IPEndPoint endPoint, ref byte[] data, ref int offset, ref int length) + { + if (_byteKey == null) + return; + int cur = offset; + for (int 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..9c9d0b0 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: fb37499ecdd1ce145b0bcaf4b4f0279e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Layers/XorEncryptLayer.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj new file mode 100644 index 0000000..4ba03de --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj @@ -0,0 +1,66 @@ + + + + LiteNetLib + LiteNetLib + net6.0;net5.0;netcoreapp3.1;netstandard2.0;netstandard2.1 + net471;net6.0;net5.0;netstandard2.0;netstandard2.1;netcoreapp3.1 + true + Library + 7.3 + + true + 1701;1702;1705;1591 + 1.2.0 + Lite reliable UDP library for Mono and .NET + true + true + 1.2.0 + + + + TRACE;DEBUG + + + + TRACE + + + + true + $(DefineConstants);LITENETLIB_UNSAFE + udp reliable-udp network + https://github.com/RevenantX/LiteNetLib/releases/tag/v1.2.0 + git + https://github.com/RevenantX/LiteNetLib + https://github.com/RevenantX/LiteNetLib + MIT + True + 1.1.0 + Ruslan Pyrch + Copyright 2023 Ruslan Pyrch + Lite reliable UDP library for .NET, Mono, and .NET Core + LNL.png + README.md + net5.0;net6.0;net7.0;net8.0;netstandard2.0;netstandard2.1;net471 + + + + + + + + + + + + True + \ + + + True + \ + + + + \ No newline at end of file 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..6656a7c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 1ec83424c5410b04aab06dddca60e7f6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/LiteNetLib.csproj + uploadId: 762203 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..0d0f926 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs @@ -0,0 +1,264 @@ +using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Net; +using System.Net.Sockets; +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 { [Preserve] get; [Preserve] set; } + public string Token { [Preserve] get; [Preserve] set; } + } + + class NatIntroduceResponsePacket + { + public IPEndPoint Internal { [Preserve] get; [Preserve] set; } + public IPEndPoint External { [Preserve] get; [Preserve] set; } + public string Token { [Preserve] get; [Preserve] set; } + } + + class NatPunchPacket + { + public string Token { [Preserve] get; [Preserve] set; } + public bool IsExternal { [Preserve] get; [Preserve] set; } + } + + private readonly NetManager _socket; + private readonly ConcurrentQueue _requestEvents = new(); + private readonly ConcurrentQueue _successEvents = new(); + private readonly NetDataReader _cacheReader = new(); + private readonly NetDataWriter _cacheWriter = new(); + private readonly NetPacketProcessor _netPacketProcessor = new(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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) || masterServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6) + { + 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() + { + 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 {senderEndPoint} - additional info: {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() + { + 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..64e37e4 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e608aa88ab820244a90b83eca0f716c5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NatPunchModule.cs + uploadId: 762203 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..60a0f61 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace LiteNetLib +{ + internal static class NativeSocket + { + static unsafe 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, + byte* pinnedBuffer, + [In] int len, + [In] SocketFlags socketFlags, + [In] byte[] socketAddress, + [In] int socketAddressSize); + } + + static unsafe 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, + byte* pinnedBuffer, + [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() + { + { 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; + } + } + + + 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); + } + + + public static unsafe int SendTo( + IntPtr socketHandle, + byte* pinnedBuffer, + 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((int)err) + : new SocketException((int)SocketError.SocketError); + return new(error); + } + + + 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..2471e94 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 12cfcf4490f2b8f4db979ee833ecf5af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NativeSocket.cs + uploadId: 762203 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..c7e4933 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: b42ee5a523e67ff4c9149f91f7fe4245 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetConstants.cs + uploadId: 762203 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..cec1952 --- /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(); + 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) + { + WriteLogic(NetLogLevel.Trace, str); + } + + [Conditional("DEBUG_MESSAGES")] + internal static void Write(NetLogLevel level, string str) + { + WriteLogic(level, str); + } + + [Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] + internal static void WriteForce(string str) + { + WriteLogic(NetLogLevel.Trace, str); + } + + [Conditional("DEBUG_MESSAGES"), Conditional("DEBUG")] + internal static void WriteForce(NetLogLevel level, string str) + { + WriteLogic(level, str); + } + + internal static void WriteError(string str) + { + WriteLogic(NetLogLevel.Error, str); + } + } +} 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..1792968 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9f50ae51c124bf1439e339eee1fcd6f5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetDebug.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs new file mode 100644 index 0000000..1b5133c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs @@ -0,0 +1,312 @@ +using System; +using System.Net; +using System.Threading; + +namespace LiteNetLib +{ + //minimal hashset class from dotnet with some optimizations + public partial class NetManager + { + private const int MaxPrimeArrayLength = 0x7FFFFFC3; + private const int HashPrime = 101; + private const int Lower31BitMask = 0x7FFFFFFF; + private static readonly int[] Primes = + { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919, + 1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437, + 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369 + }; + + private static int HashSetGetPrime(int min) + { + foreach (int prime in Primes) + { + if (prime >= min) + return prime; + } + + // Outside of our predefined table. Compute the hard way. + for (int i = (min | 1); i < int.MaxValue; i += 2) + { + if (IsPrime(i) && ((i - 1) % HashPrime != 0)) + return i; + } + return min; + + bool IsPrime(int candidate) + { + if ((candidate & 1) != 0) + { + int limit = (int)Math.Sqrt(candidate); + for (int divisor = 3; divisor <= limit; divisor += 2) + { + if (candidate % divisor == 0) + return false; + } + return true; + } + return candidate == 2; + } + } + + private struct Slot + { + internal int HashCode; + internal int Next; + internal NetPeer Value; + } + + private int[] _buckets; + private Slot[] _slots; + private int _count; + private int _lastIndex; + private int _freeList = -1; + private NetPeer[] _peersArray = new NetPeer[32]; + private readonly ReaderWriterLockSlim _peersLock = new(LockRecursionPolicy.NoRecursion); + private volatile NetPeer _headPeer; + + private void ClearPeerSet() + { + _peersLock.EnterWriteLock(); + _headPeer = null; + if (_lastIndex > 0) + { + Array.Clear(_slots, 0, _lastIndex); + Array.Clear(_buckets, 0, _buckets.Length); + _lastIndex = 0; + _count = 0; + _freeList = -1; + } + _peersArray = new NetPeer[32]; + _peersLock.ExitWriteLock(); + } + + private bool ContainsPeer(NetPeer item) + { + if (_buckets != null) + { + int hashCode = item.GetHashCode() & Lower31BitMask; + for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next) + { + if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(item)) + return true; + } + } + return false; + } + + /// + /// Gets peer by peer id + /// + /// id of peer + /// Peer if peer with id exist, otherwise null + public NetPeer GetPeerById(int id) + { + return id >= 0 && id < _peersArray.Length ? _peersArray[id] : 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; + } + + private void AddPeer(NetPeer peer) + { + _peersLock.EnterWriteLock(); + if (_headPeer != null) + { + peer.NextPeer = _headPeer; + _headPeer.PrevPeer = peer; + } + _headPeer = peer; + AddPeerToSet(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; + _peersLock.ExitWriteLock(); + } + + private void RemovePeer(NetPeer peer) + { + _peersLock.EnterWriteLock(); + RemovePeerInternal(peer); + _peersLock.ExitWriteLock(); + } + + private void RemovePeerInternal(NetPeer peer) + { + if (!RemovePeerFromSet(peer)) + 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); + } + + private bool RemovePeerFromSet(NetPeer peer) + { + if (_buckets == null) + return false; + if (peer == null) + return false; + int hashCode = peer.GetHashCode() & Lower31BitMask; + int bucket = hashCode % _buckets.Length; + int last = -1; + for (int i = _buckets[bucket] - 1; i >= 0; last = i, i = _slots[i].Next) + { + if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(peer)) + { + if (last < 0) + _buckets[bucket] = _slots[i].Next + 1; + else + _slots[last].Next = _slots[i].Next; + _slots[i].HashCode = -1; + _slots[i].Value = null; + _slots[i].Next = _freeList; + + _count--; + if (_count == 0) + { + _lastIndex = 0; + _freeList = -1; + } + else + { + _freeList = i; + } + return true; + } + } + return false; + } + + private bool TryGetPeer(IPEndPoint endPoint, out NetPeer actualValue) + { + if (_buckets != null) + { +#if NET8_0_OR_GREATER + //can be NetPeer or IPEndPoint + int hashCode = (UseNativeSockets ? endPoint.GetHashCode() : endPoint.Serialize().GetHashCode()) & Lower31BitMask; +#else + int hashCode = endPoint.GetHashCode() & Lower31BitMask; +#endif + _peersLock.EnterReadLock(); + for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next) + { + if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(endPoint)) + { + actualValue = _slots[i].Value; + _peersLock.ExitReadLock(); + return true; + } + } + _peersLock.ExitReadLock(); + } + actualValue = null; + return false; + } + + //only used for NET8 + private bool TryGetPeer(SocketAddress saddr, out NetPeer actualValue) + { + if (_buckets != null) + { + int hashCode = saddr.GetHashCode() & Lower31BitMask; + _peersLock.EnterReadLock(); + for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next) + { + if (_slots[i].HashCode == hashCode && _slots[i].Value.Serialize().Equals(saddr)) + { + actualValue = _slots[i].Value; + _peersLock.ExitReadLock(); + return true; + } + } + _peersLock.ExitReadLock(); + } + actualValue = null; + return false; + } + + private bool AddPeerToSet(NetPeer value) + { + if (_buckets == null) + { + int size = HashSetGetPrime(0); + _buckets = new int[size]; + _slots = new Slot[size]; + } + + int hashCode = value.GetHashCode() & Lower31BitMask; + int bucket = hashCode % _buckets.Length; + for (int i = _buckets[hashCode % _buckets.Length] - 1; i >= 0; i = _slots[i].Next) + { + if (_slots[i].HashCode == hashCode && _slots[i].Value.Equals(value)) + return false; + } + + int index; + if (_freeList >= 0) + { + index = _freeList; + _freeList = _slots[index].Next; + } + else + { + if (_lastIndex == _slots.Length) + { + //increase capacity + int newSize = 2 * _count; + newSize = (uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > _count + ? MaxPrimeArrayLength + : HashSetGetPrime(newSize); + + // Able to increase capacity; copy elements to larger array and rehash + Slot[] newSlots = new Slot[newSize]; + Array.Copy(_slots, 0, newSlots, 0, _lastIndex); + _buckets = new int[newSize]; + for (int i = 0; i < _lastIndex; i++) + { + int b = newSlots[i].HashCode % newSize; + newSlots[i].Next = _buckets[b] - 1; + _buckets[b] = i + 1; + } + _slots = newSlots; + // this will change during resize + bucket = hashCode % _buckets.Length; + } + index = _lastIndex; + _lastIndex++; + } + _slots[index].HashCode = hashCode; + _slots[index].Value = value; + _slots[index].Next = _buckets[bucket] - 1; + _buckets[bucket] = index + 1; + _count++; + + return true; + } + } + +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs.meta new file mode 100644 index 0000000..541e293 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a3dad2c47fd51d14fbb281bf3cb231e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.HashSet.cs + uploadId: 762203 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..18cb150 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs @@ -0,0 +1,82 @@ +using System; + +namespace LiteNetLib +{ + public partial class NetManager + { + private NetPacket _poolHead; + private int _poolCount; + private readonly object _poolLock = new(); + + /// + /// 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) + { + if (size > NetConstants.MaxPacketSize) + return new(size); + + NetPacket packet; + lock (_poolLock) + { + packet = _poolHead; + if (packet == null) + return new(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..bfe7c57 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: d384bada29340e542945f001cad348ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.PacketPool.cs + uploadId: 762203 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..68a9002 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs @@ -0,0 +1,707 @@ +#if UNITY_2018_3_OR_NEWER +#define UNITY_SOCKET_FIX +#endif +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 + { + private const int ReceivePollingTime = 500000; //0.5 second + + private Socket _udpSocketv4; + private Socket _udpSocketv6; + private Thread _receiveThread; + private IPEndPoint _bufferEndPointv4; + private IPEndPoint _bufferEndPointv6; +#if UNITY_SOCKET_FIX + private PausedSocketFix _pausedSocketFix; + private bool _useSocketFix; +#endif + +#if NET8_0_OR_GREATER + private readonly SocketAddress _sockAddrCacheV4 = new SocketAddress(AddressFamily.InterNetwork); + private readonly SocketAddress _sockAddrCacheV6 = new SocketAddress(AddressFamily.InterNetworkV6); +#endif + + 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; + + // special case in iOS (and possibly android that should be resolved in unity) + internal bool NotConnected; + + 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 ProcessError(SocketException ex) + { + switch (ex.SocketErrorCode) + { + case SocketError.NotConnected: + NotConnected = true; + return true; + case SocketError.Interrupted: + case SocketError.NotSocket: + case SocketError.OperationAborted: + return true; + case SocketError.ConnectionReset: + case SocketError.MessageSize: + case SocketError.TimedOut: + case SocketError.NetworkReset: + case SocketError.WouldBlock: + //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, int maxReceive) + { + //Reading data + try + { + int packetsReceived = 0; + while (socket.Available > 0) + { + ReceiveFrom(socket, ref bufferEndPoint); + packetsReceived++; + if (packetsReceived == maxReceive) + break; + } + } + catch (SocketException ex) + { + ProcessError(ex); + } + catch (ObjectDisposedException) + { + + } + catch (Exception e) + { + //protects socket receive thread + NetDebug.WriteError("[NM] SocketReceiveThread error: " + e ); + } + } + + private void NativeReceiveLogic() + { + IntPtr socketHandle4 = _udpSocketv4.Handle; + IntPtr socketHandle6 = _udpSocketv6?.Handle ?? IntPtr.Zero; + byte[] addrBuffer4 = new byte[NativeSocket.IPv4AddrSize]; + byte[] addrBuffer6 = new byte[NativeSocket.IPv6AddrSize]; + var tempEndPoint = new IPEndPoint(IPAddress.Any, 0); + var selectReadList = new List(2); + var socketv4 = _udpSocketv4; + var socketV6 = _udpSocketv6; + var packet = PoolGetPacket(NetConstants.MaxPacketSize); + + while (IsRunning) + { + try + { + if (socketV6 == null) + { + if (NativeReceiveFrom(socketHandle4, addrBuffer4) == false) + return; + continue; + } + bool messageReceived = false; + if (socketv4.Available != 0 || selectReadList.Contains(socketv4)) + { + if (NativeReceiveFrom(socketHandle4, addrBuffer4) == false) + return; + messageReceived = true; + } + if (socketV6.Available != 0 || selectReadList.Contains(socketV6)) + { + if (NativeReceiveFrom(socketHandle6, addrBuffer6) == false) + return; + messageReceived = true; + } + + selectReadList.Clear(); + + if (messageReceived) + continue; + + selectReadList.Add(socketv4); + selectReadList.Add(socketV6); + + Socket.Select(selectReadList, null, null, ReceivePollingTime); + } + 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 ); + } + } + + bool NativeReceiveFrom(IntPtr s, byte[] address) + { + int addrSize = address.Length; + packet.Size = NativeSocket.RecvFrom(s, packet.RawData, NetConstants.MaxPacketSize, address, ref addrSize); + if (packet.Size == 0) + return true; //socket closed or empty packet + + if (packet.Size == -1) + { + //Linux timeout EAGAIN + return ProcessError(new((int)NativeSocket.GetSocketError())) == false; + } + + //NetDebug.WriteForce($"[R]Received data from {endPoint}, result: {packet.Size}"); + //refresh temp Addr/Port + short family = (short)((address[1] << 8) | address[0]); + tempEndPoint.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 + tempEndPoint.Address = new IPAddress(new ReadOnlySpan(address, 8, 16), scope); +#else + byte[] addrBuffer = new byte[16]; + Buffer.BlockCopy(address, 8, addrBuffer, 0, 16); + tempEndPoint.Address = new(addrBuffer, scope); +#endif + } + else //IPv4 + { + long ipv4Addr = unchecked((uint)((address[4] & 0x000000FF) | + (address[5] << 8 & 0x0000FF00) | + (address[6] << 16 & 0x00FF0000) | + (address[7] << 24))); + tempEndPoint.Address = new(ipv4Addr); + } + + if (TryGetPeer(tempEndPoint, out var peer)) + { + //use cached native ep + OnMessageReceived(packet, peer); + } + else + { + OnMessageReceived(packet, tempEndPoint); + tempEndPoint = new(IPAddress.Any, 0); + } + packet = PoolGetPacket(NetConstants.MaxPacketSize); + return true; + } + } + + private void ReceiveFrom(Socket s, ref EndPoint bufferEndPoint) + { + var packet = PoolGetPacket(NetConstants.MaxPacketSize); +#if NET8_0_OR_GREATER + var sockAddr = s.AddressFamily == AddressFamily.InterNetwork ? _sockAddrCacheV4 : _sockAddrCacheV6; + packet.Size = s.ReceiveFrom(packet, SocketFlags.None, sockAddr); + OnMessageReceived(packet, TryGetPeer(sockAddr, out var peer) ? peer : (IPEndPoint)bufferEndPoint.Create(sockAddr)); +#else + packet.Size = s.ReceiveFrom(packet.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint); + OnMessageReceived(packet, (IPEndPoint)bufferEndPoint); +#endif + } + + private void ReceiveLogic() + { + EndPoint bufferEndPoint4 = new IPEndPoint(IPAddress.Any, 0); + EndPoint bufferEndPoint6 = new IPEndPoint(IPAddress.IPv6Any, 0); + var selectReadList = new List(2); + var socketv4 = _udpSocketv4; + var socketV6 = _udpSocketv6; + + while (IsRunning) + { + //Reading data + try + { + if (socketV6 == null) + { + if (socketv4.Available == 0 && !socketv4.Poll(ReceivePollingTime, SelectMode.SelectRead)) + continue; + ReceiveFrom(socketv4, ref bufferEndPoint4); + } + else + { + bool messageReceived = false; + if (socketv4.Available != 0 || selectReadList.Contains(socketv4)) + { + ReceiveFrom(socketv4, ref bufferEndPoint4); + messageReceived = true; + } + if (socketV6.Available != 0 || selectReadList.Contains(socketV6)) + { + ReceiveFrom(socketV6, ref bufferEndPoint6); + messageReceived = true; + } + + selectReadList.Clear(); + + if (messageReceived) + continue; + + selectReadList.Add(socketv4); + selectReadList.Add(socketV6); + Socket.Select(selectReadList, null, null, ReceivePollingTime); + } + //NetDebug.Write(NetLogLevel.Trace, $"[R]Received data from {bufferEndPoint}, result: {packet.Size}"); + } + 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 && NotConnected == false) + return false; + + NotConnected = false; + _manualMode = manualMode; + UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported; + _udpSocketv4 = new(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + if (!BindSocket(_udpSocketv4, new(addressIPv4, port))) + return false; + + LocalPort = ((IPEndPoint) _udpSocketv4.LocalEndPoint).Port; + +#if UNITY_SOCKET_FIX + if (_useSocketFix && _pausedSocketFix == null) + _pausedSocketFix = new(this, addressIPv4, addressIPv6, port, manualMode); +#endif + + IsRunning = true; + if (_manualMode) + { + _bufferEndPointv4 = new(IPAddress.Any, 0); + } + + //Check IPv6 support + if (IPv6Support && IPv6Enabled) + { + _udpSocketv6 = new(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); + //Use one port for two sockets + if (BindSocket(_udpSocketv6, new(addressIPv6, LocalPort))) + { + if (_manualMode) + _bufferEndPointv6 = new(IPAddress.IPv6Any, 0); + } + else + { + _udpSocketv6 = null; + } + } + + if (!manualMode) + { + ThreadStart ts = ReceiveLogic; + if (UseNativeSockets) + ts = NativeReceiveLogic; + _receiveThread = new(ts) + { + Name = $"ReceiveThread({LocalPort})", + IsBackground = true + }; + _receiveThread.Start(); + if (_logicThread == null) + { + _logicThread = new(UpdateLogic) { Name = "LogicThread", IsBackground = true }; + _logicThread.Start(); + } + } + + 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; + socket.Blocking = true; + + 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); + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, DontRoute); + } + catch + { + //Unity with IL2CPP throws an exception here, it doesn't matter in most cases so just ignore it + } + if (ep.AddressFamily == AddressFamily.InterNetwork) + { + Ttl = NetConstants.SocketTTL; + + try { socket.EnableBroadcast = true; } + catch (SocketException e) + { + NetDebug.WriteError($"[B]Broadcast error: {e.SocketErrorCode}"); + } + + 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_SOCKET_FIX + 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) + { + 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 && remoteEndPoint is NetPeer peer) + { + unsafe + { + fixed (byte* dataWithOffset = &message[start]) + result = NativeSocket.SendTo(socket.Handle, dataWithOffset, length, peer.NativeAddress, peer.NativeAddress.Length); + } + if (result == -1) + throw NativeSocket.GetSocketException(); + } + else + { +#if NET8_0_OR_GREATER + result = socket.SendTo(new ReadOnlySpan(message, start, length), SocketFlags.None, remoteEndPoint.Serialize()); +#else + result = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint); +#endif + } + //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: {length}"); + return 0; + + case SocketError.HostUnreachable: + case SocketError.NetworkUnreachable: + if (DisconnectOnUnreachable && remoteEndPoint is NetPeer peer) + { + DisconnectPeerForce( + peer, + ex.SocketErrorCode == SocketError.HostUnreachable + ? DisconnectReason.HostUnreachable + : DisconnectReason.NetworkUnreachable, + ex.SocketErrorCode, + null); + } + + CreateEvent(NetEvent.EType.Error, remoteEndPoint: remoteEndPoint, errorCode: ex.SocketErrorCode); + return -1; + + case SocketError.Shutdown: + 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 (!IsRunning) + 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 (SocketException ex) + { + if (ex.SocketErrorCode == SocketError.HostUnreachable) + return broadcastSuccess; + NetDebug.WriteError($"[S][MCAST] {ex}"); + return broadcastSuccess; + } + catch (Exception ex) + { + NetDebug.WriteError($"[S][MCAST] {ex}"); + return broadcastSuccess; + } + finally + { + PoolRecycle(packet); + } + + return broadcastSuccess || multicastSuccess; + } + + private void CloseSocket() + { + IsRunning = false; + _udpSocketv4?.Close(); + _udpSocketv6?.Close(); + _udpSocketv4 = null; + _udpSocketv6 = null; + if (_receiveThread != null && _receiveThread != Thread.CurrentThread) + _receiveThread.Join(); + _receiveThread = 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..a433775 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 9cc0423407df3e74a8e5a15dbbad2598 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.Socket.cs + uploadId: 762203 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..cf765e7 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs @@ -0,0 +1,1820 @@ +#if UNITY_2018_3_OR_NEWER +#define UNITY_SOCKET_FIX +#endif +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 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 RecycleInternal() + { + Clear(); + if (_packet != null) + _manager.PoolRecycle(_packet); + _packet = null; + _manager.RecycleEvent(_evt); + } + + public void Recycle() + { + if (_manager.AutoRecycle) + return; + RecycleInternal(); + } + } + + 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(manager, this); + } + } + + /// + /// Main class for all network operations. Can be used as client and/or server. + /// + public partial class NetManager : IEnumerable + { + 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(); + private readonly Random _randomGenerator = new(); + private const int MinLatencyThreshold = 5; +#endif + + private Thread _logicThread; + private bool _manualMode; + private readonly AutoResetEvent _updateTriggerEvent = new(true); + + private NetEvent _pendingEventHead; + private NetEvent _pendingEventTail; + + private NetEvent _netEventPoolHead; + private readonly INetEventListener _netEventListener; + private readonly IDeliveryEventListener _deliveryEventListener; + private readonly INtpEventListener _ntpEventListener; + private readonly IPeerAddressChangedListener _peerAddressChangedListener; + + private readonly Dictionary _requestsDict = new(); + private readonly ConcurrentDictionary _ntpRequests = new(); + private long _connectedPeersCount; + private readonly List _connectedPeerListCache = new(); + private readonly PacketLayerBase _extraPacketLayer; + private int _nextPeerId; + private ConcurrentQueue _peerIds = new(); + private byte _channelsCount = 1; + private readonly object _eventLock = new(); + + //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; + + /// + /// UDP Only Socket Option + /// Normally IP sockets send packets of data through routers and gateways until they reach the final destination. + /// If the DontRoute flag is set to True, then data will be delivered on the local subnet only. + /// + public bool DontRoute = false; + + /// + /// Statistics of all connections + /// + public readonly NetStatistics Statistics = new(); + + /// + /// 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 bool IPv6Enabled = true; + + /// + /// 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; + } + } + + /// + /// Returns connected peers count + /// + public int ConnectedPeersCount => (int)Interlocked.Read(ref _connectedPeersCount); + + public int ExtraPacketSizeForLayer => _extraPacketLayer?.ExtraPacketSizeForLayer ?? 0; + + public NetManager(bool useSocketFix = true) + { + _useSocketFix = useSocketFix; + } + /// + /// 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. +#if UNITY_SOCKET_FIX + public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null, bool useSocketFix = true) + { + _useSocketFix = useSocketFix; +#else + public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null) + { +#endif + _netEventListener = listener; + _deliveryEventListener = listener as IDeliveryEventListener; + _ntpEventListener = listener as INtpEventListener; + _peerAddressChangedListener = listener as IPeerAddressChangedListener; + NatPunchModule = new(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(this); + else + _netEventPoolHead = evt.Next; + } + + evt.Next = null; + 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 (_eventLock) + { + if (_pendingEventTail == null) + _pendingEventHead = evt; + else + _pendingEventTail.Next = evt; + _pendingEventTail = 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); + RemovePeer(evt.Peer); + 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); + RemovePeer(evt.Peer); + 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 (ContainsPeer(evt.Peer)) + { + _peersLock.EnterWriteLock(); + RemovePeerFromSet(evt.Peer); + previousAddress = new(evt.Peer.Address, evt.Peer.Port); + evt.Peer.FinishEndPointChange(evt.RemoteEndPoint); + AddPeerToSet(evt.Peer); + _peersLock.ExitWriteLock(); + } + _peersLock.ExitUpgradeableReadLock(); + if (previousAddress != null && _peerAddressChangedListener != null) + _peerAddressChangedListener.OnPeerAddressChanged(evt.Peer, previousAddress); + break; + } + //Recycle if not message + if (emptyData) + RecycleEvent(evt); + else if (AutoRecycle) + evt.DataReader.RecycleInternal(); + } + + 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++) + RemovePeerInternal(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(); + requestsToRemove.Add(ntpRequest.Key); + } + } + + if (requestsToRemove != null) + { + foreach (var ipEndPoint in requestsToRemove) + { + _ntpRequests.TryRemove(ipEndPoint, out _); + } + } + } + + /// + /// 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) + { + RemovePeerInternal(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); + } + lock (_requestsDict) + _requestsDict.Remove(request.RemoteEndPoint); + } + else lock (_requestsDict) + { + if (TryGetPeer(request.RemoteEndPoint, out netPeer)) + { + //already have peer + } + else if (request.Result == ConnectionRequestResult.Reject) + { + netPeer = new(this, request.RemoteEndPoint, GetNextPeerId()); + netPeer.Reject(request.InternalPacket, rejectData, start, length); + AddPeer(netPeer); + NetDebug.Write(NetLogLevel.Trace, "[NM] Peer connect reject."); + } + else //Accept + { + netPeer = new(this, request, GetNextPeerId()); + AddPeer(netPeer); + CreateEvent(NetEvent.EType.Connect, netPeer); + NetDebug.Write(NetLogLevel.Trace, $"[NM] Received peer connection Id: {netPeer.ConnectTime}, EP: {netPeer}"); + } + _requestsDict.Remove(request.RemoteEndPoint); + } + + return netPeer; + } + + private int GetNextPeerId() + { + /* Build a small queue so that when fetching + * an id its unlikely to be one that was just returned. This adds + * a buffer to applications which may be using the Id to identity + * clients in their code, but may need time to dispose of objects + * associated with the Id. */ + const int addCount = 5000; + const int halfCount = (addCount / 2); + if (_peerIds.Count < halfCount) + { + //At most how many can be added. + const int maxIdValue = int.MaxValue; + long addable = Math.Min(addCount, (maxIdValue - _nextPeerId)); + + if (addable == 0) + { + NetDebug.Write(NetLogLevel.Error, $"[NM] PeerIds have exceeded the maximum value of {maxIdValue}. A new Id cannot be returned."); + return 0; + } + + for (int i = 0; i < addCount; i++) + _peerIds.Enqueue(_nextPeerId++); + } + + return (_peerIds.TryDequeue(out int result)) ? result : _nextPeerId++; + } + + 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: {netPeer.ConnectTime}, NewId: {connRequest.ConnectionTime}, EP: {remoteEndPoint}, Result: {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: {connRequest.ConnectionTime}, EP: {remoteEndPoint}"); + } + + ConnectionRequest req; + lock (_requestsDict) + { + if (_requestsDict.TryGetValue(remoteEndPoint, out req)) + { + req.UpdateRequest(connRequest); + return; + } + req = new(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 (packet.Size == 0) + { + PoolRecycle(packet); + return; + } +#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() + { + Data = packet, + EndPoint = remoteEndPoint, + TimeWhenGet = DateTime.UtcNow.AddMilliseconds(latency) + }); + } + //hold packet + return; + } + } + + //ProcessEvents + DebugMessageReceived(packet, remoteEndPoint); + } + + private void DebugMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint) + { +#endif + var originalPacketSize = packet.Size; + if (EnableStatistics) + { + Statistics.IncrementPacketsReceived(); + Statistics.AddBytesReceived(originalPacketSize); + } + + if (_ntpRequests.Count > 0 && _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.TryRemove(remoteEndPoint, out _); + _ntpEventListener?.OnNtpResponse(ntpPacket); + } + return; + } + + if (_extraPacketLayer != null) + { + _extraPacketLayer.ProcessInboundPacket(ref remoteEndPoint, ref packet.RawData, 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 + bool peerFound = remoteEndPoint is NetPeer netPeer || TryGetPeer(remoteEndPoint, out netPeer); + + if (peerFound && EnableStatistics) + { + netPeer.Statistics.IncrementPacketsReceived(); + netPeer.Statistics.AddBytesReceived(originalPacketSize); + } + + 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]; + _peersLock.ExitUpgradeableReadLock(); + if (peer != null && + peer.ConnectTime == remoteData.ConnectionTime && + peer.ConnectionNum == remoteData.ConnectionNumber) + { + if (peer.ConnectionState == ConnectionState.Connected) + { + peer.InitiateEndPointChange(); + CreateEvent(NetEvent.EType.PeerAddressChanged, peer, remoteEndPoint); + NetDebug.Write("[NM] PeerNotFound change address of remote peer"); + } + isOldPeer = true; + } + } + } + + 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; + + if (UnsyncedEvents || UnsyncedReceiveEvent || _manualMode) + { + lock (_eventLock) + { + evt = _netEventPoolHead; + if (evt == null) + evt = new(this); + else + _netEventPoolHead = evt.Next; + } + evt.Next = null; + evt.Type = NetEvent.EType.Receive; + evt.DataReader.SetSource(packet, headerSize); + evt.Peer = fromPeer; + evt.DeliveryMethod = method; + evt.ChannelNumber = channelNumber; + ProcessEvent(evt); + } + else + { + lock (_eventLock) + { + evt = _netEventPoolHead; + if (evt == null) + evt = new(this); + else + _netEventPoolHead = evt.Next; + + evt.Next = null; + evt.Type = NetEvent.EType.Receive; + evt.DataReader.SetSource(packet, headerSize); + evt.Peer = fromPeer; + evt.DeliveryMethod = method; + evt.ChannelNumber = channelNumber; + + if (_pendingEventTail == null) + _pendingEventHead = evt; + else + _pendingEventTail.Next = evt; + _pendingEventTail = 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(); + } + } + +#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1 + /// + /// Send data to all connected peers (channel - 0) + /// + /// Data + /// Send options (reliable, unreliable, etc.) + public void SendToAll(ReadOnlySpan data, DeliveryMethod options) + { + SendToAll(data, 0, options, null); + } + + /// + /// Send data to all connected peers (channel - 0) + /// + /// Data + /// Send options (reliable, unreliable, etc.) + /// Excluded peer + public void SendToAll(ReadOnlySpan data, DeliveryMethod options, NetPeer excludePeer) + { + SendToAll(data, 0, 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(ReadOnlySpan data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer) + { + try + { + _peersLock.EnterReadLock(); + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + { + if (netPeer != excludePeer) + netPeer.Send(data, channelNumber, options); + } + } + finally + { + _peersLock.ExitReadLock(); + } + } + + /// + /// Send message without connection + /// + /// Raw data + /// Packet destination + /// Operation result + public bool SendUnconnectedMessage(ReadOnlySpan message, IPEndPoint remoteEndPoint) + { + int headerSize = NetPacket.GetHeaderSize(PacketProperty.UnconnectedMessage); + var packet = PoolGetPacket(message.Length + headerSize); + packet.Property = PacketProperty.UnconnectedMessage; + message.CopyTo(new Span(packet.RawData, headerSize, message.Length)); + return SendRawAndRecycle(packet, remoteEndPoint) > 0; + } +#endif + + /// + /// 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 "maxProcessedEvents" pending events. Call this in game update code + /// In Manual mode it will call also socket Receive (which can be slow) + /// 0 - receive all events + /// + /// Max events that will be processed (called INetEventListener Connect/Receive/Etc), 0 - receive all events + public void PollEvents(int maxProcessedEvents = 0) + { + if (_manualMode) + { + if (_udpSocketv4 != null) + ManualReceive(_udpSocketv4, _bufferEndPointv4, maxProcessedEvents); + if (_udpSocketv6 != null && _udpSocketv6 != _udpSocketv4) + ManualReceive(_udpSocketv6, _bufferEndPointv6, maxProcessedEvents); + ProcessDelayedPackets(); + return; + } + if (UnsyncedEvents) + return; + NetEvent pendingEvent; + lock (_eventLock) + { + pendingEvent = _pendingEventHead; + _pendingEventHead = null; + _pendingEventTail = null; + } + + int counter = 0; + while (pendingEvent != null) + { + var next = pendingEvent.Next; + ProcessEvent(pendingEvent); + pendingEvent = next; + counter++; + if (counter == maxProcessedEvents) + break; + } + } + + /// + /// 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)); + } + + /// + /// 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; + if (TryGetPeer(target, out var peer)) + { + switch (peer.ConnectionState) + { + //just return already connected peer + case ConnectionState.Connected: + case ConnectionState.Outgoing: + return peer; + } + //else reconnect + connectionNumber = (byte)((peer.ConnectionNum + 1) % NetConstants.MaxConnectionNumber); + RemovePeer(peer); + } + + //Create reliable connection + //And send connection request + peer = new(this, target, GetNextPeerId(), connectionNumber, connectionData); + AddPeer(peer); + 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"); + +#if UNITY_SOCKET_FIX + if (_useSocketFix) + { + _pausedSocketFix.Deinitialize(); + _pausedSocketFix = null; + } +#endif + + //Send last disconnect + for (var netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer) + netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages); + + //Stop + CloseSocket(); + _updateTriggerEvent.Set(); + if (!_manualMode) + { + _logicThread.Join(); + _logicThread = null; + } + + //clear peers + ClearPeerSet(); + _peerIds = new(); + _nextPeerId = 0; +#if DEBUG + lock (_pingSimulationList) + _pingSimulationList.Clear(); +#endif + _connectedPeersCount = 0; + _pendingEventHead = null; + _pendingEventTail = null; + } + + /// + /// 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.TryAdd(endPoint, new(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.TryAdd(endPoint, new(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.TryAdd(endPoint, new(endPoint)); + } + + public NetPeerEnumerator GetEnumerator() + { + return new(_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..d3aa3b2 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 37d95580df7122c44b9333cd3ab77732 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetManager.cs + uploadId: 762203 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..c1505d6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs @@ -0,0 +1,164 @@ +using System; +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); + } + + #if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1 + public static implicit operator Span(NetPacket p) => new Span(p.RawData, 0, p.Size); + #endif + } +} 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..6e41bcc --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: e7ddd322169be074f870f73db1a55255 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPacket.cs + uploadId: 762203 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..e71feec --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs @@ -0,0 +1,1440 @@ +#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 : IPEndPoint + { + //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(); + private int _timeSinceLastPacket; + private long _remoteDelta; + + //Common + private readonly object _shutdownLock = new(); + + 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(); + + //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 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 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.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; + + private SocketAddress _cachedSocketAddr; + private int _cachedHashCode; + + internal byte[] NativeAddress; + + /// + /// IPEndPoint serialize + /// + /// SocketAddress + public override SocketAddress Serialize() + { + return _cachedSocketAddr; + } + + public override int GetHashCode() + { + //uses SocketAddress hash in NET8 and IPEndPoint hash for NativeSockets and previous NET versions + return _cachedHashCode; + } + + //incoming connection constructor + internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id) : base(remoteEndPoint.Address, remoteEndPoint.Port) + { + Id = id; + Statistics = new(); + NetManager = netManager; + + _cachedSocketAddr = base.Serialize(); + if (NetManager.UseNativeSockets) + { + NativeAddress = new byte[_cachedSocketAddr.Size]; + for (int i = 0; i < _cachedSocketAddr.Size; i++) + NativeAddress[i] = _cachedSocketAddr[i]; + } +#if NET8_0_OR_GREATER + _cachedHashCode = NetManager.UseNativeSockets ? base.GetHashCode() : _cachedSocketAddr.GetHashCode(); +#else + _cachedHashCode = base.GetHashCode(); +#endif + + ResetMtu(); + + _connectionState = ConnectionState.Connected; + _mergeData = new(PacketProperty.Merged, NetConstants.MaxPacketSize); + _pongPacket = new(PacketProperty.Pong, 0); + _pingPacket = new(PacketProperty.Ping, 0) {Sequence = 1}; + + _unreliableChannel = new(); + _holdedFragments = new(); + _deliveredFragments = new(); + + _channels = new BaseChannel[netManager.ChannelsCount * NetConstants.ChannelTypeCount]; + _channelSendQueue = new(); + } + + internal void InitiateEndPointChange() + { + ResetMtu(); + _connectionState = ConnectionState.EndPointChange; + } + + internal void FinishEndPointChange(IPEndPoint newEndPoint) + { + if (_connectionState != ConnectionState.EndPointChange) + return; + _connectionState = ConnectionState.Connected; + + Address = newEndPoint.Address; + Port = newEndPoint.Port; + + if (NetManager.UseNativeSockets) + { + NativeAddress = new byte[_cachedSocketAddr.Size]; + for (int i = 0; i < _cachedSocketAddr.Size; i++) + NativeAddress[i] = _cachedSocketAddr[i]; + } + _cachedSocketAddr = base.Serialize(); +#if NET8_0_OR_GREATER + _cachedHashCode = NetManager.UseNativeSockets ? base.GetHashCode() : _cachedSocketAddr.GetHashCode(); +#else + _cachedHashCode = base.GetHashCode(); +#endif + } + + 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(packet, mtu, 0); + } + else + { + packet.Property = PacketProperty.Channeled; + return new(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, this); + + 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, this); + + 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"); + SendInternal(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"); + SendInternal(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"); + SendInternal(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) + { + SendInternal(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) + { + SendInternal(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) + { + SendInternal(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) + { + SendInternal(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) + { + SendInternal(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) + { + SendInternal(data, start, length, channelNumber, deliveryMethod, null); + } + + private void SendInternal( + 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: + MTU: {mtu} + headerSize: {headerSize} + packetFullSize: {packetFullSize} + packetDataSize: {packetDataSize} + totalPackets: {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"); + SendInternal(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) + { + SendInternal(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) + { + SendInternal(data, channelNumber, deliveryMethod, null); + } + + private void SendInternal( + 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(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, this); + 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: {p.FragmentId}, Part: {p.FragmentPart}, Total: {p.FragmentsTotal}"); + //Get needed array from dictionary + ushort packetFragId = p.FragmentId; + byte packetChannelId = p.ChannelId; + if (!_holdedFragments.TryGetValue(packetFragId, out var incomingFragments)) + { + incomingFragments = new() + { + 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: {pos + writtenSize} >= resultPacketSize: {resultingPacket.RawData.Length} , totalSize: {incomingFragments.TotalSize}"); + return; + } + if (fragment.Size > fragment.RawData.Length) + { + _holdedFragments.Remove(packetFragId); + NetDebug.WriteError($"Fragment error size: {fragment.Size} > fragment.RawData.Length: {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 {receivedMtu}, EMTU {endMtuCheck}, PSIZE {packet.Size}"); + return; + } + + if (packet.Property == PacketProperty.MtuCheck) + { + _mtuCheckAttempts = 0; + NetDebug.Write("[MTU] check. send back: " + receivedMtu); + packet.Property = PacketProperty.MtuOk; + NetManager.SendRawAndRecycle(packet, this); + } + 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, this) <= 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 localBytes = connRequest.TargetAddress; + for (int i = _cachedSocketAddr.Size-1; i >= 0; i--) + { + byte rb = _cachedSocketAddr[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, this); + } + //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: {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, this); + } + 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: {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, this); + } + else + { + //Send without length information and merging + bytesSent = NetManager.SendRaw(_mergeData.RawData, NetConstants.HeaderSize + 2, _mergePos - 2, this); + } + + 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, this); + + 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: {_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, this); + } + } + 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, this); + } + 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, this); + } + + //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..48d5b3d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3e1a9277334c51545b92369c8e4c6d74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetPeer.cs + uploadId: 762203 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..c25b790 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 15d4b62077bda58428b77d57fa4a9288 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetStatistics.cs + uploadId: 762203 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..e784bb8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +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 + { + private static readonly NetworkSorter NetworkSorter = new(); + + public static IPEndPoint MakeEndPoint(string hostStr, int port) + { + return new(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(); + 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 + { + // Sort networks interfaces so it prefer Wifi over Cellular networks + // Most cellulars networks seems to be incompatible with NAT Punch + var networks = NetworkInterface.GetAllNetworkInterfaces(); + Array.Sort(networks, NetworkSorter); + + foreach (NetworkInterface ni in networks) + { + //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(); + /// + /// 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: { 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: {ni.Name}, Type: {ni.NetworkInterfaceType}, Ip: {ip.Address}, OpStatus: {ni.OperationalStatus}"); + } + } + } + } + catch (Exception e) + { + NetDebug.WriteForce(NetLogLevel.Info, $"Error while getting interface infos: {e}"); + } + } + + 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 + } + } + + // Pick the most obvious choice for the local IP + // Ethernet > Wifi > Others > Cellular + internal class NetworkSorter : IComparer + { + [SuppressMessage("ReSharper", "PossibleNullReferenceException")] + public int Compare(NetworkInterface a, NetworkInterface b) + { + var isCellularA = a.NetworkInterfaceType == NetworkInterfaceType.Wman || + a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || + a.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2; + + var isCellularB = b.NetworkInterfaceType == NetworkInterfaceType.Wman || + b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp || + b.NetworkInterfaceType == NetworkInterfaceType.Wwanpp2; + + var isWifiA = a.NetworkInterfaceType == NetworkInterfaceType.Wireless80211; + var isWifiB = b.NetworkInterfaceType == NetworkInterfaceType.Wireless80211; + + var isEthernetA = a.NetworkInterfaceType == NetworkInterfaceType.Ethernet || + a.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || + a.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || + a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || + a.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT; + + var isEthernetB = b.NetworkInterfaceType == NetworkInterfaceType.Ethernet || + b.NetworkInterfaceType == NetworkInterfaceType.Ethernet3Megabit || + b.NetworkInterfaceType == NetworkInterfaceType.GigabitEthernet || + b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetFx || + b.NetworkInterfaceType == NetworkInterfaceType.FastEthernetT; + + var isOtherA = !isCellularA && !isWifiA && !isEthernetA; + var isOtherB = !isCellularB && !isWifiB && !isEthernetB; + + var priorityA = isEthernetA ? 3 : isWifiA ? 2 : isOtherA ? 1 : 0; + var priorityB = isEthernetB ? 3 : isWifiB ? 2 : isOtherB ? 1 : 0; + + return priorityA > priorityB ? -1 : priorityA < priorityB ? 1 : 0; + } + } +} 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..55b8115 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: ab6438405a73f8c46ac22cd2f259a0e2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/NetUtils.cs + uploadId: 762203 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..dc92122 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs @@ -0,0 +1,57 @@ +#if UNITY_2018_3_OR_NEWER +using System.Net; + +namespace LiteNetLib +{ + public class PausedSocketFix + { + private readonly NetManager _netManager; + private readonly IPAddress _ipv4; + private readonly IPAddress _ipv6; + private readonly int _port; + private readonly bool _manualMode; + private bool _initialized; + + public PausedSocketFix(NetManager netManager, IPAddress ipv4, IPAddress ipv6, int port, bool manualMode) + { + _netManager = netManager; + _ipv4 = ipv4; + _ipv6 = ipv6; + _port = port; + _manualMode = manualMode; + UnityEngine.Application.focusChanged += Application_focusChanged; + _initialized = true; + } + + public void Deinitialize() + { + if (_initialized) + UnityEngine.Application.focusChanged -= Application_focusChanged; + _initialized = false; + } + + private void Application_focusChanged(bool focused) + { + //If coming back into focus see if a reconnect is needed. + if (focused) + { + //try reconnect + if (!_initialized) + return; + //Was intentionally disconnected at some point. + if (!_netManager.IsRunning) + return; + //Socket is in working state. + if (_netManager.NotConnected == false) + 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}"); + } + } + } + } +} +#endif 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..6cf34cf --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 85ea50adb87db074c9a1c587f35aebb2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PausedSocketFix.cs + uploadId: 762203 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..0a0c993 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 76e47a542bb12e043b874c7180d98319 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/PooledPacket.cs + uploadId: 762203 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..88eac2d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs @@ -0,0 +1,337 @@ +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: {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(); + + 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(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: {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: {pendingSeq} - true"); + } + } + } + + 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 + lock (OutgoingQueue) + { + while (OutgoingQueue.Count > 0) + { + int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); + if (relate >= _windowSize) + break; + + var netPacket = OutgoingQueue.Dequeue(); + 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.Count > 0; + } + + //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..0e8f03a --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f7e20c169333af54fa233b0d70b19a61 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/ReliableChannel.cs + uploadId: 762203 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..e7e0e3c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs @@ -0,0 +1,114 @@ +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(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 + { + lock (OutgoingQueue) + { + while (OutgoingQueue.Count > 0) + { + NetPacket packet = OutgoingQueue.Dequeue(); + _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..5e1779d --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c4e1f97908b70e642baea0fc22724282 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/SequencedChannel.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs new file mode 100644 index 0000000..6c575b6 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs @@ -0,0 +1,12 @@ +#if NET5_0_OR_GREATER +using System.Diagnostics.CodeAnalysis; +using static System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes; + +namespace LiteNetLib +{ + internal static class Trimming + { + internal const DynamicallyAccessedMemberTypes SerializerMemberTypes = PublicProperties | NonPublicProperties; + } +} +#endif diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs.meta new file mode 100644 index 0000000..77122f2 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: af5a9e06ff14c6c42ab091c2c19e2028 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Trimming.cs + uploadId: 762203 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..6d37c54 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4ebeb715b0d0f844bad181703daa09db +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..444d50c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 912ba506b0945b743be5c4129177024c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/CRC32C.cs + uploadId: 762203 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..77c9780 --- /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 || NETCOREAPP3_1 || NET5_0 || NETCOREAPP3_0_OR_GREATER) && !BIGENDIAN +#if LITENETLIB_UNSAFE + + 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 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 + + 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; + } + + + 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 + } + + + 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 + } + + + 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 + } + + + public static void GetBytes(byte[] bytes, int startIndex, double value) + { + ConverterHelperDouble ch = new() { Adouble = value }; + WriteLittleEndian(bytes, startIndex, ch.Along); + } + + + public static void GetBytes(byte[] bytes, int startIndex, float value) + { + ConverterHelperFloat ch = new() { Afloat = value }; + WriteLittleEndian(bytes, startIndex, ch.Aint); + } + + + public static void GetBytes(byte[] bytes, int startIndex, short value) + { + WriteLittleEndian(bytes, startIndex, value); + } + + + public static void GetBytes(byte[] bytes, int startIndex, ushort value) + { + WriteLittleEndian(bytes, startIndex, (short)value); + } + + + public static void GetBytes(byte[] bytes, int startIndex, int value) + { + WriteLittleEndian(bytes, startIndex, value); + } + + + public static void GetBytes(byte[] bytes, int startIndex, uint value) + { + WriteLittleEndian(bytes, startIndex, (int)value); + } + + + public static void GetBytes(byte[] bytes, int startIndex, long value) + { + WriteLittleEndian(bytes, startIndex, (ulong)value); + } + + + 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..88a9709 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 21018f84ccfb8244e99b5ec22ceb91c9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/FastBitConverter.cs + uploadId: 762203 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..db54ba0 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c26b17a74d3fb8b498fc89d253f1742a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/INetSerializable.cs + uploadId: 762203 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..f2776e8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs @@ -0,0 +1,783 @@ +using System; +using System.Net; +using System.Runtime.CompilerServices; + +namespace LiteNetLib.Utils +{ + public class NetDataReader + { + protected byte[] _data; + protected int _position; + protected int _dataSize; + private int _offset; + + public byte[] RawData + { + + get => _data; + } + public int RawDataSize + { + + get => _dataSize; + } + public int UserDataOffset + { + + get => _offset; + } + public int UserDataSize + { + + get => _dataSize - _offset; + } + public bool IsNull + { + + get => _data == null; + } + public int Position + { + + get => _position; + } + public bool EndOfData + { + + get => _position == _dataSize; + } + public int AvailableBytes + { + + get => _dataSize - _position; + } + + 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 void Get(out T result) where T : struct, INetSerializable + { + result = default(T); + result.Deserialize(this); + } + + public void Get(out T result, Func constructor) where T : class, INetSerializable + { + result = constructor(); + result.Deserialize(this); + } + + public void Get(out IPEndPoint result) + { + result = GetNetEndPoint(); + } + + public void Get(out byte result) + { + result = GetByte(); + } + + public void Get(out sbyte result) + { + result = (sbyte)GetByte(); + } + + public void Get(out bool result) + { + result = GetBool(); + } + + public void Get(out char result) + { + result = GetChar(); + } + + public void Get(out ushort result) + { + result = GetUShort(); + } + + public void Get(out short result) + { + result = GetShort(); + } + + public void Get(out ulong result) + { + result = GetULong(); + } + + public void Get(out long result) + { + result = GetLong(); + } + + public void Get(out uint result) + { + result = GetUInt(); + } + + public void Get(out int result) + { + result = GetInt(); + } + + public void Get(out double result) + { + result = GetDouble(); + } + + public void Get(out float result) + { + result = GetFloat(); + } + + public void Get(out string result) + { + result = GetString(); + } + + public void Get(out string result, int maxLength) + { + result = GetString(maxLength); + } + + public IPEndPoint GetNetEndPoint() + { + string host = GetString(1000); + int port = GetInt(); + return NetUtils.MakeEndPoint(host, port); + } + + public byte GetByte() + { + byte res = _data[_position]; + _position++; + return res; + } + + public sbyte GetSByte() + { + return (sbyte)GetByte(); + } + + public T[] GetArray(ushort size) + { + ushort length = BitConverter.ToUInt16(_data, _position); + _position += 2; + T[] result = new T[length]; + length *= size; + Buffer.BlockCopy(_data, _position, result, 0, length); + _position += length; + return result; + } + + public T[] GetArray() where T : INetSerializable, new() + { + ushort length = BitConverter.ToUInt16(_data, _position); + _position += 2; + T[] result = new T[length]; + for (int i = 0; i < length; i++) + { + var item = new T(); + item.Deserialize(this); + result[i] = item; + } + return result; + } + + public bool[] GetBoolArray() + { + return GetArray(1); + } + + public ushort[] GetUShortArray() + { + return GetArray(2); + } + + public short[] GetShortArray() + { + return GetArray(2); + } + + public int[] GetIntArray() + { + return GetArray(4); + } + + public uint[] GetUIntArray() + { + return GetArray(4); + } + + public float[] GetFloatArray() + { + return GetArray(4); + } + + public double[] GetDoubleArray() + { + return GetArray(8); + } + + public long[] GetLongArray() + { + return GetArray(8); + } + + public ulong[] GetULongArray() + { + return GetArray(8); + } + + public string[] GetStringArray() + { + ushort length = GetUShort(); + string[] arr = new string[length]; + for (int i = 0; i < length; i++) + { + arr[i] = GetString(); + } + return arr; + } + + /// + /// Note that "maxStringLength" only limits the number of characters in a string, not its size in bytes. + /// Strings that exceed this parameter are returned as empty + /// + public string[] GetStringArray(int maxStringLength) + { + ushort length = GetUShort(); + string[] arr = new string[length]; + for (int i = 0; i < length; i++) + { + arr[i] = GetString(maxStringLength); + } + return arr; + } + + public bool GetBool() + { + return GetByte() == 1; + } + + 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 string.Empty; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + ArraySegment data = GetBytesSegment(actualSize); + + return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(data.Array, data.Offset, data.Count) > maxLength) ? + string.Empty : + NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count); + } + + public string GetString() + { + ushort size = GetUShort(); + if (size == 0) + { + return string.Empty; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + ArraySegment data = GetBytesSegment(actualSize); + + return NetDataWriter.uTF8Encoding.Value.GetString(data.Array, data.Offset, data.Count); + } + + public ArraySegment GetBytesSegment(int count) + { + ArraySegment segment = new(_data, _position, count); + _position += count; + return segment; + } + + public ArraySegment GetRemainingBytesSegment() + { + ArraySegment segment = new(_data, _position, AvailableBytes); + _position = _data.Length; + return segment; + } + + public T Get() where T : struct, INetSerializable + { + var obj = default(T); + obj.Deserialize(this); + return obj; + } + + public T Get(Func constructor) where T : class, INetSerializable + { + var obj = constructor(); + obj.Deserialize(this); + return obj; + } + +#if LITENETLIB_SPANS || NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1 || NETCOREAPP3_1 || NET5_0 || NETSTANDARD2_1 + + public ReadOnlySpan GetRemainingBytesSpan() + { + return new ReadOnlySpan(_data, _position, _dataSize - _position); + } +#endif + + 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() + { + return GetArray(1); + } + + public byte[] GetBytesWithLength() + { + return GetArray(1); + } + #endregion + + #region PeekMethods + + public byte PeekByte() + { + return _data[_position]; + } + + public sbyte PeekSByte() + { + return (sbyte)_data[_position]; + } + + public bool PeekBool() + { + return _data[_position] == 1; + } + + 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); + } + + /// + /// Note that "maxLength" only limits the number of characters in a string, not its size in bytes. + /// + public string PeekString(int maxLength) + { + ushort size = PeekUShort(); + if (size == 0) + { + return string.Empty; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + return (maxLength > 0 && NetDataWriter.uTF8Encoding.Value.GetCharCount(_data, _position + 2, actualSize) > maxLength) ? + string.Empty : + NetDataWriter.uTF8Encoding.Value.GetString(_data, _position + 2, actualSize); + } + + public string PeekString() + { + ushort size = PeekUShort(); + if (size == 0) + { + return string.Empty; + } + + int actualSize = size - 1; + if (actualSize >= NetDataWriter.StringBufferMaxLength) + { + return null; + } + + return NetDataWriter.uTF8Encoding.Value.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) + { + if (!TryGetUShort(out ushort 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 >= 2) + { + ushort length = PeekUShort(); + if (length >= 0 && AvailableBytes >= 2 + length) + { + 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..f0ec295 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 2876e12b475627f448ca5a6850748bc3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataReader.cs + uploadId: 762203 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..3862f85 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs @@ -0,0 +1,389 @@ +using System; +using System.Net; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +namespace LiteNetLib.Utils +{ + public class NetDataWriter + { + protected byte[] _data; + protected int _position; + private const int InitialSize = 64; + private readonly bool _autoResize; + + public int Capacity + { + + get => _data.Length; + } + public byte[] Data + { + + get => _data; + } + public int Length + { + + get => _position; + } + + public static readonly ThreadLocal uTF8Encoding = new(() => new(false, true)); + public const int StringBufferMaxLength = 65535; + 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(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; + } + + + public void ResizeIfNeed(int newSize) + { + if (_data.Length < newSize) + { + Array.Resize(ref _data, Math.Max(newSize, _data.Length * 2)); + } + } + + + 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, ushort length) + { + if (_autoResize) + ResizeIfNeed(_position + 2 + length); + FastBitConverter.GetBytes(_data, _position, length); + Buffer.BlockCopy(data, offset, _data, _position + 2, length); + _position += 2 + length; + } + + public void PutSBytesWithLength(sbyte[] data) + { + PutArray(data, 1); + } + + public void PutBytesWithLength(byte[] data, int offset, ushort length) + { + if (_autoResize) + ResizeIfNeed(_position + 2 + length); + FastBitConverter.GetBytes(_data, _position, length); + Buffer.BlockCopy(data, offset, _data, _position + 2, length); + _position += 2 + length; + } + + public void PutBytesWithLength(byte[] data) + { + PutArray(data, 1); + } + + public void Put(bool value) + { + Put((byte)(value ? 1 : 0)); + } + + public 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 PutArray(T[] value) where T : INetSerializable, new() + { + ushort strArrayLength = (ushort)(value?.Length ?? 0); + Put(strArrayLength); + for (int i = 0; i < strArrayLength; i++) + value[i].Serialize(this); + } + + 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 (string.IsNullOrEmpty(value)) + { + Put((ushort)0); + return; + } + + int length = maxLength > 0 && value.Length > maxLength ? maxLength : value.Length; + int size = uTF8Encoding.Value.GetBytes(value, 0, length, _stringBuffer, 0); + + if (size == 0 || 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..25493f8 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: bb3b55fb59ddd9044b1fd56b8543053b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetDataWriter.cs + uploadId: 762203 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..d4d5e4c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +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(); + + public NetPacketProcessor() + { + _netSerializer = new(); + } + + public NetPacketProcessor(int maxStringLength) + { + _netSerializer = new(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 Write< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T, TUserData>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T, TUserData>(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..0450dbd --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: f0d5a653362556943a36db010a601057 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetPacketProcessor.cs + uploadId: 762203 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..a8e9517 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs @@ -0,0 +1,762 @@ +using System; +using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +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(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(); + + public NetSerializer() : this(0) + { + } + + public NetSerializer(int maxStringLength) + { + _maxStringLength = maxStringLength; + } + + private ClassInfo RegisterInternal< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>() + { + if (ClassInfo.Instance != null) + return ClassInfo.Instance; + + var props = typeof(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(serializers); + return ClassInfo.Instance; + } + + /// 's fields are not supported, or it has no fields + public void Register< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>() + { + 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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(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< +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(Trimming.SerializerMemberTypes)] +#endif + T>(T obj) where T : class, new() + { + if (_writer == null) + _writer = new(); + _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..bd1724c --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 503136cdfd845ea439d6eb1e7fcfa924 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NetSerializer.cs + uploadId: 762203 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..20ec4fa --- /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(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(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..47f8863 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: daf31cf4ab8132943b2c8ca301fc919a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpPacket.cs + uploadId: 762203 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..5c436ba --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c3fa3bfbb02dd944e939070e3cf0638c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/NtpRequest.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs new file mode 100644 index 0000000..b73e1b9 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs @@ -0,0 +1,12 @@ +using System; + +namespace LiteNetLib.Utils +{ + /// + /// PreserveAttribute prevents byte code stripping from removing a class, method, field, or property. + /// + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)] + public class PreserveAttribute : Attribute + { + } +} diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs.meta new file mode 100644 index 0000000..6097d6e --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c1036adeea398ef48b307b909966d955 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/Utils/Preserve.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json new file mode 100644 index 0000000..e501145 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json @@ -0,0 +1,11 @@ +{ + "name": "com.revenantx.litenetlib", + "version": "1.0.1-1", + "displayName": "LiteNetLib", + "description": "Lite reliable UDP library for .NET Standard 2.0 (Mono, .NET Core, .NET Framework)", + "unity": "2018.3", + "author": { + "name": "RevenantX", + "url": "https://github.com/RevenantX" + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json.meta b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json.meta new file mode 100644 index 0000000..bb0f615 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: f46cc664b3dd2944a8b04542ecb07732 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/LiteNetLib/package.json + uploadId: 762203 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..257b5ac --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs @@ -0,0 +1,612 @@ +using FishNet.Managing; +using FishNet.Managing.Transporting; +using LiteNetLib.Layers; +using System; +using System.Runtime.CompilerServices; +using LiteNetLib; +using UnityEngine; + +namespace FishNet.Transporting.Tugboat +{ + [DisallowMultipleComponent] + [AddComponentMenu("FishNet/Transport/Tugboat")] + public class Tugboat : Transport + { + ~Tugboat() + { + Shutdown(); + } + + #region Serialized. + /* Settings / Misc. */ + /// + /// True to stop local server and client sockets using a new thread. + /// + internal bool StopSocketsOnThread => _stopSocketsOnThread; + [Tooltip("True to stop local server and client sockets using a new thread.")] + [SerializeField] + private bool _stopSocketsOnThread = false; + /// + /// While true, forces sockets to send data directly to interface without routing. + /// + internal bool DontRoute => _dontRoute; + [Tooltip("While true, forces sockets to send data directly to interface without routing.")] + [SerializeField] + private bool _dontRoute; + /// + /// Allows the same address and port to be used multiple times by the server. This can be useful if you wish to launch multiple builds or server instances on the same machine using the same configuration. + /// + internal bool ReuseAddress => _reuseAddress; + [Tooltip("Allows the same address and port to be used multiple times by the server. This can be useful if you wish to launch multiple builds or server instances on the same machine using the same configuration.")] + [SerializeField] + private bool _reuseAddress; + /* 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; + + /* Server. */ + /// + /// IPv4 address to bind server to. + /// + [Tooltip("IPv4 Address to bind server to.")] + [SerializeField] + private string _ipv4BindAddress; + /// + /// Enable IPv6 only on demand to avoid problems in Linux environments where it may have been disabled on host + /// + [Tooltip("Enable IPv6, Server listens on IPv4 and IPv6 address")] + [SerializeField] + private bool _enableIpv6 = true; + /// + /// 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; + + /* Client. */ + /// + /// Address to connect. + /// + [Tooltip("Address to connect.")] + [SerializeField] + private string _clientAddress = "localhost"; + #endregion + + #region Private. + /// + /// PacketLayer to use with LiteNetLib. + /// + private PacketLayerBase _packetLayer; + /// + /// Server socket and handler. This field is exposed for advanced-use. Use caution when accessing this outside of this class. + /// + public Server.ServerSocket ServerSocket = new(); + /// + /// Client socket and handler. This field is exposed for advanced-use. Use caution when accessing this outside of this class. + /// + public Client.ClientSocket ClientSocket = new(); + /// + /// Current timeout for the client. + /// + private int _clientTimeout = MAX_TIMEOUT_SECONDS; + /// + /// Current timeout for the server. + /// + private int _serverTimeout = MAX_TIMEOUT_SECONDS; + #endregion + + #region Const. + /// + /// Maximum timeout value to use. + /// + 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); + networkManager.TimeManager.OnUpdate += TimeManager_OnUpdate; + } + + protected void OnDestroy() + { + Shutdown(); + if (base.NetworkManager != null) + base.NetworkManager.TimeManager.OnUpdate -= TimeManager_OnUpdate; + } + #endregion + + #region ConnectionStates. + /// + /// Gets the address of a remote connection Id. + /// + /// + /// + public override string GetConnectionAddress(int connectionId) + { + return ServerSocket.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 ServerSocket.GetConnectionState(); + else + return ClientSocket.GetConnectionState(); + } + + /// + /// Gets the current ConnectionState of a remote client on the server. + /// + /// ConnectionId to get ConnectionState for. + public override RemoteConnectionState GetConnectionState(int connectionId) + { + return ServerSocket.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); + } + + /// + /// Handles a ConnectionStateArgs for a remote client. + /// + /// + public override void HandleRemoteConnectionState(RemoteConnectionStateArgs connectionStateArgs) + { + OnRemoteConnectionState?.Invoke(connectionStateArgs); + } + #endregion + + #region Iterating. + /// + /// Called every update to poll for data. + /// + private void TimeManager_OnUpdate() + { + ServerSocket?.PollSocket(); + ClientSocket?.PollSocket(); + } + + /// + /// Processes data received by the socket. + /// + /// True to read data from clients, false to read data from the server. + public override void IterateIncoming(bool asServer) + { + if (asServer) + ServerSocket.IterateIncoming(); + else + ClientSocket.IterateIncoming(); + } + + /// + /// Processes data to be sent by the socket. + /// + /// True to send data from the local server to clients, false to send from the local client to server. + public override void IterateOutgoing(bool asServer) + { + if (asServer) + ServerSocket.IterateOutgoing(); + else + ClientSocket.IterateOutgoing(); + } + #endregion + + #region Sending. + /// + /// Sends to the server or all clients. + /// + /// Channel to use. + /// Data to send. + public override void SendToServer(byte channelId, ArraySegment segment) + { + SanitizeChannel(ref channelId); + ClientSocket.SendToServer(channelId, segment); + } + + /// + /// Sends data to a client. + /// + /// + /// + /// + public override void SendToClient(byte channelId, ArraySegment segment, int connectionId) + { + SanitizeChannel(ref channelId); + ServerSocket.SendToClient(channelId, segment, connectionId); + } + #endregion + + #region Receiving. + /// + /// 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); + } + + /// + /// Returns packet loss percentage. This transport supports this feature. + /// + /// True to return packet loss on the server, false to return packet loss on the client. + public override float GetPacketLoss(bool asServer) + { + NetManager nm; + if (asServer && ServerSocket != null) + nm = ServerSocket.NetManager; + else if (!asServer && ClientSocket != null) + nm = ClientSocket.NetManager; + else + nm = null; + + if (nm == null) + return 0f; + + return nm.Statistics.PacketLossPercent; + } + #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."); + + InitializeSocket(asServer: true); + InitializeSocket(asServer: false); + } + + /// + /// 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)MAX_TIMEOUT_SECONDS; + } + + /// + /// 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) + { + int timeoutValue = (int)Math.Ceiling(value); + if (asServer) + _serverTimeout = timeoutValue; + else + _clientTimeout = timeoutValue; + + UpdateTimeout(); + } + + /// + /// 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 ServerSocket.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; + ServerSocket.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() + { + //Server. + ushort? result = ServerSocket?.GetPort(); + if (result.HasValue) + return result.Value; + //Client. + result = ClientSocket?.GetPort(); + if (result.HasValue) + return result.Value; + + 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 ServerSocket.StopConnection(connectionId); + } + + /// + /// Stops both client and server. + /// + public override void Shutdown() + { + //Stops client then server connections. + StopConnection(false); + StopConnection(true); + } + + #region Privates. + /// + /// Initializes client or server socket. + /// + private void InitializeSocket(bool asServer) + { + if (asServer) + ServerSocket.Initialize(this, _unreliableMtu, _packetLayer, _enableIpv6); + else + ClientSocket.Initialize(this, _unreliableMtu, _packetLayer); + } + + /// + /// Starts server. + /// + private bool StartServer() + { + InitializeSocket(asServer: true); + UpdateTimeout(); + return ServerSocket.StartConnection(_port, _maximumClients, _ipv4BindAddress, _ipv6BindAddress); + } + + /// + /// Stops server. + /// + private bool StopServer() + { + if (ServerSocket == null) + return false; + else + return ServerSocket.StopConnection(); + } + + /// + /// Starts the client. + /// + /// + private bool StartClient(string address) + { + InitializeSocket(asServer: false); + UpdateTimeout(); + return ClientSocket.StartConnection(address, _port); + } + + /// + /// Updates clients timeout values. + /// + private void UpdateTimeout() + { + ClientSocket.UpdateTimeout(_clientTimeout); + ServerSocket.UpdateTimeout(_serverTimeout); + } + + /// + /// Stops the client. + /// + private bool StopClient() + { + if (ClientSocket == null) + return false; + else + return ClientSocket.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 + } +} \ No newline at end of file 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..b49b755 --- /dev/null +++ b/Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6f48f002b825cbd45a19bd96d90f9edb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: bf9191e2e07d29749bca3a1ae44e4bc8, type: 3} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Transporting/Transports/Tugboat/Tugboat.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility.meta b/Assets/FishNet/Runtime/Utility.meta new file mode 100644 index 0000000..6a09ac5 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 54fe12cb57fcc5e48bfd592d260140d7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs b/Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs new file mode 100644 index 0000000..298cb5e --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs @@ -0,0 +1,36 @@ +// namespace FishNet.Component.Transforming +// { +// public enum AdaptiveInterpolationType +// { +// /// +// /// Adaptive interpolation is disabled. An exact interpolation value is used. +// /// +// Off = 0, +// /// +// /// Visual disturbances caused by desynchronization are definite without predicting future states. +// /// +// ExtremelyLow = 1, +// /// +// /// Visual disturbances caused by desynchronization are likely without predicting future states. +// /// +// VeryLow = 2, +// /// +// /// Visual disturbances caused by desynchronization are still possible but less likely. +// /// +// Low = 3, +// /// +// /// Visual disturbances caused by desynchronization are likely without predicting a small amount of future states. +// /// +// Moderate = 4, +// /// +// /// Visual disturbances caused by desynchronization are very unlikely. Graphics are using a generous amount interpolation. +// /// +// High = 5, +// /// +// /// Visual disturbances caused by desynchronization are extremely unlikely. Graphics are using a generous amount interpolation. +// /// +// VeryHigh = 6, +// } +// +// +// } diff --git a/Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs.meta b/Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs.meta new file mode 100644 index 0000000..1f0e2a3 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c9aaf062df354644098da9c46e2a9512 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/AdaptiveInterpolationType.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs b/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs new file mode 100644 index 0000000..f8f586d --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs @@ -0,0 +1,629 @@ +// Remove on V5 +// using FishNet.Managing; +// using FishNet.Managing.Timing; +// using FishNet.Utility.Extension; +// using GameKit.Dependencies.Utilities; +// using System.Runtime.CompilerServices; +// using UnityEngine; +// using UnityEngine.Scripting; +// +// namespace FishNet.Object.Prediction +// { +// /// +// /// This class is under regular development and it's API may change at any time. +// /// +// public sealed class ChildTransformTickSmoother : IResettable +// { +// #region Types. +// [Preserve] +// private struct TickTransformProperties +// { +// public uint Tick; +// public TransformProperties Properties; +// +// public TickTransformProperties(uint tick, Transform t) +// { +// Tick = tick; +// Properties = new TransformProperties(t.localPosition, t.localRotation, t.localScale); +// } +// public TickTransformProperties(uint tick, Transform t, Vector3 localScale) +// { +// Tick = tick; +// Properties = new TransformProperties(t.localPosition, t.localRotation, localScale); +// } +// public TickTransformProperties(uint tick, TransformProperties tp) +// { +// Tick = tick; +// Properties = tp; +// } +// } +// #endregion +// +// #region Private. +// /// +// /// Object to smooth. +// /// +// private Transform _graphicalObject; +// /// +// /// When not MoveRates.UNSET_VALUE the graphical object will teleport into it's next position if the move distance exceeds this value. +// /// +// private float _teleportThreshold; +// /// +// /// How quickly to move towards goal values. +// /// +// private MoveRates _moveRates = new MoveRates(MoveRates.UNSET_VALUE); +// /// +// /// True if a pretick occurred since last postTick. +// /// +// private bool _preTicked; +// /// +// /// World offset values of the graphical from the NetworkObject during initialization. +// /// +// private TransformProperties _gfxInitializedOffsetValues; +// /// +// /// World values of the graphical after it's been aligned to initialized values in PreTick. +// /// +// private TransformProperties _gfxPreSimulateWorldValues; +// /// +// /// TickDelta on the TimeManager. +// /// +// private float _tickDelta; +// /// +// /// How many ticks to interpolate over when not using adaptive. +// /// +// private byte _ownerInterpolation; +// /// +// /// Current interpolation, regardless of if using adaptive or not. +// /// +// private byte _interpolation; +// /// +// /// NetworkObject this is for. +// /// +// private NetworkObject _networkObject; +// /// +// /// Value to multiply movement by. This is used to reduce or increase the rate the movement buffer is consumed. +// /// +// private float _movementMultiplier = 1f; +// /// +// /// TransformProperties to move towards. +// /// +// private BasicQueue _transformProperties; +// /// +// /// Which properties to smooth. +// /// +// private TransformPropertiesFlag _ownerSmoothedProperties; +// /// +// /// Which properties to smooth. +// /// +// private TransformPropertiesFlag _spectatorSmoothedProperties; +// /// +// /// Updates the smoothedProperties value. +// /// +// /// New value. +// /// True if updating values for the spectator, false if updating for owner. +// public void SetSmoothedProperties(TransformPropertiesFlag value, bool forSpectator) +// { +// if (forSpectator) +// _spectatorSmoothedProperties = value; +// else +// _ownerSmoothedProperties = value; +// } +// /// +// /// Amount of adaptive interpolation to use. +// /// +// private AdaptiveInterpolationType _adaptiveInterpolation = AdaptiveInterpolationType.Low; +// /// +// /// Updates the adaptiveInterpolation value. +// /// +// /// New value. +// public void SetAdaptiveInterpolation(AdaptiveInterpolationType adaptiveInterpolation) +// { +// if (adaptiveInterpolation != AdaptiveInterpolationType.Off) +// { +// adaptiveInterpolation = AdaptiveInterpolationType.Off; +// Debug.Log($"AdaptiveInterpolation has been changed to off at runtime while it's under development. This message may be ignored."); +// } +// _adaptiveInterpolation = adaptiveInterpolation; +// } +// /// +// /// Set interpolation to use for spectated objects if adaptiveInterpolation is off. +// /// +// private byte _spectatorInterpolation; +// /// +// /// Sets the spectator interpolation value. +// /// +// /// New value. +// /// True to also disable adaptive interpolation to use this new value. +// public void SetSpectatorInterpolation(byte value, bool disableAdaptiveInterpolation = true) +// { +// _spectatorInterpolation = value; +// if (disableAdaptiveInterpolation) +// _adaptiveInterpolation = AdaptiveInterpolationType.Off; +// } +// /// +// /// Previous parent the graphical was attached to. +// /// +// private Transform _previousParent; +// /// +// /// True if to detach at runtime. +// /// +// private bool _detach; +// /// +// /// True if were an owner of the NetworkObject during PreTick. +// /// This is only used for performance gains. +// /// +// private bool _ownerOnPretick; +// /// +// /// True if adaptive interpolation should be used. +// /// +// private bool _useAdaptiveInterpolation => (!_ownerOnPretick && _adaptiveInterpolation != AdaptiveInterpolationType.Off); +// /// +// /// True if Initialized has been called and settings have not been reset. +// /// +// private bool _initialized; +// #endregion +// +// #region Const. +// /// +// /// Maximum allowed entries to be queued over the interpolation amount. +// /// +// private int MAXIMUM_QUEUED_OVER_INTERPOLATION = 3; +// #endregion +// +// ~ChildTransformTickSmoother() +// { +// //This is a last resort for if something didnt deinitialize right. +// ResetState(); +// } +// +// /// +// /// Initializes this smoother; should only be completed once. +// /// +// public void Initialize(NetworkObject nob, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, byte spectatorInterpolation, TransformPropertiesFlag specatorSmoothedProperties, AdaptiveInterpolationType adaptiveInterpolation) +// { +// ResetState(); +// _detach = detach; +// _networkObject = nob; +// _transformProperties = CollectionCaches.RetrieveBasicQueue(); +// _gfxInitializedOffsetValues = nob.transform.GetTransformOffsets(graphicalObject); +// _tickDelta = tickDelta; +// _graphicalObject = graphicalObject; +// _teleportThreshold = teleportDistance; +// _ownerInterpolation = ownerInterpolation; +// _spectatorInterpolation = spectatorInterpolation; +// _ownerSmoothedProperties = ownerSmoothedProperties; +// _spectatorSmoothedProperties = specatorSmoothedProperties; +// SetAdaptiveInterpolation(adaptiveInterpolation); +// UpdateInterpolation(0); +// _initialized = true; +// } +// +// /// +// /// Deinitializes this smoother resetting values. +// /// +// public void Deinitialize() +// { +// ResetState(); +// } +// +// /// +// /// Updates interpolation based on localClient latency. +// /// +// private void UpdateInterpolation(uint clientStateTick) +// { +// if (_networkObject.IsServerStarted || _networkObject.IsOwner) +// { +// _interpolation = _ownerInterpolation; +// } +// else +// { +// if (_adaptiveInterpolation == AdaptiveInterpolationType.Off) +// { +// _interpolation = _spectatorInterpolation; +// } +// else +// { +// float interpolation; +// TimeManager tm = _networkObject.TimeManager; +// if (clientStateTick == 0) +// { +// //Not enough data to calculate; guestimate. This should only happen once. +// float fRtt = (float)tm.RoundTripTime; +// interpolation = (fRtt / 10f); +// +// } +// else +// { +// interpolation = (tm.LocalTick - clientStateTick) + _networkObject.PredictionManager.StateInterpolation; +// } +// +// switch (_adaptiveInterpolation) +// { +// case AdaptiveInterpolationType.VeryLow: +// interpolation *= 0.25f; +// break; +// case AdaptiveInterpolationType.Low: +// interpolation *= 0.375f; +// break; +// case AdaptiveInterpolationType.Medium: +// interpolation *= 0.5f; +// break; +// case AdaptiveInterpolationType.High: +// interpolation *= 0.75f; +// break; +// //Make no changes for maximum. +// } +// +// interpolation = Mathf.Clamp(interpolation, 1f, (float)byte.MaxValue); +// _interpolation = (byte)Mathf.RoundToInt(interpolation); +// } +// } +// } +// +// internal void OnStartClient() +// { +// if (!_detach) +// return; +// +// _previousParent = _graphicalObject.parent; +// TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties(); +// _graphicalObject.SetParent(null); +// _graphicalObject.SetWorldProperties(gfxWorldProperties); +// } +// +// internal void OnStopClient() +// { +// if (!_detach || _previousParent == null || _graphicalObject == null) +// return; +// +// _graphicalObject.SetParent(_previousParent); +// _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); +// } +// +// /// +// /// Called every frame. +// /// +// internal void Update() +// { +// if (!CanSmooth()) +// return; +// +// if (_useAdaptiveInterpolation) +// AdaptiveMoveToTarget(Time.deltaTime); +// else +// BasicMoveToTarget(Time.deltaTime); +// } +// +// /// +// /// Called when the TimeManager invokes OnPreTick. +// /// +// public void OnPreTick() +// { +// if (!CanSmooth()) +// return; +// +// _preTicked = true; +// +// _ownerOnPretick = _networkObject.IsOwner; +// if (_useAdaptiveInterpolation) +// DiscardExcessiveTransformPropertiesQueue(); +// else +// ClearTransformPropertiesQueue(); +// //These only need to be set if still attached. +// if (!_detach) +// _gfxPreSimulateWorldValues = _graphicalObject.GetWorldProperties(); +// } +// +// /// +// /// Called when the PredictionManager invokes OnPreReconcile. +// /// +// public void OnPreReconcile() +// { +// UpdateInterpolation(_networkObject.PredictionManager.ClientStateTick); +// } +// +// /// +// /// Called when the TimeManager invokes OnPostReplay. +// /// +// /// Replay tick for the local client. +// public void OnPostReplay(uint clientTick) +// { +// if (_transformProperties.Count == 0) +// return; +// if (!_useAdaptiveInterpolation) +// return; +// +// uint firstTick = _transformProperties.Peek().Tick; +// //Already in motion to first entry, or first entry passed tick. +// if (clientTick <= firstTick) +// return; +// +// ModifyTransformProperties(clientTick, firstTick); +// } +// +// /// +// /// Called when TimeManager invokes OnPostTick. +// /// +// /// Local tick of the client. +// public void OnPostTick(uint clientTick) +// { +// if (!CanSmooth()) +// return; +// +// //If preticked then previous transform values are known. +// if (_preTicked) +// { +// if (_useAdaptiveInterpolation) +// DiscardExcessiveTransformPropertiesQueue(); +// else +// ClearTransformPropertiesQueue(); +// //Only needs to be put to pretick position if not detached. +// if (!_detach) +// _graphicalObject.SetWorldProperties(_gfxPreSimulateWorldValues); +// AddTransformProperties(clientTick); +// } +// //If did not pretick then the only thing we can do is snap to instantiated values. +// else +// { +// //Only set to position if not to detach. +// if (!_detach) +// _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); +// } +// } +// +// /// +// /// Teleports the graphical to it's starting position and clears the internal movement queue. +// /// +// public void Teleport() +// { +// ClearTransformPropertiesQueue(); +// TransformProperties startProperties = _networkObject.transform.GetWorldProperties(); +// startProperties.Add(_gfxInitializedOffsetValues); +// _graphicalObject.SetWorldProperties(startProperties); +// } +// +// /// +// /// Clears the pending movement queue. +// /// +// private void ClearTransformPropertiesQueue() +// { +// _transformProperties.Clear(); +// //Also unset move rates since there is no more queue. +// _moveRates = new MoveRates(MoveRates.UNSET_VALUE); +// } +// +// /// +// /// Discards datas over interpolation limit from movement queue. +// /// +// private void DiscardExcessiveTransformPropertiesQueue() +// { +// if (!_useAdaptiveInterpolation) +// { +// _networkObject.NetworkManager.LogError($"This method should only be called when using adaptive interpolation."); +// return; +// } +// +// int dequeueCount = (_transformProperties.Count - (_interpolation + MAXIMUM_QUEUED_OVER_INTERPOLATION)); +// //If there are entries to dequeue. +// if (dequeueCount > 0) +// { +// TickTransformProperties tpp = default; +// for (int i = 0; i < dequeueCount; i++) +// tpp = _transformProperties.Dequeue(); +// +// SetAdaptiveMoveRates(tpp.Properties, _transformProperties[0].Properties); +// } +// } +// +// /// +// /// Adds a new transform properties and sets move rates if needed. +// /// +// private void AddTransformProperties(uint tick) +// { +// TickTransformProperties tpp = new TickTransformProperties(tick, GetNetworkObjectWorldPropertiesWithOffset()); +// +// _transformProperties.Enqueue(tpp); +// //If first entry then set move rates. +// if (_transformProperties.Count == 1) +// { +// TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties(); +// if (_useAdaptiveInterpolation) +// SetAdaptiveMoveRates(gfxWorldProperties, tpp.Properties); +// else +// SetBasicMoveRates(gfxWorldProperties, tpp.Properties); +// } +// } +// +// /// +// /// Modifies a transform property for a tick. This does not error check for empty collections. +// /// +// /// First tick in the queue. If 0 this will be looked up. +// private void ModifyTransformProperties(uint clientTick, uint firstTick) +// { +// uint tick = clientTick; +// /*Ticks will always be added incremental by 1 so it's safe to jump ahead the difference +// * of tick and firstTick. */ +// int index = (int)(tick - firstTick); +// //Replace with new data. +// if (index < _transformProperties.Count) +// { +// _transformProperties[index] = new TickTransformProperties(tick, _networkObject.transform, _graphicalObject.localScale); +// } +// else +// { +// //This should never happen. +// } +// } +// +// /// +// /// Returns TransformProperties of the NetworkObject with the graphicals world offset. +// /// +// /// +// private TransformProperties GetNetworkObjectWorldPropertiesWithOffset() => _networkObject.transform.GetWorldProperties(_gfxInitializedOffsetValues); +// +// /// +// /// Returns if prediction can be used on this rigidbody. +// /// +// /// +// private bool CanSmooth() +// { +// if (_graphicalObject == null) +// return false; +// +// return true; +// } +// +// /// +// /// Sets Position and Rotation move rates to reach Target datas. +// /// +// private void SetBasicMoveRates(TransformProperties prevValues, TransformProperties nextValues) +// { +// byte interpolation = _interpolation; +// float duration = (_tickDelta * interpolation); +// /* If interpolation is 1 then add on a tiny amount +// * of more time to compensate for frame time, so that +// * the smoothing does not complete before the next tick, +// * as this would result in jitter. */ +// if (interpolation == 1) +// duration += (1 / 55f); +// float teleportT = (_teleportThreshold * (float)interpolation); +// +// _moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, teleportT); +// _moveRates.TimeRemaining = duration; +// } +// +// +// /// +// /// Sets Position and Rotation move rates to reach Target datas. +// /// +// private void SetAdaptiveMoveRates(TransformProperties prevValues, TransformProperties nextValues) +// { +// float duration = _tickDelta; +// /* If interpolation is 1 then add on a tiny amount +// * of more time to compensate for frame time, so that +// * the smoothing does not complete before the next tick, +// * as this would result in jitter. */ +// float teleportT = _teleportThreshold; +// _moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, teleportT); +// _moveRates.TimeRemaining = duration; +// +// SetMovementMultiplier(); +// } +// +// private void SetMovementMultiplier() +// { +// /* If there's more in queue than interpolation then begin to move faster based on overage. +// * Move 5% faster for every overage. */ +// int overInterpolation = (_transformProperties.Count - _interpolation); +// //If needs to be adjusted. +// if (overInterpolation != 0f) +// { +// _movementMultiplier += (0.015f * overInterpolation); +// } +// //If does not need to be adjusted. +// else +// { +// //If interpolation is 1 then slow down just barely to accomodate for frame delta variance. +// if (_interpolation == 1) +// _movementMultiplier = 0.99f; +// } +// +// _movementMultiplier = Mathf.Clamp(_movementMultiplier, 0.95f, 1.05f); +// } +// +// +// /// +// /// Moves transform to target values. +// /// +// +// private void BasicMoveToTarget(float delta) +// { +// int tpCount = _transformProperties.Count; +// //No data. +// if (tpCount == 0) +// return; +// +// TickTransformProperties ttp = _transformProperties.Peek(); +// _moveRates.MoveWorldToTarget(_graphicalObject, ttp.Properties, delta); +// +// //if TimeLeft is <= 0f then transform should be at goal. +// if (_moveRates.TimeRemaining <= 0f) +// ClearTransformPropertiesQueue(); +// } +// +// /// +// /// Moves transform to target values. +// /// +// +// private void AdaptiveMoveToTarget(float delta) +// { +// int tpCount = _transformProperties.Count; +// //No data. +// if (tpCount == 0) +// return; +// /* If buffer is considerably under goal then halt +// * movement. This will allow the buffer to grow. */ +// if ((tpCount - _interpolation) < -4) +// return; +// +// TickTransformProperties ttp = _transformProperties.Peek(); +// TransformPropertiesFlag smoothedProperties = (_ownerOnPretick) ? _ownerSmoothedProperties : _spectatorSmoothedProperties; +// _moveRates.MoveWorldToTarget(_graphicalObject, ttp.Properties, smoothedProperties, (delta * _movementMultiplier)); +// float tRemaining = _moveRates.TimeRemaining; +// //if TimeLeft is <= 0f then transform is at goal. Grab a new goal if possible. +// if (tRemaining <= 0f) +// { +// //Dequeue current entry and if there's another call a move on it. +// _transformProperties.Dequeue(); +// +// //If there are entries left then setup for the next. +// if (_transformProperties.Count > 0) +// { +// SetAdaptiveMoveRates(ttp.Properties, _transformProperties.Peek().Properties); +// //If delta is negative then call move again with abs. +// if (tRemaining < 0f) +// AdaptiveMoveToTarget(Mathf.Abs(tRemaining)); +// } +// //No remaining, set to snap. +// else +// { +// ClearTransformPropertiesQueue(); +// } +// } +// } +// +// public void ResetState() +// { +// if (!_initialized) +// return; +// +// _networkObject = null; +// if (_graphicalObject != null) +// { +// if (_networkObject != null) +// { +// if (_detach) +// _graphicalObject.SetParent(_networkObject.transform); +// _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); +// _graphicalObject = null; +// } +// else if (_detach) +// { +// UnityEngine.Object.Destroy(_graphicalObject.gameObject); +// } +// } +// _movementMultiplier = 1f; +// CollectionCaches.StoreAndDefault(ref _transformProperties); +// _teleportThreshold = default; +// _moveRates = default; +// _preTicked = default; +// _gfxInitializedOffsetValues = default; +// _gfxPreSimulateWorldValues = default; +// _tickDelta = default; +// _interpolation = default; +// } +// +// public void InitializeState() { } +// } +// +// } \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs.meta b/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs.meta new file mode 100644 index 0000000..78d0f03 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 50721ce3e3600754dbc3c6e2e5446728 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/AdaptiveLocalTransformSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Constants.cs b/Assets/FishNet/Runtime/Utility/Constants.cs new file mode 100644 index 0000000..22242f4 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Constants.cs @@ -0,0 +1,15 @@ +namespace FishNet.Utility +{ + 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"; + + public const string GeneratedWriterPrefix = "GWrite___"; + public const string GeneratedReaderPrefix = "GRead___"; + } + +} \ 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..98daba7 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Constants.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3f2a3c23b44e4ef4e9783ef53ec0d5da +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Constants.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Extension.meta b/Assets/FishNet/Runtime/Utility/Extension.meta new file mode 100644 index 0000000..8e97793 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d008bcb408806c45980331a665c65a7 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Extension/Networks.cs b/Assets/FishNet/Runtime/Utility/Extension/Networks.cs new file mode 100644 index 0000000..1120600 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Networks.cs @@ -0,0 +1,25 @@ +using FishNet.Documenting; +using FishNet.Managing; +using FishNet.Object; + +namespace FishNet.Utility.Extension +{ + [APIExclude] + public static class NetworksFN + { + /// + /// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side. + /// + public static bool DoubleLogic(this NetworkObject nob, bool asServer) => (!asServer && nob.NetworkManager.IsServerStarted); + /// + /// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side. + /// + public static bool DoubleLogic(this NetworkManager manager, bool asServer) => (!asServer && manager.IsServerStarted); + /// + /// Returns if logic could have potentially called already on server side, and is calling a second time for clientHost side. + /// + public static bool DoubleLogic(this NetworkBehaviour nb, bool asServer) => (!asServer && nb.NetworkManager.IsServerStarted); + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Networks.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Networks.cs.meta new file mode 100644 index 0000000..e819db2 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Networks.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cbd3adaae5d34a14ab9cf68726b3bd3e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Extension/Networks.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs b/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs new file mode 100644 index 0000000..c15db54 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs @@ -0,0 +1,95 @@ +using FishNet.Managing; +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace FishNet.Utility.Extension +{ + + public static class Scenes + { + /// + /// 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. + /// + public static void GetSceneNetworkObjects(Scene s, bool firstOnly, bool errorOnDuplicates, bool ignoreUnsetSceneIds, ref List result) + { + List nobCacheA = CollectionCaches.RetrieveList(); + List nobCacheB = CollectionCaches.RetrieveList(); + List gameObjectCache = CollectionCaches.RetrieveList(); + Dictionary sceneIds = CollectionCaches.RetrieveDictionary(); + + //Iterate all root objects for the scene. + s.GetRootGameObjects(gameObjectCache); + foreach (GameObject go in gameObjectCache) + { + //Get NetworkObjects within children of each root. + go.GetComponentsInChildren(true, nobCacheA); + //If network objects are found. + if (nobCacheA.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 nobCacheA) + { + if (ignoreUnsetSceneIds && !nob.IsSceneObject) + continue; + + nob.GetComponentsInParent(true, nobCacheB); + //No extra nobs, only this one. + if (nobCacheB.Count == 1 && !TryDisplayDuplicateError(nob)) + result.Add(nob); + } + } + //Not first only, add them all. + else + { + foreach (NetworkObject item in nobCacheA) + { + if (ignoreUnsetSceneIds && !item.IsSceneObject) + continue; + if (!TryDisplayDuplicateError(item)) + result.Add(item); + } + } + + } + } + + CollectionCaches.Store(sceneIds); + + bool TryDisplayDuplicateError(NetworkObject nob) + { + if (!errorOnDuplicates) + return false; + + ulong id = nob.SceneId; + //There is a duplicate. + if (sceneIds.TryGetValue(id, out NetworkObject originalNob)) + { + string err = $"Object {nob.name} and {originalNob.name} in scene {nob.gameObject.scene.name} have the same sceneId of {id}. This will result in spawning errors. Exit play mode and use the Fish-Networking menu to reserialize sceneIds for scene {nob.gameObject.scene.name}."; + NetworkManagerExtensions.LogError(err); + return true; + } + else + { + sceneIds[id] = nob; + return false; + } + } + + } + + } + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs.meta b/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs.meta new file mode 100644 index 0000000..62d43dc --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Scenes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: a02f3d03f737e304e9854278f4e9211d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Extension/Scenes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs new file mode 100644 index 0000000..9f9947a --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs @@ -0,0 +1,199 @@ +using System; +using FishNet.Documenting; +using FishNet.Object; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Utility.Extension +{ + [APIExclude] + public static class TransformFN + { + /// + /// Sets values of TransformProperties to a transforms world properties. + /// + public static TransformProperties GetWorldProperties(this Transform t) + { + TransformProperties tp = new(t.position, t.rotation, t.localScale); + return tp; + } + + /// + /// Sets values of TransformProperties to a transforms world properties. + /// + + public static TransformProperties GetWorldProperties(this Transform t, TransformProperties offset) + { + TransformProperties tp = new(t.position, t.rotation, t.localScale); + tp.Add(offset); + return tp; + } + + /// + /// Sets values of TransformProperties to a transforms world properties. + /// + public static TransformPropertiesCls GetWorldPropertiesCls(this Transform t) + { + TransformPropertiesCls tp = new(t.position, t.rotation, t.localScale); + return tp; + } + + + /// + /// Sets values of TransformProperties to a transforms world properties. + /// + public static TransformProperties GetLocalProperties(this Transform t) + { + TransformProperties tp = new(t.localPosition, t.localRotation, t.localScale); + return tp; + } + + /// + /// Sets values of TransformProperties to a transforms world properties. + /// + public static TransformPropertiesCls GetLocalPropertiesCls(this Transform t) + { + TransformPropertiesCls tp = new(t.localPosition, t.localRotation, t.localScale); + return tp; + } + + + /// + /// Sets values of TransformPropertiesCls to a transforms world properties. + /// + [Obsolete("Use TransformPropertiesExtensions.SetWorldProperties.")] + public static void SetWorldProperties(this TransformPropertiesCls tp, Transform t) => TransformPropertiesExtensions.SetWorldProperties(tp, t); + + /// + /// Gets the offset values by subtracting this from target. + /// + /// 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 = (target.position - t.position); + rot = (target.rotation * Quaternion.Inverse(t.rotation)); + } + + /// + /// Gets the offset values by subtracting this from target. + /// + /// True to set scale to Vector3.zero. + public static TransformProperties GetTransformOffsets(this Transform t, Transform target) + { + if (target == null) + return default; + + return new( + (target.position - t.position), + (target.rotation * Quaternion.Inverse(t.rotation)), + (target.localScale - t.localScale) + ); + } + + /// + /// Sets a transform to local properties. + /// + public static void SetLocalProperties(this Transform t, TransformPropertiesCls tp) + { + t.localPosition = tp.Position; + t.localRotation = tp.Rotation; + t.localScale = tp.LocalScale; + } + + /// + /// Sets a transform to local properties. + /// + public static void SetLocalProperties(this Transform t, TransformProperties tp) + { + t.localPosition = tp.Position; + t.localRotation = tp.Rotation; + t.localScale = tp.Scale; + } + + /// + /// Sets a transform to world properties. + /// + public static void SetWorldProperties(this Transform t, TransformPropertiesCls tp) + { + t.position = tp.Position; + t.rotation = tp.Rotation; + t.localScale = tp.LocalScale; + } + /// + /// Sets a transform to world properties. + /// + public static void SetWorldProperties(this Transform t, TransformProperties tp) + { + t.position = tp.Position; + t.rotation = tp.Rotation; + t.localScale = tp.Scale; + } + + /// + /// 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; + } + /// + /// Sets local position, rotation, and scale using nullables for a transform. If a value is null then that property is skipped. + /// + public static void SetLocalPositionRotationAndScale(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale) + { + if (nullablePos.HasValue) + t.localPosition = nullablePos.Value; + if (nullableRot.HasValue) + t.localRotation = nullableRot.Value; + if (nullableScale.HasValue) + t.localScale = nullableScale.Value; + } + + /// + /// Sets world position, rotation, and scale using nullables for a transform. If a value is null then that property is skipped. + /// + public static void SetWorldPositionRotationAndScale(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale) + { + if (nullablePos.HasValue) + t.position = nullablePos.Value; + if (nullableRot.HasValue) + t.rotation = nullableRot.Value; + if (nullableScale.HasValue) + t.localScale = nullableScale.Value; + } + + /// + /// Oututs properties to use for a transform. When a nullable property has value that value is used, otherwise the transforms current property is used. + /// + public static void OutLocalPropertyValues(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale, out Vector3 pos, out Quaternion rot, out Vector3 scale) + { + pos = (nullablePos == null) ? t.localPosition : nullablePos.Value; + rot = (nullableRot == null) ? t.localRotation : nullableRot.Value; + scale = (nullableScale == null) ? t.localScale : nullableScale.Value; + } + + /// + /// Oututs properties to use for a transform. When a nullable property has value that value is used, otherwise the transforms current property is used. + /// + public static void OutWorldPropertyValues(this Transform t, Vector3? nullablePos, Quaternion? nullableRot, Vector3? nullableScale, out Vector3 pos, out Quaternion rot, out Vector3 scale) + { + pos = (nullablePos == null) ? t.position : nullablePos.Value; + rot = (nullableRot == null) ? t.rotation : nullableRot.Value; + scale = (nullableScale == null) ? t.localScale : nullableScale.Value; + } + } + +} \ 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..29a1e2b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Extension/Transforms.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 3d311fc1bf09b9e4fbc5a17a9c50ab0d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Extension/Transforms.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs b/Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs new file mode 100644 index 0000000..e803d69 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs @@ -0,0 +1,162 @@ +using System; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Object.Prediction +{ + [Obsolete("This class will be removed in version 5.")] + internal class LocalTransformTickSmoother : IResettable + { + #region Private. + /// + /// Object to smooth. + /// + private Transform _graphicalObject; + /// + /// When not MoveRates.UNSET_VALUE the graphical object will teleport into it's next position if the move distance exceeds this value. + /// + private float _teleportThreshold; + /// + /// How quickly to move towards goal values. + /// + private MoveRates _moveRates; + /// + /// True if a pretick occurred since last postTick. + /// + private bool _preTicked; + /// + /// Local values of the graphical during pretick. + /// + private TransformProperties _gfxInitializedLocalValues; + /// + /// World values of the graphical after it's been aligned to initialized values in PreTick. + /// + private TransformProperties _gfxPreSimulateWorldValues; + /// + /// TickDelta on the TimeManager. + /// + private float _tickDelta; + /// + /// How many ticks to interpolate over. + /// + private byte _interpolation; + #endregion + + /// + /// Initializes this smoother; should only be completed once. + /// + internal void InitializeOnce(Transform graphicalObject, float teleportDistance, float tickDelta, byte interpolation) + { + _gfxInitializedLocalValues = graphicalObject.GetLocalProperties(); + _tickDelta = tickDelta; + _graphicalObject = graphicalObject; + _teleportThreshold = (teleportDistance * (float)interpolation); + _interpolation = interpolation; + } + + /// + /// Called every frame. + /// + internal void Update() + { + if (!CanSmooth()) + return; + + MoveToTarget(); + } + + + /// + /// Called when the TimeManager invokes OnPreTick. + /// + internal void OnPreTick() + { + if (!CanSmooth()) + return; + + _preTicked = true; + _gfxPreSimulateWorldValues = _graphicalObject.GetWorldProperties(); + } + + /// + /// Called when TimeManager invokes OnPostTick. + /// + internal void OnPostTick() + { + if (!CanSmooth()) + return; + + //If preticked then previous transform values are known. + if (_preTicked) + { + _graphicalObject.SetWorldProperties(_gfxPreSimulateWorldValues); + SetMoveRates(_gfxInitializedLocalValues, _graphicalObject); + } + //If did not pretick then the only thing we can do is snap to instantiated values. + else + { + _graphicalObject.SetLocalProperties(_gfxInitializedLocalValues); + } + } + + /// + /// Returns if prediction can be used on this rigidbody. + /// + /// + private bool CanSmooth() + { + if (_graphicalObject == null) + return false; + + return true; + } + + /// + /// Sets Position and Rotation move rates to reach Target datas. + /// + private void SetMoveRates(TransformProperties prevValues, Transform t) + { + float duration = (_tickDelta * (float)_interpolation); + /* If interpolation is 1 then add on a tiny amount + * of more time to compensate for frame time, so that + * the smoothing does not complete before the next tick, + * as this would result in jitter. */ + if (_interpolation == 1) + duration += Mathf.Max(Time.deltaTime, (1f / 50f)); + float teleportT = _teleportThreshold; + _moveRates = MoveRates.GetLocalMoveRates(prevValues, t, duration, teleportT); + } + + + /// + /// Moves transform to target values. + /// + + private void MoveToTarget() + { + _moveRates.Move(_graphicalObject, _gfxInitializedLocalValues, Time.deltaTime, useWorldSpace: false); + } + + public void ResetState() + { + if (_graphicalObject != null) + { + _graphicalObject.SetLocalProperties(_gfxInitializedLocalValues); + _graphicalObject = null; + } + _teleportThreshold = default; + _moveRates = default; + _preTicked = default; + _gfxInitializedLocalValues = default; + _gfxPreSimulateWorldValues = default; + _tickDelta = default; + _interpolation = default; + } + + public void InitializeState() { } + } + + +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs.meta b/Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs.meta new file mode 100644 index 0000000..43dc069 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: facff21f41a83964fb8c0dc195d94646 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/LocalTransformTickSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Performance.meta b/Assets/FishNet/Runtime/Utility/Performance.meta new file mode 100644 index 0000000..35c150b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7b315d9102b18b541b0b2346c9ad63ed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs b/Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs new file mode 100644 index 0000000..d3b04c9 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs @@ -0,0 +1,231 @@ +//using System; //Remove on V5 + +//namespace FishNet.Utility.Performance +//{ + +// /// +// /// Unity 2022 has a bug where codegen will not compile when referencing a Queue type, +// /// while also targeting .Net as the framework API. +// /// As a work around this class is used for queues instead. +// /// +// public class BasicQueue +// { +// /// +// /// Maximum size of the collection. +// /// +// public int Capacity => Collection.Length; +// /// +// /// Number of elements in the queue. +// /// +// public int Count => _written; +// /// +// /// Collection containing data. +// /// +// private T[] Collection = new T[4]; +// /// +// /// Current write index of the collection. +// /// +// public int WriteIndex { get; private set; } +// /// +// /// Buffer for resizing. +// /// +// private T[] _resizeBuffer = new T[0]; +// /// +// /// Read position of the next Dequeue. +// /// +// private int _read; + +// /// +// /// Length of the queue. +// /// +// private int _written; + +// /// +// /// Enqueues an entry. +// /// +// /// +// public void Enqueue(T data) +// { +// if (_written == Collection.Length) +// Resize(); + +// if (WriteIndex >= Collection.Length) +// WriteIndex = 0; +// Collection[WriteIndex] = data; + +// WriteIndex++; +// _written++; +// } + +// /// +// /// Tries to dequeue the next entry. +// /// +// /// Dequeued entry. +// /// True if an entry existed to dequeue. +// public bool TryDequeue(out T result) +// { +// if (_written == 0) +// { +// result = default; +// return false; +// } + +// result = Dequeue(); +// return true; +// } + +// /// +// /// Dequeues the next entry. +// /// +// /// +// public T Dequeue() +// { +// if (_written == 0) +// throw new Exception($"Queue of type {typeof(T).Name} is empty."); + +// T result = Collection[_read]; + +// _written--; +// _read++; +// if (_read >= Collection.Length) +// _read = 0; + +// return result; +// } + +// /// +// /// Tries to peek the next entry. +// /// +// /// Peeked entry. +// /// True if an entry existed to peek. +// public bool TryPeek(out T result) +// { +// if (_written == 0) +// { +// result = default; +// return false; +// } + +// result = Peek(); +// return true; +// } + +// /// +// /// Peeks the next queue entry. +// /// +// /// +// public T Peek() +// { +// if (_written == 0) +// throw new Exception($"Queue of type {typeof(T).Name} is empty."); + +// return Collection[_read]; +// } + +// /// +// /// Clears the queue. +// /// +// public void Clear() +// { +// _read = 0; +// WriteIndex = 0; +// _written = 0; + +// DefaultCollection(Collection); +// DefaultCollection(_resizeBuffer); + +// void DefaultCollection(T[] array) +// { +// int count = array.Length; +// for (int i = 0; i < count; i++) +// array[i] = default; +// } +// } + +// /// +// /// Doubles the queue size. +// /// +// private void Resize() +// { +// int length = _written; +// int doubleLength = (length * 2); +// int read = _read; + +// /* Make sure copy array is the same size as current +// * and copy contents into it. */ +// //Ensure large enough to fit contents. +// T[] resizeBuffer = _resizeBuffer; +// if (resizeBuffer.Length < doubleLength) +// Array.Resize(ref resizeBuffer, doubleLength); +// //Copy from the read of queue first. +// int copyLength = (length - read); +// Array.Copy(Collection, read, resizeBuffer, 0, copyLength); +// /* If read index was higher than 0 +// * then copy remaining data as well from 0. */ +// if (read > 0) +// Array.Copy(Collection, 0, resizeBuffer, copyLength, read); + +// //Set _array to resize. +// Collection = resizeBuffer; +// //Reset positions. +// _read = 0; +// WriteIndex = length; +// } + +// /// +// /// 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 = GetRealIndex(simulatedIndex); +// return Collection[offset]; +// } +// set +// { +// int offset = GetRealIndex(simulatedIndex); +// Collection[offset] = value; +// } +// } + + + +// /// +// /// Returns the real index of the collection using a simulated index. +// /// +// /// True to allow an index be returned from an unused portion of the buffer so long as it is within bounds. +// private int GetRealIndex(int simulatedIndex, bool allowUnusedBuffer = false) +// { +// if (simulatedIndex >= Capacity) +// { +// return ReturnError(); +// } +// else +// { +// int written = _written; +// //May be out of bounds if allowUnusedBuffer is false. +// if (simulatedIndex >= written) +// { +// if (!allowUnusedBuffer) +// return ReturnError(); +// } +// int offset = (Capacity - written) + simulatedIndex + WriteIndex; +// if (offset >= Capacity) +// offset -= Capacity; + +// return offset; +// } + +// int ReturnError() +// { +// UnityEngine.Debug.LogError($"Index {simulatedIndex} is out of range. Collection count is {_written}, Capacity is {Capacity}"); +// return -1; +// } +// } + +// } + +//} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs.meta new file mode 100644 index 0000000..1c8ce79 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7c0a46ad871780b45bfe317c8f904322 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Performance/BasicQueue.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs b/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs new file mode 100644 index 0000000..dc178dd --- /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(); + + /// + /// 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..dacaaaa --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: c7620a5e6fedc18408f8f04821b35bbd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Performance/ByteArrayPool.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs new file mode 100644 index 0000000..74f5dcf --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs @@ -0,0 +1,321 @@ +using FishNet.Managing; +using FishNet.Managing.Object; +using FishNet.Object; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + public class DefaultObjectPool : ObjectPool + { + #region Public. + /// + /// Cache for pooled NetworkObjects. + /// Key: CollectionId. + /// + public IReadOnlyList>> Cache => _cache; + private List>> _cache = new(); + #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 + +#pragma warning disable CS0672 // Member overrides obsolete member + public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, Transform parent = null, Vector3? nullablePosition = null, Quaternion? nullableRotation = null, Vector3? nullableScale = null, bool makeActive = true, bool asServer = true) +#pragma warning restore CS0672 // Member overrides obsolete member + { + ObjectPoolRetrieveOption options = ObjectPoolRetrieveOption.Unset; + if (makeActive) + options |= ObjectPoolRetrieveOption.MakeActive; + + return RetrieveObject(prefabId, collectionId, options, parent, nullablePosition, nullableRotation, nullableScale, 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 object to return. + /// True if being called on the server side. + /// + public override NetworkObject RetrieveObject(int prefabId, ushort collectionId, ObjectPoolRetrieveOption options, Transform parent = null, Vector3? nullablePosition = null, Quaternion? nullableRotation = null, Vector3? nullableScale = null, bool asServer = true) + { + bool makeActive = options.FastContains(ObjectPoolRetrieveOption.MakeActive); + bool localSpace = options.FastContains(ObjectPoolRetrieveOption.LocalSpace); + + if (!_enabled) + return GetFromInstantiate(); + + Stack cache = GetCache(collectionId, prefabId, createIfMissing: true); + NetworkObject nob = null; + + //Iterate until nob is populated just in case cache entries have been destroyed. + while (nob == null) + { + if (cache.TryPop(out nob)) + { + if (nob != null) + { + nob.transform.SetParent(parent); + if (localSpace) + nob.transform.SetLocalPositionRotationAndScale(nullablePosition, nullableRotation, nullableScale); + else + nob.transform.SetWorldPositionRotationAndScale(nullablePosition, nullableRotation, nullableScale); + + if (makeActive) + nob.gameObject.SetActive(true); + + return nob; + } + } + //Nothing left in cache. + else + { + break; + } + } + + //Fall through, nothing in cache. + return GetFromInstantiate(); + + //Returns a network object via instantation. + NetworkObject GetFromInstantiate() + { + NetworkObject prefab = GetPrefab(prefabId, collectionId, asServer); + if (prefab == null) + { + return null; + } + else + { + NetworkObject result; + Vector3 scale; + + if (localSpace) + { + prefab.transform.OutLocalPropertyValues(nullablePosition, nullableRotation, nullableScale, out Vector3 pos, out Quaternion rot, out scale); + if (parent != null) + { + //Convert pos and rot to world values for the instantiate. + pos = parent.TransformPoint(pos); + rot = (parent.rotation * rot); + } + result = Instantiate(prefab, pos, rot, parent); + } + else + { + prefab.transform.OutWorldPropertyValues(nullablePosition, nullableRotation, nullableScale, out Vector3 pos, out Quaternion rot, out scale); + result = Instantiate(prefab, pos, rot, parent); + } + + result.transform.localScale = scale; + + if (makeActive) + result.gameObject.SetActive(true); + return result; + } + } + } + + /// + /// Returns a prefab for prefab and collectionId. + /// + public override NetworkObject GetPrefab(int prefabId, ushort collectionId, bool asServer) + { + PrefabObjects po = base.NetworkManager.GetPrefabObjects(collectionId, false); + return po.GetObject(asServer, prefabId); + } + + /// + /// Stores an object into the pool. + /// + /// Object to store. + /// True if being called on the server side. + /// + public override void StoreObject(NetworkObject instantiated, bool asServer) + { + //Pooling is not enabled. + if (!_enabled) + { + Destroy(instantiated.gameObject); + return; + } + + //Get all children as well and reset state on them. + List nestedNobs = instantiated.GetNetworkObjects(GetNetworkObjectOption.All); + + foreach (NetworkObject nob in nestedNobs) + nob.ResetState(asServer); + + CollectionCaches.Store(nestedNobs); + + //Set root inactive. + instantiated.gameObject.SetActive(false); + + Stack cache = GetCache(instantiated.SpawnableCollectionId, instantiated.PrefabId, createIfMissing: true); + 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. +#pragma warning disable CS0672 // Member overrides obsolete member + public override void CacheObjects(NetworkObject prefab, int count, bool asServer) => StorePrefabObjects(prefab, count, asServer); +#pragma warning restore CS0672 // Member overrides obsolete member + + /// + /// 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. + /// Prefabs instantiated and added to cache. + public override List StorePrefabObjects(NetworkObject prefab, int count, bool asServer) + { + if (!_enabled) + return null; + if (count <= 0) + return null; + if (prefab == null) + return null; + if (prefab.PrefabId == NetworkObject.UNSET_PREFABID_VALUE) + { + NetworkManagerExtensions.LogError($"Pefab {prefab.name} has an invalid prefabId and cannot be cached."); + return null; + } + + List added = new(); + Stack cache = GetCache(prefab.SpawnableCollectionId, prefab.PrefabId, createIfMissing: true); + + for (int i = 0; i < count; i++) + { + NetworkObject nob = Instantiate(prefab); + nob.gameObject.SetActive(false); + cache.Push(nob); + added.Add(nob); + } + + return added; + } + + /// + /// Clears pooled objects for a specific NetworkObject. + /// + /// Prefab or Instantiated NetworkObject to clear pool for. + /// This will clear the entire pool for the specified object. + public void ClearPool(NetworkObject nob) + { + if (!_enabled) + return; + if (nob == null) + return; + + int spawnableCollectionId = nob.SpawnableCollectionId; + Stack stack = GetCache(spawnableCollectionId, nob.PrefabId, createIfMissing: false); + if (stack == null) + return; + + DestroyStackNetworkObjectsAndClear(stack); + _cache[spawnableCollectionId].Clear(); + } + + /// + /// Clears all pooled objects. + /// + public void ClearPool() + { + int count = _cache.Count; + for (int i = 0; i < count; i++) + ClearPool(i); + } + + /// + /// Clears a pool destroying objects for a SpawnableCollectionId. + /// + /// CollectionId to clear for. + public void ClearPool(int spawnableCollectionId) + { + if (spawnableCollectionId >= _cacheCount) + return; + + Dictionary> dict = _cache[spawnableCollectionId]; + + foreach (Stack item in dict.Values) + DestroyStackNetworkObjectsAndClear(item); + + dict.Clear(); + } + + /// + /// Gets a cache for an id or creates one if does not exist. + /// + /// + public Stack GetCache(int collectionId, int prefabId, bool createIfMissing) + { + if (collectionId >= _cacheCount) + { + //Do not create if missing. + if (!createIfMissing) + return null; + + //Add more to the cache. + while (_cache.Count <= collectionId) + { + Dictionary> dict = new(); + _cache.Add(dict); + } + _cacheCount = _cache.Count; + } + + Dictionary> dictionary = _cache[collectionId]; + //No cache for prefabId yet, make one. + if (!dictionary.TryGetValueIL2CPP(prefabId, out Stack cache)) + { + if (createIfMissing) + { + cache = new(); + dictionary[prefabId] = cache; + } + } + + return cache; + } + + [Obsolete("Use GetCache(int, int, bool)")] + public Stack GetOrCreateCache(int collectionId, int prefabId) => GetCache(collectionId, prefabId, createIfMissing: true); + + /// + /// Destroys all NetworkObjects within a stack and clears the stack. + /// + private void DestroyStackNetworkObjectsAndClear(Stack stack) + { + foreach (NetworkObject networkObject in stack) + { + if (networkObject != null) + Destroy(networkObject.gameObject); + } + + stack.Clear(); + } + } +} \ 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..2d624e3 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: cb13d174096685549b1d6a94d726ff7d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Performance/DefaultObjectPool.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs new file mode 100644 index 0000000..34f4599 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs @@ -0,0 +1,85 @@ +using FishNet.Managing; +using FishNet.Managing.Object; +using FishNet.Object; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + public abstract class ObjectPool : MonoBehaviour + { + /// + /// NetworkManager this ObjectPool belongs to. + /// + protected NetworkManager NetworkManager { get; private set; } + + /// + /// Called at the end of every frame. This can be used to perform routine tasks. + /// + public virtual void LateUpdate() { } + /// + /// Initializes this script for use. + /// + public virtual void InitializeOnce(NetworkManager nm) + { + NetworkManager = nm; + } + /// + /// 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 object to return. + /// True if being called on the server side. + /// + [Obsolete("Use RetrieveObject(int, ushort, RetrieveOption, parent, Vector3?, Quaternion? Vector3?, bool) instead.")] //Remove in V5 + public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, Transform parent = null, Vector3? position = null, Quaternion? rotation = null, Vector3? scale = null, bool makeActive = true, bool asServer = true) => null; + /// + /// 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 object to return. + /// True if being called on the server side. + /// + public virtual NetworkObject RetrieveObject(int prefabId, ushort collectionId, ObjectPoolRetrieveOption options, Transform parent = null, Vector3? position = null, Quaternion? rotation = null, Vector3? scale = null, bool asServer = true) => null; + /// + /// Returns a prefab using specified values. + /// + /// PrefabId of the object to return. + /// CollectionId of the object to return. + /// True if being called on the server side. + /// + public virtual NetworkObject GetPrefab(int prefabId, ushort collectionId, bool asServer) + { + PrefabObjects po = NetworkManager.GetPrefabObjects(collectionId, false); + return po.GetObject(asServer, prefabId); + } + /// + /// 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); + /// + /// 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. + [Obsolete("Use AddPrefabObjects.")] + public virtual void CacheObjects(NetworkObject prefab, int count, bool asServer) { } + + /// + /// 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. + /// Prefabs instantiated and added to cache. + public virtual List StorePrefabObjects(NetworkObject prefab, int count, bool asServer) => default; + + + } + +} \ 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..b744d0c --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 6ec7d855ffa7afc45b619b84ddbda27c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Performance/ObjectPool.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs b/Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs new file mode 100644 index 0000000..bc34815 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs @@ -0,0 +1,25 @@ + +namespace FishNet.Utility.Performance +{ + + public static class RetrieveOptionExtensions + { + public static bool FastContains(this ObjectPoolRetrieveOption whole, ObjectPoolRetrieveOption part) => (whole & part) == part; + } + + [System.Flags] + public enum ObjectPoolRetrieveOption + { + Unset = 0, + /// + /// True to make the object active before returning. + /// + MakeActive = 1, + /// + /// True to treat supplied transform properties as local space. + /// False will treat the properties as world space. + /// + LocalSpace = 2, + } +} + diff --git a/Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs.meta b/Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs.meta new file mode 100644 index 0000000..8d4a26d --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 30dbc4b626ddb7844aac0189cc4c7487 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Performance/ObjectPoolRetrieveOption.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs new file mode 100644 index 0000000..1aa8a37 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs @@ -0,0 +1,28 @@ +using FishNet.Object; +using System.Collections.Generic; +using UnityEngine; + +namespace FishNet.Utility.Performance +{ + + public static class GetNonAlloc + { + /// + /// Gets all NetworkBehaviours on a transform. + /// + public static void GetNetworkBehavioursNonAlloc(this Transform t, ref List results) + { + t.GetComponents(results); + } + + /// + /// Gets all transforms on transform and it's children. + /// + public static void GetTransformsInChildrenNonAlloc(this Transform t, ref List results, bool includeInactive = false) + { + t.GetComponentsInChildren(includeInactive, results); + } + + } + +} \ 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..d5ba807 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Performance/Transforms.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 7d0740f919077254c8ffb131b9587407 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Performance/Transforms.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/SnappedAxes.cs b/Assets/FishNet/Runtime/Utility/SnappedAxes.cs new file mode 100644 index 0000000..7233dae --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/SnappedAxes.cs @@ -0,0 +1,25 @@ +using System; +using GameKit.Dependencies.Utilities; + +namespace FishNet.Component.Transforming +{ + /// + /// Axes to snap of properties. + /// + [Flags] + public enum SnappedAxes : uint + { + Unset = 0, + X = (1 << 0), + Y = (1 << 1), + Z = (1 << 2), + Everything = Enums.SHIFT_EVERYTHING_UINT, + } + + + public static class SnappedAxesExtensions + { + public static bool FastContains(this SnappedAxes whole, SnappedAxes part) => (whole & part) == part; + } + +} diff --git a/Assets/FishNet/Runtime/Utility/SnappedAxes.cs.meta b/Assets/FishNet/Runtime/Utility/SnappedAxes.cs.meta new file mode 100644 index 0000000..5ccd891 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/SnappedAxes.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 0431c1ba9e0caa147aeccf016ceca606 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/SnappedAxes.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/Template.meta b/Assets/FishNet/Runtime/Utility/Template.meta new file mode 100644 index 0000000..3055629 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Template.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f0c2d33d2e9aea44195c265bd08ff498 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs b/Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs new file mode 100644 index 0000000..76cc605 --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs @@ -0,0 +1,117 @@ +using FishNet.Managing.Timing; +using FishNet.Object; +using GameKit.Dependencies.Utilities; +using UnityEngine; + +namespace FishNet.Utility.Template +{ + /// + /// Subscribes to tick events making them available as virtual methods. + /// + public abstract class TickNetworkBehaviour : NetworkBehaviour + { + #region Types. + [System.Flags] + [System.Serializable] + public enum TickCallback : uint + { + None = 0, + PreTick = (1 << 0), + Tick = (1 << 1), + PostTick = (1 << 2), + Update = (1 << 3), + LateUpdate = (1 << 4), + Everything = Enums.SHIFT_EVERYTHING_UINT, + } + #endregion + + /// + /// Tick callbacks to use. + /// + [Tooltip("Tick callbacks to use.")] + [SerializeField] + private TickCallback _tickCallbacks = (TickCallback.Tick | TickCallback.PostTick); + + /// + /// Last subscription state. + /// + private bool _subscribed; + /// + /// TimeManager subscribed to. + /// + private TimeManager _timeManager; + + internal override void OnStartNetwork_Internal() + { + _timeManager = base.TimeManager; + ChangeSubscriptions(subscribe: true); + + base.OnStartNetwork_Internal(); + } + + internal override void OnStopNetwork_Internal() + { + ChangeSubscriptions(subscribe: false); + + base.OnStopNetwork_Internal(); + } + + /// + /// Updates callbacks to use and changes subscriptions accordingly. + /// + /// Next value. + public void SetTickCallbacks(TickCallback value) + { + ChangeSubscriptions(subscribe: false); + _tickCallbacks = value; + if (value != TickCallback.None) + ChangeSubscriptions(subscribe: true); + } + + private void ChangeSubscriptions(bool subscribe) + { + TimeManager tm = _timeManager; + + if (tm == null) + return; + if (subscribe == _subscribed) + return; + _subscribed = subscribe; + + if (subscribe) + { + if (TickCallbackFastContains(_tickCallbacks, TickCallback.PreTick)) + tm.OnPreTick += TimeManager_OnPreTick; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.Tick)) + tm.OnTick += TimeManager_OnTick; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.PostTick)) + tm.OnPostTick += TimeManager_OnPostTick; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.Update)) + tm.OnUpdate += TimeManager_OnUpdate; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.LateUpdate)) + tm.OnLateUpdate += TimeManager_OnLateUpdate; + } + else + { + if (TickCallbackFastContains(_tickCallbacks, TickCallback.PreTick)) + tm.OnPreTick -= TimeManager_OnPreTick; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.Tick)) + tm.OnTick -= TimeManager_OnTick; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.PostTick)) + tm.OnPostTick -= TimeManager_OnPostTick; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.Update)) + tm.OnUpdate -= TimeManager_OnUpdate; + if (TickCallbackFastContains(_tickCallbacks, TickCallback.LateUpdate)) + tm.OnLateUpdate -= TimeManager_OnLateUpdate; + } + } + + protected virtual void TimeManager_OnPreTick() { } + protected virtual void TimeManager_OnTick() { } + protected virtual void TimeManager_OnPostTick() { } + protected virtual void TimeManager_OnUpdate() { } + protected virtual void TimeManager_OnLateUpdate() { } + + private bool TickCallbackFastContains(TickCallback whole, TickCallback part) => ((whole & part) == part); + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs.meta b/Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs.meta new file mode 100644 index 0000000..18eadfd --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 68553251b6087a342a59efd0b6433401 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/Template/TickNetworkBehaviour.cs + uploadId: 762203 diff --git a/Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs b/Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs new file mode 100644 index 0000000..3fdc3fe --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs @@ -0,0 +1,691 @@ +using System; +using FishNet.Managing; +using FishNet.Managing.Timing; +using FishNet.Object; +using FishNet.Object.Prediction; +using FishNet.Utility.Extension; +using GameKit.Dependencies.Utilities; +using UnityEngine; +using UnityEngine.Scripting; + +namespace FishNet.Component.Transforming +{ + /// + /// This class is under regular development and it's API may change at any time. + /// + [Obsolete("This class will be removed in version 5.")] + public sealed class TransformTickSmoother : IResettable + { + #region Types. + private enum InitializeType + { + /// + /// Not initialized. + /// + Unset, + /// + /// Initialized for network use. + /// + Networked, + /// + /// Initialized for non-network use. + /// + NonNetworked, + } + + [Preserve] + private struct TickTransformProperties + { + public uint Tick; + public TransformProperties Properties; + + public TickTransformProperties(uint tick, Transform t) + { + Tick = tick; + Properties = new(t.localPosition, t.localRotation, t.localScale); + } + + public TickTransformProperties(uint tick, Transform t, Vector3 localScale) + { + Tick = tick; + Properties = new(t.localPosition, t.localRotation, localScale); + } + + public TickTransformProperties(uint tick, TransformProperties tp) + { + Tick = tick; + Properties = tp; + } + + public TickTransformProperties(uint tick, TransformProperties tp, Vector3 localScale) + { + Tick = tick; + tp.Scale = localScale; + Properties = tp; + } + } + #endregion + + #region Private. + /// + /// Object to smooth. + /// + private Transform _graphicalObject; + /// + /// When not MoveRates.UNSET_VALUE the graphical object will teleport into it's next position if the move distance exceeds this value. + /// + private float _teleportThreshold; + /// + /// How quickly to move towards goal values. + /// + private MoveRates _moveRates = new(MoveRates.UNSET_VALUE); + /// + /// True if a pretick occurred since last postTick. + /// + private bool _preTicked; + /// + /// World offset values of the graphical from the NetworkObject during initialization. + /// + private TransformProperties _gfxInitializedOffsetValues; + /// + /// World values of the graphical after it's been aligned to initialized values in PreTick. + /// + private TransformProperties _gfxPreSimulateWorldValues; + /// + /// TickDelta on the TimeManager. + /// + private float _tickDelta; + /// + /// How many ticks to interpolate over when not using adaptive. + /// + private byte _ownerInterpolation; + /// + /// Current interpolation, regardless of if using adaptive or not. + /// + private byte _interpolation; + /// + /// NetworkObject this is for. + /// + private NetworkObject _networkObject; + /// + /// Value to multiply movement by. This is used to reduce or increase the rate the movement buffer is consumed. + /// + private float _movementMultiplier = 1f; + /// + /// TransformProperties to move towards. + /// + private BasicQueue _transformProperties; + /// + /// Which properties to smooth. + /// + private TransformPropertiesFlag _ownerSmoothedProperties; + /// + /// Which properties to smooth. + /// + private TransformPropertiesFlag _spectatorSmoothedProperties; + + /// + /// Updates the smoothedProperties value. + /// + /// New value. + /// True if updating values for the spectator, false if updating for owner. + public void SetSmoothedProperties(TransformPropertiesFlag value, bool forSpectator) + { + if (forSpectator) + _spectatorSmoothedProperties = value; + else + _ownerSmoothedProperties = value; + } + + /// + /// Amount of adaptive interpolation to use. + /// + private AdaptiveInterpolationType _adaptiveInterpolation = AdaptiveInterpolationType.VeryLow; + + /// + /// Updates the adaptiveInterpolation value. + /// + /// New value. + public void SetAdaptiveInterpolation(AdaptiveInterpolationType adaptiveInterpolation) + { + _adaptiveInterpolation = adaptiveInterpolation; + } + + /// + /// Set interpolation to use for spectated objects if adaptiveInterpolation is off. + /// + private byte _spectatorInterpolation; + + /// + /// Sets the spectator interpolation value. + /// + /// New value. + /// True to also disable adaptive interpolation to use this new value. + public void SetSpectatorInterpolation(byte value, bool disableAdaptiveInterpolation = true) + { + _spectatorInterpolation = value; + if (disableAdaptiveInterpolation) + _adaptiveInterpolation = AdaptiveInterpolationType.Off; + } + + /// + /// Previous parent the graphical was attached to. + /// + private Transform _previousParent; + /// + /// True if to detach at runtime. + /// + private bool _detach; + /// + /// True if were an owner of the NetworkObject during PreTick. + /// This is only used for performance gains. + /// + private bool _useOwnerSmoothing; + /// + /// True if Initialized has been called and settings have not been reset. + /// + private InitializeType _initializeType = InitializeType.Unset; + /// + /// Last tick this was teleported on. + /// + private uint _teleportedTick = TimeManager.UNSET_TICK; + /// + /// Last local tick a reconcile callback was received. + /// + private uint _lastReconcileTick = TimeManager.UNSET_TICK; + /// + /// Transform to track movement on. + /// + private Transform _rootTransform; + /// + /// Frame which smoothing can start. + /// + private int _startFrame; + #endregion + + #region Const. + /// + /// Default expected interval for reconciles. + /// + private const int RECONCILE_INTERVAL_DEFAULT = int.MaxValue; + /// + /// Maximum allowed entries to be queued over the interpolation amount. + /// + private const int MAXIMUM_QUEUED_OVER_INTERPOLATION = 3; + #endregion + + [Preserve] + public TransformTickSmoother() { } + + ~TransformTickSmoother() + { + //This is a last resort for if something didnt deinitialize right. + ResetState(); + } + + /// + /// Initializes this smoother for owner and spectator. This should only occur once. + /// + public void InitializeNetworked(NetworkObject nob, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, byte spectatorInterpolation, TransformPropertiesFlag specatorSmoothedProperties, AdaptiveInterpolationType adaptiveInterpolation) + { + ResetState(); + + _networkObject = nob; + _spectatorInterpolation = spectatorInterpolation; + _spectatorSmoothedProperties = specatorSmoothedProperties; + + Initialize_Internal(nob.transform, graphicalObject, detach, teleportDistance, tickDelta, ownerInterpolation, ownerSmoothedProperties, forNetworked: true); + + SetAdaptiveInterpolation(adaptiveInterpolation); + UpdateInterpolation(0); + } + + private void Initialize_Internal(Transform rootTransform, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties, bool forNetworked) + { + _rootTransform = rootTransform; + _detach = detach; + _transformProperties = CollectionCaches.RetrieveBasicQueue(); + _gfxInitializedOffsetValues = rootTransform.GetTransformOffsets(graphicalObject); + _tickDelta = tickDelta; + _graphicalObject = graphicalObject; + _teleportThreshold = teleportDistance; + _ownerInterpolation = ownerInterpolation; + + _ownerSmoothedProperties = ownerSmoothedProperties; + _initializeType = (forNetworked) ? InitializeType.Networked : InitializeType.NonNetworked; + } + + /// + /// Initializes this smoother non-networked use. + /// + public void Initialize(Transform rootTransform, Transform graphicalObject, bool detach, float teleportDistance, float tickDelta, byte ownerInterpolation, TransformPropertiesFlag ownerSmoothedProperties) + { + ResetState(); + + Initialize_Internal(rootTransform, graphicalObject, detach, teleportDistance, tickDelta, ownerInterpolation, ownerSmoothedProperties, false); + + SetAdaptiveInterpolation(AdaptiveInterpolationType.Off); + } + + /// + /// Deinitializes this smoother resetting values. + /// + public void Deinitialize() + { + ResetState(); + } + + /// + /// Updates interpolation based on localClient latency. + /// + private void UpdateInterpolation(uint clientStateTick) + { + /* Use owner interpolation if: + * - Owner. + * - Server, since server always interpolates over 1 tick; this only applies as clientHost. + * - Not networked, use specified 'owner' interpolation. + */ + if (_initializeType == InitializeType.NonNetworked || _networkObject.IsServerInitialized || _networkObject.Owner.IsLocalClient) + { + _interpolation = _ownerInterpolation; + } + //Not using owner interpolation. + else + { + if (_adaptiveInterpolation == AdaptiveInterpolationType.Off) + { + _interpolation = _spectatorInterpolation; + } + else + { + float interpolation; + TimeManager tm = _networkObject.TimeManager; + if (clientStateTick == 0) + { + //Not enough data to calculate; guestimate. This should only happen once. + float fRtt = (float)tm.RoundTripTime; + interpolation = (fRtt / 10f); + } + else + { + interpolation = (tm.LocalTick - clientStateTick); + } + + interpolation *= GetInterpolationMultiplier(); + interpolation = Mathf.Clamp(interpolation, 2f, (float)byte.MaxValue); + _interpolation = (byte)Mathf.CeilToInt(interpolation); + + float GetInterpolationMultiplier() + { + switch (_adaptiveInterpolation) + { + case AdaptiveInterpolationType.ExtremelyLow: + return 0.2f; + case AdaptiveInterpolationType.VeryLow: + return 0.45f; + case AdaptiveInterpolationType.Low: + return 0.8f; + case AdaptiveInterpolationType.Moderate: + return 1.05f; + case AdaptiveInterpolationType.High: + return 1.25f; + case AdaptiveInterpolationType.VeryHigh: + return 1.5f; + //Make no changes for maximum. + default: + _networkObject.NetworkManager.LogError($"AdaptiveInterpolationType {_adaptiveInterpolation} is unhandled."); + return 1f; + } + } + } + } + } + + internal void OnStartClient() + { + if (!_detach) + return; + + _previousParent = _graphicalObject.parent; + TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties(); + _graphicalObject.SetParent(null); + _graphicalObject.SetWorldProperties(gfxWorldProperties); + } + + internal void OnStopClient() + { + if (!_detach || _previousParent == null || _graphicalObject == null) + return; + + _graphicalObject.SetParent(_previousParent); + _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); + } + + /// + /// Called every frame. + /// + internal void OnUpdate() + { + if (!CanSmooth()) + return; + + MoveToTarget(Time.deltaTime); + } + + /// + /// Called when the TimeManager invokes OnPreTick. + /// + public void OnPreTick() + { + if (!CanSmooth()) + return; + + _preTicked = true; + _useOwnerSmoothing = (_networkObject == null || _networkObject.IsOwner); + + DiscardExcessiveTransformPropertiesQueue(); + + //These only need to be set if still attached. + if (!_detach) + _gfxPreSimulateWorldValues = _graphicalObject.GetWorldProperties(); + } + + /// + /// Called when the PredictionManager invokes OnPreReconcile. + /// + public void OnPreReconcile() + { + if (!_networkObject.IsObjectReconciling) + return; + if (_networkObject.IsOwner || _adaptiveInterpolation == AdaptiveInterpolationType.Off) + return; + + uint clientStateTick = _networkObject.PredictionManager.ClientStateTick; + _lastReconcileTick = clientStateTick; + + UpdateInterpolation(clientStateTick); + } + + /// + /// Called when the TimeManager invokes OnPostReplay. + /// + /// Replay tick for the local client. + public void OnPostReplicateReplay(uint clientTick) + { + if (_networkObject.IsOwner || _adaptiveInterpolation == AdaptiveInterpolationType.Off) + return; + if (_transformProperties.Count == 0) + return; + if (clientTick <= _teleportedTick) + return; + uint firstTick = _transformProperties.Peek().Tick; + //Already in motion to first entry, or first entry passed tick. + if (clientTick <= firstTick) + return; + + ModifyTransformProperties(clientTick, firstTick); + } + + /// + /// Called when TimeManager invokes OnPostTick. + /// + /// Local tick of the client. + public void OnPostTick(uint clientTick) + { + if (!CanSmooth()) + return; + if (clientTick <= _teleportedTick) + return; + + //If preticked then previous transform values are known. + if (_preTicked) + { + DiscardExcessiveTransformPropertiesQueue(); + + //Only needs to be put to pretick position if not detached. + if (!_detach) + _graphicalObject.SetWorldProperties(_gfxPreSimulateWorldValues); + + AddTransformProperties(clientTick); + } + //If did not pretick then the only thing we can do is snap to instantiated values. + else + { + //Only set to position if not to detach. + if (!_detach) + _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); + } + } + + /// + /// Teleports the graphical to it's starting position and clears the internal movement queue. + /// + public void Teleport() + { + if (_networkObject == null) + return; + _teleportedTick = _networkObject.TimeManager.LocalTick; + ClearTransformPropertiesQueue(); + TransformProperties startProperties = _networkObject.transform.GetWorldProperties(); + startProperties.Add(_gfxInitializedOffsetValues); + _graphicalObject.SetWorldProperties(startProperties); + } + + /// + /// Clears the pending movement queue. + /// + private void ClearTransformPropertiesQueue() + { + _transformProperties.Clear(); + //Also unset move rates since there is no more queue. + _moveRates = new(MoveRates.UNSET_VALUE); + } + + /// + /// Discards datas over interpolation limit from movement queue. + /// + private void DiscardExcessiveTransformPropertiesQueue() + { + int propertiesCount = _transformProperties.Count; + int dequeueCount = (propertiesCount - (_interpolation + MAXIMUM_QUEUED_OVER_INTERPOLATION)); + //If there are entries to dequeue. + if (dequeueCount > 0) + { + TickTransformProperties tpp = default; + for (int i = 0; i < dequeueCount; i++) + tpp = _transformProperties.Dequeue(); + + SetMoveRates(tpp.Properties); + } + } + + /// + /// Adds a new transform properties and sets move rates if needed. + /// + private void AddTransformProperties(uint tick) + { + TickTransformProperties tpp = new(tick, GetNetworkObjectWorldPropertiesWithOffset()); + _transformProperties.Enqueue(tpp); + + //If first entry then set move rates. + if (_transformProperties.Count == 1) + { + TransformProperties gfxWorldProperties = _graphicalObject.GetWorldProperties(); + SetMoveRates(gfxWorldProperties); + _startFrame = Time.frameCount + 1; + } + } + + /// + /// Modifies a transform property for a tick. This does not error check for empty collections. + /// + /// First tick in the queue. If 0 this will be looked up. + private void ModifyTransformProperties(uint clientTick, uint firstTick) + { + uint tick = clientTick; + /*Ticks will always be added incremental by 1 so it's safe to jump ahead the difference + * of tick and firstTick. */ + int index = (int)(tick - firstTick); + //Replace with new data. + if (index < _transformProperties.Count) + { + if (tick != _transformProperties[index].Tick) + { + //Should not be possible. + } + else + { + _transformProperties[index] = new(tick, GetNetworkObjectWorldPropertiesWithOffset(), _graphicalObject.localScale); + } + } + else + { + //This should never happen. + } + } + + /// + /// Returns TransformProperties of the NetworkObject with the graphicals world offset. + /// + /// + private TransformProperties GetNetworkObjectWorldPropertiesWithOffset() => _networkObject.transform.GetWorldProperties(_gfxInitializedOffsetValues); + + /// + /// Returns if prediction can be used on this rigidbody. + /// + /// + private bool CanSmooth() + { + if (_graphicalObject == null) + return false; + if (_networkObject != null && _networkObject.EnablePrediction && !_networkObject.EnableStateForwarding && !_networkObject.IsController) + return false; + if (_networkObject.IsServerOnlyStarted) + return false; + + return true; + } + + /// + /// Sets new rates based on next entries in transformProperties queue, against a supplied TransformProperties. + /// + private void SetMoveRates(in TransformProperties prevValues) + { + if (_transformProperties.Count == 0) + { + _moveRates = new(MoveRates.UNSET_VALUE); + return; + } + + TransformProperties nextValues = _transformProperties.Peek().Properties; + float duration = _tickDelta; + float teleportT = _teleportThreshold; + + _moveRates = MoveRates.GetMoveRates(prevValues, nextValues, duration, teleportT); + _moveRates.TimeRemaining = duration; + + SetMovementMultiplier(); + } + + private void SetMovementMultiplier() + { + /* If there's more in queue than interpolation then begin to move faster based on overage. + * Move 5% faster for every overage. */ + int overInterpolation = (_transformProperties.Count - _interpolation); + //If needs to be adjusted. + if (overInterpolation != 0) + { + _movementMultiplier += (0.015f * overInterpolation); + } + //If does not need to be adjusted. + else + { + //If interpolation is 1 then slow down just barely to accomodate for frame delta variance. + if (_interpolation == 1) + _movementMultiplier = 1f; + } + + _movementMultiplier = Mathf.Clamp(_movementMultiplier, 0.95f, 1.05f); + } + + /// + /// Moves transform to target values. + /// + private void MoveToTarget(float delta) + { + if (Time.frameCount < _startFrame) + return; + int tpCount = _transformProperties.Count; + //No data. + if (tpCount == 0) + return; + /* If buffer is considerably under goal then halt + * movement. This will allow the buffer to grow. */ + if ((tpCount - _interpolation) < -4) + return; + + TickTransformProperties ttp = _transformProperties.Peek(); + TransformPropertiesFlag smoothedProperties = (_useOwnerSmoothing) ? _ownerSmoothedProperties : _spectatorSmoothedProperties; + _moveRates.Move(_graphicalObject, ttp.Properties, smoothedProperties, (delta * _movementMultiplier), useWorldSpace: true); + + float tRemaining = _moveRates.TimeRemaining; + //if TimeLeft is <= 0f then transform is at goal. Grab a new goal if possible. + if (tRemaining <= 0f) + { + //Dequeue current entry and if there's another call a move on it. + _transformProperties.Dequeue(); + + //If there are entries left then setup for the next. + if (_transformProperties.Count > 0) + { + SetMoveRates(ttp.Properties); + //If delta is negative then call move again with abs. + if (tRemaining < 0f) + MoveToTarget(Mathf.Abs(tRemaining)); + } + //No remaining, set to snap. + else + { + ClearTransformPropertiesQueue(); + } + } + } + + public void ResetState() + { + if (_initializeType == InitializeType.Unset) + return; + + if (_graphicalObject != null) + { + if (_rootTransform != null) + { + //Check isQuitting for UnityEditor fix //https://github.com/FirstGearGames/FishNet/issues/818 + if (_detach && !ApplicationState.IsQuitting()) + _graphicalObject.SetParent(_rootTransform); + _graphicalObject.SetWorldProperties(GetNetworkObjectWorldPropertiesWithOffset()); + _graphicalObject = null; + } + else if (_detach) + { + UnityEngine.Object.Destroy(_graphicalObject.gameObject); + } + } + + _networkObject = null; + _teleportedTick = TimeManager.UNSET_TICK; + _lastReconcileTick = TimeManager.UNSET_TICK; + _movementMultiplier = 1f; + CollectionCaches.StoreAndDefault(ref _transformProperties); + _teleportThreshold = default; + _moveRates = default; + _preTicked = default; + _gfxInitializedOffsetValues = default; + _gfxPreSimulateWorldValues = default; + _tickDelta = default; + _interpolation = default; + } + + public void InitializeState() { } + } +} \ No newline at end of file diff --git a/Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs.meta b/Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs.meta new file mode 100644 index 0000000..f47943b --- /dev/null +++ b/Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 5d265649320af034b9a4e966b0a341d4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Runtime/Utility/TransformTickSmoother.cs + uploadId: 762203 diff --git a/Assets/FishNet/THIRD PARTY NOTICE.md b/Assets/FishNet/THIRD PARTY NOTICE.md new file mode 100644 index 0000000..54856ca --- /dev/null +++ b/Assets/FishNet/THIRD PARTY NOTICE.md @@ -0,0 +1,113 @@ +This package contains third-party software components governed by the license(s) indicated below: + +Component Name: GameKit +License Type: BSD 2-Clause +Copyright (c) Benjamin Berwick + +Paths: FishNet\GameKit + +This software was designed for Fish-Networking on Unity https://github.com/FirstGearGames/FishNet + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +Component Name: LiteNetLib +License Type: MIT +Copyright (c) 2020 Ruslan Pyrch +Copyright (c) 2021, Benjamin Berwick + +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 + +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..5dd53ff --- /dev/null +++ b/Assets/FishNet/THIRD PARTY NOTICE.md.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 6ee8e3f1530d3594488bfe438dced5ea +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/THIRD PARTY NOTICE.md + uploadId: 762203 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/EdgegapMenu.cs b/Assets/FishNet/Upgrading/EdgegapMenu.cs new file mode 100644 index 0000000..3fb9311 --- /dev/null +++ b/Assets/FishNet/Upgrading/EdgegapMenu.cs @@ -0,0 +1,26 @@ +// #if !EDGEGAP_PLUGIN_SERVERS +// #if UNITY_EDITOR +// using FishNet.Documenting; +// using UnityEditor; +// using UnityEngine; +// +// namespace FishNet.Plugin +// { +// [APIExclude] +// public class EdgegapMenu : MonoBehaviour +// { +// +// /// +// /// Replaces all components. +// /// +// [MenuItem("Tools/Get Edgegap Hosting", false, 0)] +// private static void GetEdgegapHosting() +// { +// Application.OpenURL("https://firstgeargames.com/FishNet/Edgegap/"); +// } +// +// +// } +// } +// #endif +// #endif \ No newline at end of file diff --git a/Assets/FishNet/Upgrading/EdgegapMenu.cs.meta b/Assets/FishNet/Upgrading/EdgegapMenu.cs.meta new file mode 100644 index 0000000..5a2b0e0 --- /dev/null +++ b/Assets/FishNet/Upgrading/EdgegapMenu.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 935359f2f3838f44192b9ee79c003578 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Upgrading/EdgegapMenu.cs + uploadId: 762203 diff --git a/Assets/FishNet/Upgrading/MirrorUpgrade.cs b/Assets/FishNet/Upgrading/MirrorUpgrade.cs new file mode 100644 index 0000000..1431ba7 --- /dev/null +++ b/Assets/FishNet/Upgrading/MirrorUpgrade.cs @@ -0,0 +1,410 @@ +#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 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 (_sceneCondition == null) + return false; + +#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); + } + + return false; + } + + + + private bool IterateProximityChecker(GameObject go) + { + if (_distanceCondition == null) + return false; + +#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); + } + + 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..aab6561 --- /dev/null +++ b/Assets/FishNet/Upgrading/MirrorUpgrade.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 491b9891492df1444937419bc0e39642 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Upgrading/MirrorUpgrade.cs + uploadId: 762203 diff --git a/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs b/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs new file mode 100644 index 0000000..b7b4a61 --- /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("Tools/Fish-Networking/Utility/Upgrading/From Mirror/Replace Components", false, 1)] + 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("Tools/Fish-Networking/Utility/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(); + 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..39b5ae7 --- /dev/null +++ b/Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs.meta @@ -0,0 +1,18 @@ +fileFormatVersion: 2 +guid: 55cd1de3399bf564a9545f089421a88d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/Upgrading/UpgradeFromMirrorMenu.cs + uploadId: 762203 diff --git a/Assets/FishNet/package.json b/Assets/FishNet/package.json new file mode 100644 index 0000000..9b781da --- /dev/null +++ b/Assets/FishNet/package.json @@ -0,0 +1,37 @@ +{ + "name": "com.firstgeargames.fishnet", + "version": "4.6.9", + "displayName": "FishNet: Networking Evolved", + "description": "A feature-rich Unity networking solution aimed towards reliability, ease of use, efficiency, and flexibility.", + "unity": "2021.3", + "documentationUrl": "https://fish-networking.gitbook.io/docs/", + "licensesUrl": "https://github.com/FirstGearGames/FishNet/blob/main/LICENSE.md", + "license": "See LICENSE.txt file", + "keywords": [ + "FishNetworking", + "Networking", + "FishNet", + "Unity Networking", + "Bolt", + "Mirror", + "Photon", + "DarkRift", + "DedicatedServer", + "Fusion", + "Netcode" + ], + "author": { + "name": "FirstGearGames", + "url": "http://www.firstgeargames.com" + }, + "dependencies": { + "com.unity.nuget.newtonsoft-json": "3.2.1", + "com.unity.ugui": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.ui": "1.0.0" + } +} \ No newline at end of file diff --git a/Assets/FishNet/package.json.meta b/Assets/FishNet/package.json.meta new file mode 100644 index 0000000..8aced8c --- /dev/null +++ b/Assets/FishNet/package.json.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: b6cd22251db5121439ea8056c82bb541 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: +AssetOrigin: + serializedVersion: 1 + productId: 207815 + packageName: 'FishNet: Networking Evolved' + packageVersion: 4.6.9R + assetPath: Assets/FishNet/package.json + uploadId: 762203 diff --git a/Assets/InputSystem_Actions.inputactions b/Assets/InputSystem_Actions.inputactions new file mode 100644 index 0000000..1a12cb9 --- /dev/null +++ b/Assets/InputSystem_Actions.inputactions @@ -0,0 +1,1057 @@ +{ + "name": "InputSystem_Actions", + "maps": [ + { + "name": "Player", + "id": "df70fa95-8a34-4494-b137-73ab6b9c7d37", + "actions": [ + { + "name": "Move", + "type": "Value", + "id": "351f2ccd-1f9f-44bf-9bec-d62ac5c5f408", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Look", + "type": "Value", + "id": "6b444451-8a00-4d00-a97e-f47457f736a8", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Attack", + "type": "Button", + "id": "6c2ab1b8-8984-453a-af3d-a3c78ae1679a", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Interact", + "type": "Button", + "id": "852140f2-7766-474d-8707-702459ba45f3", + "expectedControlType": "Button", + "processors": "", + "interactions": "Hold", + "initialStateCheck": false + }, + { + "name": "Crouch", + "type": "Button", + "id": "27c5f898-bc57-4ee1-8800-db469aca5fe3", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Jump", + "type": "Button", + "id": "f1ba0d36-48eb-4cd5-b651-1c94a6531f70", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Previous", + "type": "Button", + "id": "2776c80d-3c14-4091-8c56-d04ced07a2b0", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Next", + "type": "Button", + "id": "b7230bb6-fc9b-4f52-8b25-f5e19cb2c2ba", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Sprint", + "type": "Button", + "id": "641cd816-40e6-41b4-8c3d-04687c349290", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "", + "id": "978bfe49-cc26-4a3d-ab7b-7d7a29327403", + "path": "/leftStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "WASD", + "id": "00ca640b-d935-4593-8157-c05846ea39b3", + "path": "Dpad", + "interactions": "", + "processors": "", + "groups": "", + "action": "Move", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "e2062cb9-1b15-46a2-838c-2f8d72a0bdd9", + "path": "/w", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "8180e8bd-4097-4f4e-ab88-4523101a6ce9", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "320bffee-a40b-4347-ac70-c210eb8bc73a", + "path": "/s", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "1c5327b5-f71c-4f60-99c7-4e737386f1d1", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "d2581a9b-1d11-4566-b27d-b92aff5fabbc", + "path": "/a", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "2e46982e-44cc-431b-9f0b-c11910bf467a", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcfe95b8-67b9-4526-84b5-5d0bc98d6400", + "path": "/d", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "77bff152-3580-4b21-b6de-dcd0c7e41164", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "1635d3fe-58b6-4ba9-a4e2-f4b964f6b5c8", + "path": "/{Primary2DAxis}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3ea4d645-4504-4529-b061-ab81934c3752", + "path": "/stick", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c1f7a91b-d0fd-4a62-997e-7fb9b69bf235", + "path": "/rightStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8c8e490b-c610-4785-884f-f04217b23ca4", + "path": "/delta", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse;Touch", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3e5f5442-8668-4b27-a940-df99bad7e831", + "path": "/{Hatswitch}", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "143bb1cd-cc10-4eca-a2f0-a3664166fe91", + "path": "/buttonWest", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "05f6913d-c316-48b2-a6bb-e225f14c7960", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "886e731e-7071-4ae4-95c0-e61739dad6fd", + "path": "/primaryTouch/tap", + "interactions": "", + "processors": "", + "groups": ";Touch", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "ee3d0cd2-254e-47a7-a8cb-bc94d9658c54", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8255d333-5683-4943-a58a-ccb207ff1dce", + "path": "/{PrimaryAction}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3c1c7f0-bd20-4ee7-a0f1-899b24bca6d7", + "path": "/enter", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "cbac6039-9c09-46a1-b5f2-4e5124ccb5ed", + "path": "/2", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e15ca19d-e649-4852-97d5-7fe8ccc44e94", + "path": "/dpad/right", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "f2e9ba44-c423-42a7-ad56-f20975884794", + "path": "/leftShift", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8cbb2f4b-a784-49cc-8d5e-c010b8c7f4e6", + "path": "/leftStickPress", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "d8bf24bf-3f2f-4160-a97c-38ec1eb520ba", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "eb40bb66-4559-4dfa-9a2f-820438abb426", + "path": "/space", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "daba33a1-ad0c-4742-a909-43ad1cdfbeb6", + "path": "/buttonSouth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "603f3daf-40bd-4854-8724-93e8017f59e3", + "path": "/secondaryButton", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1534dc16-a6aa-499d-9c3a-22b47347b52a", + "path": "/1", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "25060bbd-a3a6-476e-8fba-45ae484aad05", + "path": "/dpad/left", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1c04ea5f-b012-41d1-a6f7-02e963b52893", + "path": "/e", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3f66d0b-7751-423f-908b-a11c5bd95930", + "path": "/buttonNorth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4f4649ac-64a8-4a73-af11-b3faef356a4d", + "path": "/buttonEast", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "36e52cba-0905-478e-a818-f4bfcb9f3b9a", + "path": "/c", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + } + ] + }, + { + "name": "UI", + "id": "272f6d14-89ba-496f-b7ff-215263d3219f", + "actions": [ + { + "name": "Navigate", + "type": "PassThrough", + "id": "c95b2375-e6d9-4b88-9c4c-c5e76515df4b", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Submit", + "type": "Button", + "id": "7607c7b6-cd76-4816-beef-bd0341cfe950", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Cancel", + "type": "Button", + "id": "15cef263-9014-4fd5-94d9-4e4a6234a6ef", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Point", + "type": "PassThrough", + "id": "32b35790-4ed0-4e9a-aa41-69ac6d629449", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Click", + "type": "PassThrough", + "id": "3c7022bf-7922-4f7c-a998-c437916075ad", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "RightClick", + "type": "PassThrough", + "id": "44b200b1-1557-4083-816c-b22cbdf77ddf", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "MiddleClick", + "type": "PassThrough", + "id": "dad70c86-b58c-4b17-88ad-f5e53adf419e", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "ScrollWheel", + "type": "PassThrough", + "id": "0489e84a-4833-4c40-bfae-cea84b696689", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "TrackedDevicePosition", + "type": "PassThrough", + "id": "24908448-c609-4bc3-a128-ea258674378a", + "expectedControlType": "Vector3", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "TrackedDeviceOrientation", + "type": "PassThrough", + "id": "9caa3d8a-6b2f-4e8e-8bad-6ede561bd9be", + "expectedControlType": "Quaternion", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "Gamepad", + "id": "809f371f-c5e2-4e7a-83a1-d867598f40dd", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "14a5d6e8-4aaf-4119-a9ef-34b8c2c548bf", + "path": "/leftStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "9144cbe6-05e1-4687-a6d7-24f99d23dd81", + "path": "/rightStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2db08d65-c5fb-421b-983f-c71163608d67", + "path": "/leftStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "58748904-2ea9-4a80-8579-b500e6a76df8", + "path": "/rightStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "8ba04515-75aa-45de-966d-393d9bbd1c14", + "path": "/leftStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "712e721c-bdfb-4b23-a86c-a0d9fcfea921", + "path": "/rightStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcd248ae-a788-4676-a12e-f4d81205600b", + "path": "/leftStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "1f04d9bc-c50b-41a1-bfcc-afb75475ec20", + "path": "/rightStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "fb8277d4-c5cd-4663-9dc7-ee3f0b506d90", + "path": "/dpad", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "Joystick", + "id": "e25d9774-381c-4a61-b47c-7b6b299ad9f9", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "3db53b26-6601-41be-9887-63ac74e79d19", + "path": "/stick/up", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "0cb3e13e-3d90-4178-8ae6-d9c5501d653f", + "path": "/stick/down", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "0392d399-f6dd-4c82-8062-c1e9c0d34835", + "path": "/stick/left", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "942a66d9-d42f-43d6-8d70-ecb4ba5363bc", + "path": "/stick/right", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Keyboard", + "id": "ff527021-f211-4c02-933e-5976594c46ed", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "563fbfdd-0f09-408d-aa75-8642c4f08ef0", + "path": "/w", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "eb480147-c587-4a33-85ed-eb0ab9942c43", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2bf42165-60bc-42ca-8072-8c13ab40239b", + "path": "/s", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "85d264ad-e0a0-4565-b7ff-1a37edde51ac", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "74214943-c580-44e4-98eb-ad7eebe17902", + "path": "/a", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "cea9b045-a000-445b-95b8-0c171af70a3b", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "8607c725-d935-4808-84b1-8354e29bab63", + "path": "/d", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "4cda81dc-9edd-4e03-9d7c-a71a14345d0b", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "9e92bb26-7e3b-4ec4-b06b-3c8f8e498ddc", + "path": "*/{Submit}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Submit", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "82627dcc-3b13-4ba9-841d-e4b746d6553e", + "path": "*/{Cancel}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Cancel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c52c8e0b-8179-41d3-b8a1-d149033bbe86", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e1394cbc-336e-44ce-9ea8-6007ed6193f7", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "5693e57a-238a-46ed-b5ae-e64e6e574302", + "path": "/touch*/position", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4faf7dc9-b979-4210-aa8c-e808e1ef89f5", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8d66d5ba-88d7-48e6-b1cd-198bbfef7ace", + "path": "/tip", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "47c2a644-3ebc-4dae-a106-589b7ca75b59", + "path": "/touch*/press", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "bb9e6b34-44bf-4381-ac63-5aa15d19f677", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "38c99815-14ea-4617-8627-164d27641299", + "path": "/scroll", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "ScrollWheel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4c191405-5738-4d4b-a523-c6a301dbf754", + "path": "/rightButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "RightClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "24066f69-da47-44f3-a07e-0015fb02eb2e", + "path": "/middleButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "MiddleClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "7236c0d9-6ca3-47cf-a6ee-a97f5b59ea77", + "path": "/devicePosition", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "TrackedDevicePosition", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "23e01e3a-f935-4948-8d8b-9bcac77714fb", + "path": "/deviceRotation", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "TrackedDeviceOrientation", + "isComposite": false, + "isPartOfComposite": false + } + ] + } + ], + "controlSchemes": [ + { + "name": "Keyboard&Mouse", + "bindingGroup": "Keyboard&Mouse", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + }, + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Gamepad", + "bindingGroup": "Gamepad", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Touch", + "bindingGroup": "Touch", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Joystick", + "bindingGroup": "Joystick", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "XR", + "bindingGroup": "XR", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + } + ] +} \ No newline at end of file diff --git a/Assets/InputSystem_Actions.inputactions.meta b/Assets/InputSystem_Actions.inputactions.meta new file mode 100644 index 0000000..6b38b04 --- /dev/null +++ b/Assets/InputSystem_Actions.inputactions.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 052faaac586de48259a63d0c4782560b +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} + generateWrapperCode: 0 + wrapperCodePath: + wrapperClassName: + wrapperCodeNamespace: diff --git a/Assets/NuGet.config b/Assets/NuGet.config new file mode 100644 index 0000000..d267a78 --- /dev/null +++ b/Assets/NuGet.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Assets/NuGet.config.meta b/Assets/NuGet.config.meta new file mode 100644 index 0000000..72d2f3a --- /dev/null +++ b/Assets/NuGet.config.meta @@ -0,0 +1,28 @@ +fileFormatVersion: 2 +guid: e4c52702b30b9964ab235cadad663fbe +labels: +- NuGetForUnity +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OpusMicRecorder.cs b/Assets/OpusMicRecorder.cs new file mode 100644 index 0000000..e2a56dd --- /dev/null +++ b/Assets/OpusMicRecorder.cs @@ -0,0 +1,147 @@ +using UnityEngine; +using Concentus.Structs; +using Concentus.Enums; + +public class OpusMicRecorder : MonoBehaviour +{ + [Header("Audio Settings")] + public int sampleRate = 16000; + public int frameSizeMs = 20; // 20 میلی‌ثانیه + + [Header("Bandpass Filter Frequencies (Hz)")] + [Range(50f, 1000f)] + public float lowFreq = 300f; + + [Range(1000f, 8000f)] + public float highFreq = 3400f; + + private AudioClip micClip; + private int frameSizeSamples; + private int lastSamplePos = 0; + + private OpusEncoder encoder; + private BandpassFilter voiceFilter; + + public delegate void EncodedAudioReady(byte[] data); + public event EncodedAudioReady OnEncodedAudio; + + void Start() + { + frameSizeSamples = (sampleRate / 1000) * frameSizeMs; + + encoder = new OpusEncoder(sampleRate, 1, OpusApplication.OPUS_APPLICATION_VOIP); + encoder.Bitrate = 16000; + + micClip = Microphone.Start(null, true, 1, sampleRate); + + // اعتبارسنجی ورودی فرکانس ها + if (lowFreq >= highFreq) + { + Debug.LogWarning("lowFreq باید کمتر از highFreq باشد. تنظیم به مقادیر پیش‌فرض."); + lowFreq = 300f; + highFreq = 3400f; + } + + voiceFilter = new BandpassFilter(sampleRate, lowFreq, highFreq); + } + + void Update() + { + int micPos = Microphone.GetPosition(null); + int available = micPos - lastSamplePos; + if (available < 0) available += micClip.samples; + if (available < frameSizeSamples) return; + + float[] samples = new float[frameSizeSamples]; + micClip.GetData(samples, lastSamplePos); + lastSamplePos = (lastSamplePos + frameSizeSamples) % micClip.samples; + + // آپدیت فیلتر اگر کاربر در Inspector مقادیر را تغییر داد + if (voiceFilter.LowFreq != lowFreq || voiceFilter.HighFreq != highFreq) + { + voiceFilter.SetFrequencies(lowFreq, highFreq); + } + + voiceFilter.Process(samples); + + short[] pcm = new short[frameSizeSamples]; + for (int i = 0; i < frameSizeSamples; i++) + pcm[i] = (short)Mathf.Clamp(samples[i] * short.MaxValue, short.MinValue, short.MaxValue); + + byte[] encoded = new byte[1275]; + int encodedLength = encoder.Encode(pcm, 0, frameSizeSamples, encoded, 0, encoded.Length); + + byte[] packet = new byte[encodedLength]; + System.Buffer.BlockCopy(encoded, 0, packet, 0, encodedLength); + OnEncodedAudio?.Invoke(packet); + } +} + +// کلاس فیلتر میان‌گذر (Bandpass) با قابلیت تنظیم داینامیک فرکانس‌ها +public class BandpassFilter +{ + private float a0, a1, a2, b1, b2; + private float z1, z2; + + private float sampleRate; + public float LowFreq { get; private set; } + public float HighFreq { get; private set; } + + public BandpassFilter(float sampleRate, float lowFreq, float highFreq) + { + this.sampleRate = sampleRate; + SetFrequencies(lowFreq, highFreq); + z1 = 0; + z2 = 0; + } + + public void SetFrequencies(float lowFreq, float highFreq) + { + // اعتبارسنجی ساده + if (lowFreq <= 0) lowFreq = 50f; + if (highFreq >= sampleRate / 2f) highFreq = sampleRate / 2f - 100; + if (lowFreq >= highFreq) + { + Debug.LogWarning("BandpassFilter: lowFreq must be less than highFreq"); + return; + } + + LowFreq = lowFreq; + HighFreq = highFreq; + + float omegaL = 2.0f * Mathf.PI * LowFreq / sampleRate; + float omegaH = 2.0f * Mathf.PI * HighFreq / sampleRate; + + float centerFreq = (omegaL + omegaH) / 2.0f; + float bandwidth = omegaH - omegaL; + float Q = centerFreq / bandwidth; + + float alpha = Mathf.Sin(centerFreq) / (2.0f * Q); + float cosW0 = Mathf.Cos(centerFreq); + float norm = 1.0f + alpha; + + a0 = alpha / norm; + a1 = 0; + a2 = -alpha / norm; + b1 = -2.0f * cosW0 / norm; + b2 = (1.0f - alpha) / norm; + + // ریست فیلتر برای جلوگیری از artifact بعد تغییر فرکانس + z1 = 0; + z2 = 0; + } + + public void Process(float[] samples) + { + for (int i = 0; i < samples.Length; i++) + { + float input = samples[i]; + float output = a0 * input + a1 * z1 + a2 * z2 - b1 * z1 - b2 * z2; + + z2 = z1; + z1 = output; + + samples[i] = output; + } + } +} diff --git a/Assets/OpusMicRecorder.cs.meta b/Assets/OpusMicRecorder.cs.meta new file mode 100644 index 0000000..aa532f6 --- /dev/null +++ b/Assets/OpusMicRecorder.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 65e8804865c7c6b4d9ccb1da5ec057fb \ No newline at end of file diff --git a/Assets/OpusVoiceReceiverWithBuffer.cs b/Assets/OpusVoiceReceiverWithBuffer.cs new file mode 100644 index 0000000..08a44a4 --- /dev/null +++ b/Assets/OpusVoiceReceiverWithBuffer.cs @@ -0,0 +1,101 @@ +using UnityEngine; +using WebSocketSharp; +using System.Threading; +using Concentus.Structs; + +public class OpusVoiceReceiverWithBuffer : MonoBehaviour +{ + private WebSocket ws; + private AudioSource audioSource; + private OpusDecoder decoder; + + private const int sampleRate = 16000; + private const int channels = 1; + + private const int clipLengthSeconds = 5; // طول کل AudioClip به ثانیه + private AudioClip audioClip; + + private float[] audioBuffer; + private int bufferWritePos = 0; + private int bufferReadPos = 0; + private int bufferSize; + + private object bufferLock = new object(); + + void Start() + { + audioSource = gameObject.AddComponent(); + audioSource.loop = true; + + decoder = new OpusDecoder(sampleRate, channels); + + bufferSize = sampleRate * clipLengthSeconds; // اندازه بافر به تعداد نمونه ها + audioBuffer = new float[bufferSize]; + + audioClip = AudioClip.Create("VoiceStream", bufferSize, channels, sampleRate, true, OnAudioRead, OnAudioSetPosition); + audioSource.clip = audioClip; + audioSource.Play(); + + ws = new WebSocket("ws://192.168.31.10:8765"); + ws.OnMessage += OnMessageReceived; + ws.Connect(); + } + + private void OnMessageReceived(object sender, MessageEventArgs e) + { + // دیکد کردن داده اوپوس + short[] pcm = new short[960]; // 20ms frame at 16kHz + int decodedSamples = decoder.Decode(e.RawData, 0, e.RawData.Length, pcm, 0, pcm.Length, false); + + float[] floatSamples = new float[decodedSamples]; + for (int i = 0; i < decodedSamples; i++) + floatSamples[i] = pcm[i] / (float)short.MaxValue; + + // نوشتن در بافر حلقه‌ای امن در برابر چند Thread + lock (bufferLock) + { + for (int i = 0; i < decodedSamples; i++) + { + audioBuffer[bufferWritePos] = floatSamples[i]; + bufferWritePos = (bufferWritePos + 1) % bufferSize; + + // در صورت پر شدن بافر، خواندن را جلو می‌بریم (داده‌های قدیمی حذف می‌شوند) + if (bufferWritePos == bufferReadPos) + { + bufferReadPos = (bufferReadPos + 1) % bufferSize; + } + } + } + } + + // این تابع توسط AudioSource برای خواندن داده‌های صوتی فراخوانی می‌شود + void OnAudioRead(float[] data) + { + lock (bufferLock) + { + for (int i = 0; i < data.Length; i++) + { + if (bufferReadPos != bufferWritePos) + { + data[i] = audioBuffer[bufferReadPos]; + bufferReadPos = (bufferReadPos + 1) % bufferSize; + } + else + { + // اگر بافر خالی باشد، سکوت پخش می‌کنیم + data[i] = 0f; + } + } + } + } + + void OnAudioSetPosition(int newPosition) + { + // این تابع باید باشد ولی در اینجا کاری انجام نمی‌دهیم + } + + private void OnDestroy() + { + ws?.Close(); + } +} diff --git a/Assets/OpusVoiceReceiverWithBuffer.cs.meta b/Assets/OpusVoiceReceiverWithBuffer.cs.meta new file mode 100644 index 0000000..34689e5 --- /dev/null +++ b/Assets/OpusVoiceReceiverWithBuffer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7a0109d5fe4f62e4b81d939f9643cdb5 \ No newline at end of file diff --git a/Assets/OpusVoiceSender.cs b/Assets/OpusVoiceSender.cs new file mode 100644 index 0000000..fda6c49 --- /dev/null +++ b/Assets/OpusVoiceSender.cs @@ -0,0 +1,26 @@ +using UnityEngine; +using WebSocketSharp; + +public class OpusVoiceSender : MonoBehaviour +{ + private WebSocket ws; + + void Start() + { + ws = new WebSocket("ws://192.168.31.10:8765"); + ws.Connect(); + + GetComponent().OnEncodedAudio += SendEncoded; + } + + void SendEncoded(byte[] data) + { + if (ws.ReadyState == WebSocketState.Open) + ws.Send(data); + } + + void OnDestroy() + { + ws?.Close(); + } +} diff --git a/Assets/OpusVoiceSender.cs.meta b/Assets/OpusVoiceSender.cs.meta new file mode 100644 index 0000000..2e22c30 --- /dev/null +++ b/Assets/OpusVoiceSender.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 830aab62e0babd844bcb5f89e4cfb3b4 \ No newline at end of file diff --git a/Assets/Packages.meta b/Assets/Packages.meta new file mode 100644 index 0000000..e6e28ef --- /dev/null +++ b/Assets/Packages.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aa2d6f20931eef84d868ac94c79f3c5b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2.meta b/Assets/Packages/Concentus.2.2.2.meta new file mode 100644 index 0000000..dad25b4 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8cbf653f41d6a2469a7d8d2feef0821 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2/.signature.p7s b/Assets/Packages/Concentus.2.2.2/.signature.p7s new file mode 100644 index 0000000000000000000000000000000000000000..53743a772dd24525a4006d77ba7ae58eb8f80c1d GIT binary patch literal 12927 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 literal 0 HcmV?d00001 diff --git a/Assets/Packages/Concentus.2.2.2/Concentus.nuspec b/Assets/Packages/Concentus.2.2.2/Concentus.nuspec new file mode 100644 index 0000000..bcefb21 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/Concentus.nuspec @@ -0,0 +1,37 @@ + + + + Concentus + 2.2.2 + Concentus + Logan Stromberg + LICENSE + https://aka.ms/deprecateLicenseUrl + icon.png + https://github.com/lostromb/concentus + This package is a portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams. For better performance depending on your platform, see also the Concentus.Native package. + Native binaries are no longer distributed by default; if you want to automatically allow native code acceleration, install the Concentus.Native package as well. + © Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp. + Concentus Opus Audio Codec Resampler Resampling DSP Compression Encoder Decoder + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Assets/Packages/Concentus.2.2.2/Concentus.nuspec.meta b/Assets/Packages/Concentus.2.2.2/Concentus.nuspec.meta new file mode 100644 index 0000000..062c036 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/Concentus.nuspec.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: be45713b8661a334a82a58c58a19a076 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2/LICENSE b/Assets/Packages/Concentus.2.2.2/LICENSE new file mode 100644 index 0000000..62d21b6 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/LICENSE @@ -0,0 +1,37 @@ +Copyright (c) by various holding parties, including (but not limited to): +Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation, +Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry, +Logan Stromberg. All rights are reserved by their respective holders. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +This repository and its redistributable packages contain independently compiled +versions of the Opus C reference library, which is maintained by Xiph.org and the +Opus open-source contributors. The source code for these libraries is freely available +at https://gitlab.xiph.org/xiph/opus/-/tags/v1.5.2, and all binaries are being +redistributed to you under the same terms of the general Opus license dictated above. \ No newline at end of file diff --git a/Assets/Packages/Concentus.2.2.2/LICENSE.meta b/Assets/Packages/Concentus.2.2.2/LICENSE.meta new file mode 100644 index 0000000..97d7f10 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/LICENSE.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: aa23380966599314a95005b99d0a0841 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2/icon.png b/Assets/Packages/Concentus.2.2.2/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8d53bd84d02b48f0fc1702fdf49f02a82731a0c6 GIT binary patch literal 3294 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 CyXP$c literal 0 HcmV?d00001 diff --git a/Assets/Packages/Concentus.2.2.2/icon.png.meta b/Assets/Packages/Concentus.2.2.2/icon.png.meta new file mode 100644 index 0000000..e0b2203 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/icon.png.meta @@ -0,0 +1,130 @@ +fileFormatVersion: 2 +guid: 7fd063e586275b542af2356404626a50 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + 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 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 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 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2/lib.meta b/Assets/Packages/Concentus.2.2.2/lib.meta new file mode 100644 index 0000000..dbaadf3 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/lib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7fb6eafa7061b9f45af61aa37ee23670 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0.meta b/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0.meta new file mode 100644 index 0000000..395ef8f --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c02fb5c2ae91ffd489a650f27f19a75e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.dll b/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.dll new file mode 100644 index 0000000000000000000000000000000000000000..37bb7e1495de0c57ea29f4005f63018b0b1897cf GIT binary patch literal 392192 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 r-L!T2qhvK9t!OLz*&=@Op^NzcMN>w + + + Concentus + + + + Apply window and compute the MDCT for all sub-frames and + all channels in a frame + + + + OPT: This is the kernel you really want to optimize. It gets used a lot by the prefilter and by the PLC. + + + + + + + + + non-pointer case + + + + + + + + + + + only needed in one place + + + + + + + + + + + Decoder state + + + + + Scratch space used by the decoder. It is actually a variable-sized + field that resulted in a variable-sized struct. There are 6 distinct regions inside. + I have laid them out into separate variables here, + but these were the original definitions: + val32 decode_mem[], Size = channels*(DECODE_BUFFER_SIZE+mode.overlap) + val16 lpc[], Size = channels*LPC_ORDER + val16 oldEBands[], Size = 2*mode.nbEBands + val16 oldLogE[], Size = 2*mode.nbEBands + val16 oldLogE2[], Size = 2*mode.nbEBands + val16 backgroundLogE[], Size = 2*mode.nbEBands + + + + + The original C++ defined in_mem as a single float[1] which was the "caboose" + to the overall encoder struct, containing 5 separate variable-sized buffer + spaces of heterogeneous datatypes. I have laid them out into separate variables here, + but these were the original definitions: + val32 in_mem[], Size = channels*mode.overlap + val32 prefilter_mem[], Size = channels*COMBFILTER_MAXPERIOD + val16 oldBandE[], Size = channels*mode.nbEBands + val16 oldLogE[], Size = channels*mode.nbEBands + val16 oldLogE2[], Size = channels*mode.nbEBands + + + + + Definition for each "pseudo-critical band" + + + + + Number of lines in allocVectors + + + + + Number of bits in each band for several rates + + + + Takes the pitch vector and the decoded residual vector, computes the gain + that will give ||p+g*y||=1 and mixes the residual with the pitch. + + + Decode pulse vector and combine the result with the pitch vector to produce + the final normalised signal in the current band. + + + + For performance reasons, do not use this generic class if possible + + + + + + This simulates a C++ style pointer as far as can be implemented in C#. It represents a handle + to an array of objects, along with a base offset that represents the address. + When you are programming in debug mode, this class also enforces memory boundaries, + tracks uninitialized values, and also records all statistics of accesses to its base array. + + + + + + Returns the value currently under the pointer, and returns a new pointer with +1 offset. + This method is not very efficient because it creates new pointers; this is because we must preserve + the pass-by-value nature of C++ pointers when they are used as arguments to functions + + + + + + Copies the contents of this pointer, starting at its current address, into the space of another pointer. + !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!! + IN C# IT IS (SOURCE, DEST, LENGTH). DON'T GET SCOOPED LIKE I DID + + + + + + + Copies the contents of this pointer, starting at its current address, into an array. + !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!! + + + + + + + Loads N values from a source array into this pointer's space + + + + + + Assigns a certain value to a range of spaces in this array + + The value to set + The number of values to write + + + + Assigns a certain value to a range of spaces in this array + + The value to set + The number of values to write + + + + Moves regions of memory within the bounds of this pointer's array. + Extra checks are done to ensure that the data is not corrupted if the copy + regions overlap + + The offset to send this pointer's data to + The number of values to copy + + + + This is a helper class which contains static methods that involve pointers + + + + + Allocates a new array and returns a pointer to it + + + + + + + + Creates a pointer to an existing array + + + + + + + + *The number of bits to use for the range-coded part of uint integers.*/ + + + *The resolution of fractional-precision bit usage measurements, i.e., + + + + Normalizes the contents of val and rng so that rng lies entirely in the high-order symbol. + + + + + The probability of having a "one" is 1/(1<<_logp). + + + + + + + Outputs a symbol, with a carry bit. + If there is a potential to propagate a carry over several symbols, they are + buffered until it can be determined whether or not an actual carry will + occur. + If the counter for the buffered symbols overflows, then the stream becomes + undecodable. + This gives a theoretical limit of a few billion symbols in a single packet on + 32-bit systems. + The alternative is to truncate the range in order to force a carry, but + requires similar carry tracking in the decoder, needlessly slowing it down. + + + + + + Returns the number of bits "used" by the encoded or decoded symbols so far. + This same number can be computed in either the encoder or the decoder, and is + suitable for making coding decisions. + This will always be slightly larger than the exact value (e.g., all + rounding error is in the positive direction). + + The number of bits. + + + + This is a faster version of ec_tell_frac() that takes advantage + of the low(1/8 bit) resolution to use just a linear function + followed by a lookup to determine the exact transition thresholds. + + + + + Integer log in base2. Undefined for zero and negative numbers + + + Integer log in base2. Defined for zero, but not for negative numbers + + + + Multiplies two 16-bit fractional values. Bit-exactness of this macro is important + + + + + + + + Compute floor(sqrt(_val)) with exact arithmetic. + This has been tested on all possible 32-bit inputs. + + + + + + Sqrt approximation (QX input, QX/2 output) + + + Reciprocal approximation (Q15 input, Q16 output) + + + Reciprocal sqrt approximation in the range [0.25,1) (Q16 in, Q14 out) + + + Base-2 logarithm approximation (log2(x)). (Q14 input, Q10 output) + + + Base-2 exponential approximation (2^x). (Q10 input, Q16 output) + + + + Rotate a32 right by 'rot' bits. Negative rot values result in rotating + left. Output is 32bit int. + + + + + + + + Rotate a32 right by 'rot' bits. Negative rot values result in rotating + left. Output is 32bit uint. + + + + + + + + ((a32 >> 16) * (b32 >> 16)) + + + + + + + + Adds two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour + (just standard two's complement implementation-specific behaviour) + + + + + + + + Subtracts two signed 32-bit values in a way that can overflow, while not relying on undefined behaviour + (just standard two's complement implementation-specific behaviour) + + + + + + + + Multiply-accumulate macros that allow overflow in the addition (ie, no asserts in debug mode) + + + + + + + + + (a32 * (int)((short)(b32))) >> 16 output have to be 32bit int + + + + + + + + ////////////////// + + + + + + + + Add with saturation for positive input values + + + + + + + + Add with saturation for positive input values + + + + + + + + Add with saturation for positive input values + + + + + + + + Add with saturation for positive input values + + + + + + + + saturates before shifting + + + + + + + + Macro to convert floating-point constants to fixed-point by applying a scalar factor + Because of limitations of the C# JIT, this macro is actually evaluated at runtime and therefore should not be used if you want to maximize performance + + + + + PSEUDO-RANDOM GENERATOR + Make sure to store the result as the seed for the next call (also in between + frames), otherwise result won't be random at all. When only using some of the + bits, take the most significant bits by right-shifting. + + + + + silk_SMMUL: Signed top word multiply. + + + + + + + + Divide two int32 values and return result as int32 in a given Q-domain + + I numerator (Q0) + I denominator (Q0) + I Q-domain of result (>= 0) + O returns a good approximation of "(a32 << Qres) / b32" + + + + Invert int32 value and return result as int32 in a given Q-domain + + I denominator (Q0) + I Q-domain of result (> 0) + a good approximation of "(1 << Qres) / b32" + + + + a32 + (b32 * (int)((short)(c32))) >> 16 output have to be 32bit int + + + + * (a32 * (b32 >> 16)) >> 16 */ + + + * (int)((short)(a32)) * (b32 >> 16) */ + + + * a32 + (int)((short)(b32)) * (c32 >> 16) */ + + + * a64 + (b32 * c32) */ + + + + (a32 * b32) >> 16 + + + + + a32 + ((b32 * c32) >> 16) + + + + + Get number of leading zeros and fractional part (the bits right after the leading one) + + input + number of leading zeros + the 7 bits right after the leading one + + + + Approximation of square root. + Accuracy: +/- 10% for output values > 15 + +/- 2.5% for output values > 120 + + + + + + + Approximation of 128 * log2() (very close inverse of silk_log2lin()) + Convert input to a log scale + + (I) input in linear scale + + + + + Approximation of 2^() (very close inverse of silk_lin2log()) + Convert input to a linear scale + + input on log scale + Linearized value + + + + Interpolate two vectors + + (O) interpolated vector [MAX_LPC_ORDER] + (I) first vector [MAX_LPC_ORDER] + (I) second vector [MAX_LPC_ORDER] + (I) interp. factor, weight on 2nd vector + (I) number of parameters + + + + Inner product with bit-shift + + I input vector 1 + I input vector 2 + I number of bits to shift + I vector lengths + + + + + returns the value that has fewer higher-order bits, ignoring sign bit (? I think?) + + + + + + + + Counts leading zeroes + + + + + + + returns inverse base-2 log of a value + + + + + + + Arbitrary-rate audio resampler originally implemented for the Speex codec. + + + + + typedef int (* resampler_basic_func)(SpeexResamplerState*, int , Pointer<short>, int *, Pointer<short>, Pointer<int>); + + + + + Create a new resampler with integer input and output rates (in hertz). + + The number of channels to be processed + Input sampling rate, in hertz + Output sampling rate, in hertz + Resampling quality, from 0 to 10 + + + + Create a new resampler with fractional input/output rates. The sampling + rate ratio is an arbitrary rational number with both the numerator and + denominator being 32-bit integers. + + The number of channels to be processed + Numerator of sampling rate ratio + Denominator of sampling rate ratio + Input sample rate rounded to the nearest integer (in hz) + Output sample rate rounded to the nearest integer (in hz) + Resampling quality, from 0 to 10 + A newly created resampler + + + + + + + + + + + + + + + + Make sure that the first samples to go out of the resamplers don't have + leading zeros. This is only useful before starting to use a newly created + resampler. It is recommended to use that when resampling an audio file, as + it will generate a file with the same length.For real-time processing, + it is probably easier not to use this call (so that the output duration + is the same for the first frame). + + + + + Clears the resampler buffers so a new (unrelated) stream can be processed. + + + + + + + + Sets the input and output rates + + Input sampling rate, in hertz + Output sampling rate, in hertz + + + + Get the current input/output sampling rates (integer value). + + (Output) Sampling rate of input + (Output) Sampling rate of output + + + + Sets the input/output sampling rates and resampling ration (fractional values in Hz supported) + + Numerator of the sampling rate ratio + Denominator of the sampling rate ratio + Input sampling rate rounded to the nearest integer (in Hz) + Output sampling rate rounded to the nearest integer (in Hz) + + + + Gets the current resampling ratio. This will be reduced to the least common denominator + + (Output) numerator of the sampling rate ratio + (Output) denominator of the sampling rate ratio + + + + Gets or sets the resampling quality between 0 and 10, where 0 has poor + quality and 10 has very high quality. + + + + + Gets or sets the input stride + + + + + Gets or sets the output stride + + + + + Get the latency introduced by the resampler measured in input samples. + + + + + Gets the latency introduced by the resampler measured in output samples. + + + + + Gets the latency introduced by the resampler. + + + + + The Opus decoder structure. + + Opus is a stateful codec with overlapping blocks and as a result Opus + packets are not coded independently of each other. Packets must be + passed into the decoder serially and in the correct order for a correct + decode. Lost packets can be replaced with loss concealment by calling + the decoder with a null reference and zero length for the missing packet. + + A single codec state may only be accessed from a single thread at + a time and any required locking must be performed by the caller. Separate + streams must be decoded with separate decoder states and can be decoded + in parallel. + + + + + Decodes an Opus packet, putting the decoded audio into a floating-point buffer. + + The input payload. This may be empty if the previous packet was lost in transit (when PLC is enabled) + A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + The number of samples (per channel) of available space in the output PCM buf. + If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Decodes an Opus packet, putting the decoded audio into an int16 buffer. + + The input payload. This may be empty if the previous packet was lost in transit (when PLC is enabled) + A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + The number of samples (per channel) of available space in the output PCM buf. + If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Resets all buffers and prepares this decoder to process a fresh (unrelated) stream + + + + + Gets the version string of the library backing this implementation. + + An arbitrary version string. + + + + Gets the encoded bandwidth of the last packet decoded. This may be lower than the actual decoding sample rate, + and is only an indicator of the encoded audio's quality + + + + + Returns the final range of the entropy coder. If you need this then I also assume you know what it's for. + + + + + Gets or sets the gain (Q8) to use in decoding + + + + + Gets the duration of the last packet, in PCM samples per channel + + + + + Gets the number of channels that this decoder decodes to. Always constant for the lifetime of the decoder. + + + + + Gets the last estimated pitch value of the decoded audio + + + + + Gets the sample rate that this decoder decodes to. Always constant for the lifetime of the decoder + + + + + Represents an Opus encoder for a 1- or 2-channel audio stream. + May be backed either by managed code or a native adapter layer, + depending on your platform and performance requirements. + + + + + Encodes an Opus frame. + + Input signal (Interleaved if stereo). Length should be at least frame_size * channels + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + Destination buffer for the output payload. This must contain at least max_data_bytes + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Encodes an Opus frame using floating point input. + + Input signal in float format (Interleaved if stereo). Length should be at least frame_size * channels. + Value should be normalized to the +/- 1.0 range. Samples with a range beyond +/-1.0 will be clipped. + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + Destination buffer for the output payload. This must contain at least max_data_bytes + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Resets the state of this encoder, usually to prepare it for processing + a new audio stream without reallocating. + + + + + Gets the version string of the library backing this implementation. + + An arbitrary version string. + + + + Gets or sets the application (or signal type) of the input signal. This hints + to the encoder what type of details we want to preserve in the encoding. + This cannot be changed after the encoder has started + + + + + Gets or sets the bitrate for encoder, in bits per second. Valid bitrates are between 6K (6144) and 510K (522240) + + + + + Gets or sets the maximum number of channels to be encoded. This can be used to force a downmix from stereo to mono if stereo + separation is not important + + + + + Gets or sets the maximum bandwidth to be used by the encoder. This can be used if + high-frequency audio is not important to your application (e.g. telephony) + + + + + Gets or sets the "preferred" encoded bandwidth. This does not affect the sample rate of the input audio, + only the encoding cutoffs + + + + + Gets or sets a flag to enable Discontinuous Transmission mode. This mode is only available in the SILK encoder + (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, the encoder detects silence and background noise + and reduces the number of output packets, with up to 600ms in between separate packet transmissions. + + + + + Gets or sets the encoder complexity, between 0 and 10 + + + + + Gets or sets a flag to enable Forward Error Correction. This mode is only available in the SILK encoder + (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, lost packets can be partially recovered + by decoding data stored in the following packet. + + + + + Gets or sets the expected amount of packet loss in the transmission medium, from 0 to 100. + Only applies if UseInbandFEC is also enabled, and the encoder is in SILK mode. + + + + + Gets or sets a flag to enable Variable Bitrate encoding. This is recommended as it generally improves audio quality + with little impact on average bitrate + + + + + Gets or sets a flag to enable constrained VBR. This only applies when the encoder is in CELT mode (i.e. high bitrates) + + + + + Gets or sets a hint to the encoder for what type of audio is being processed, voice or music. + This is not set by the encoder itself i.e. it's not the result of any actual signal analysis. + + + + + Gets the number of samples of audio that are being stored in a buffer and are therefore contributing to latency. + + + + + Gets the encoder's input sample rate. This is fixed for the lifetime of the encoder. + + + + + Gets the number of channels that this encoder expects in its input. Always constant for the lifetime of the decoder. + + + + + Returns the final range of the entropy coder. If you need this then I also assume you know what it's for. + + + + + Gets or sets the bit resolution of the input audio signal. Though the encoder always uses 16-bit internally, this can help + it make better decisions about bandwidth and cutoff values + + + + + Gets or sets a fixed length for each encoded frame. Typically, the encoder just chooses a frame duration based on the input length + and the current internal mode. This can be used to enforce an exact length if it is required by your application (e.g. monotonous transmission) + + + + + Sets a user-forced mode for the encoder. There are three modes, SILK, HYBRID, and CELT. Silk can only encode below 40Kbit/s and is best suited + for speech. Silk also has modes such as FEC which may be desirable. Celt sounds better at higher bandwidth and is comparable to AAC. It also performs somewhat faster. + Hybrid is used to create a smooth transition between the two modes. Note that this value may not always be honored due to other factors such + as frame size and bitrate. + + + + + Gets or sets a flag to disable prediction, which does... something with the SILK codec + + + + + The Opus multistream decoder structure. + + Multistream decoding is an aggregate of several internal decoders and extra logic to parse multiple frames from + single packets and map them to the correct channels. The behavior of a multistream decoder is functionally the + same as a single decoder in most other respects. + + + + + Decodes a multichannel Opus packet, putting the decoded audio into a floating-point buffer. + + The input payload. This may be empty if the previous packet was lost in transit (when PLC is enabled) + A buffer to put the output PCM. The output size is (# of samples) * (# of channels) for a given single frame size (maximum 120ms). + The number of samples (per channel) of available space in the output PCM buf. + It should contain at least enough space to contain (# of samples) * (# of channels) for a given single frame size (maximum 120ms). + In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Decodes a multichannel Opus packet, putting the decoded audio into an int16 buffer. + + The input payload. This may be empty if the previous packet was lost in transit (when PLC is enabled) + A buffer to put the output PCM. The output size is (# of samples) * (# of channels) for a given single frame size (maximum 120ms). + The number of samples (per channel) of available space in the output PCM buf. + It should contain at least enough space to contain (# of samples) * (# of channels) for a given single frame size (maximum 120ms). + In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Resets all buffers and prepares this decoder to process a fresh (unrelated) stream + + + + + Gets the version string of the library backing this implementation. + + An arbitrary version string. + + + + Gets the encoded bandwidth of the last packet decoded. This may be lower than the actual decoding sample rate, + and is only an indicator of the encoded audio's quality + + + + + Returns the final range of the entropy coder. If you need this then I also assume you know what it's for. + + + + + Gets or sets the gain (Q8) to use in decoding + + + + + Gets the duration of the last packet, in PCM samples per channel + + + + + Gets the sample rate that this decoder decodes to. Always constant for the lifetime of the decoder + + + + + Gets the number of channels of the input data. Always constant for the lifetime of the decoder + + + + + The Opus multistream encoder structure. + + Multistream encoding is an aggregate of several internal encoders and extra logic to pack multiple frames into + single packets and map them to the correct channels. The behavior of a multistream encoder is functionally the + same as a single encoder in most other respects. + + + + + Encodes a multistream Opus frame. + + Input signal, interleaved to the total number of surround channels, according to Vorbis channel layouts. + Length should be at least (# of samples) * (# of channels) for a given single frame size (maximum 120ms). + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + Destination buffer for the output payload. This must contain at least max_data_bytes + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Encodes a multistream Opus frame. + + Input signal, interleaved to the total number of surround channels, according to Vorbis channel layouts. + Length should be at least (# of samples) * (# of channels) for a given single frame size (maximum 120ms). + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + Destination buffer for the output payload. This must contain at least max_data_bytes + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Resets the state of this encoder, usually to prepare it for processing + a new audio stream without reallocating. + + + + + Gets the version string of the library backing this implementation. + + An arbitrary version string. + + + + Gets or sets the application (or signal type) of the input signal. This hints + to the encoder what type of details we want to preserve in the encoding. + This cannot be changed after the encoder has started + + + + + Gets or sets the "preferred" encoded bandwidth. This does not affect the sample rate of the input audio, + only the encoding cutoffs + + + + + Gets or sets the bitrate for encoder, in bits per second. Valid bitrates are between 6K (6144) and 510K (522240) + + + + + Gets or sets the encoder complexity, between 0 and 10 + + + + + Gets the number of channels that this encoder expects in its input. Always constant for the lifetime of the decoder. + + + + + Gets or sets a fixed length for each encoded frame. Typically, the encoder just chooses a frame duration based on the input length + and the current internal mode. This can be used to enforce an exact length if it is required by your application (e.g. monotonous transmission) + + + + + Returns the final range of the entropy coder. If you need this then I also assume you know what it's for. + + + + + Gets or sets a user-forced mode for the encoder. There are three modes, SILK, HYBRID, and CELT. Silk can only encode below 40Kbit/s and is best suited + for speech. Silk also has modes such as FEC which may be desirable. Celt sounds better at higher bandwidth and is comparable to AAC. It also performs somewhat faster. + Hybrid is used to create a smooth transition between the two modes. Note that this value may not always be honored due to other factors such + as frame size and bitrate. + + + + + Gets the number of samples of audio that are being stored in a buffer and are therefore contributing to latency. + + + + + Gets or sets the bit resolution of the input audio signal. Though the encoder always uses 16-bit internally, this can help + it make better decisions about bandwidth and cutoff values + + + + + Gets or sets the maximum bandwidth to be used by the encoder. This can be used if + high-frequency audio is not important to your application (e.g. telephony) + + + + + Gets or sets the expected amount of packet loss in the transmission medium, from 0 to 100. + Only applies if UseInbandFEC is also enabled, and the encoder is in SILK mode. + + + + + Gets or sets a flag to disable prediction, which does... something with the SILK codec + + + + + Gets the encoder's input sample rate. This is fixed for the lifetime of the encoder. + + + + + Gets or sets a hint to the encoder for what type of audio is being processed, voice or music. + This is not set by the encoder itself i.e. it's not the result of any actual signal analysis. + + + + + Gets or sets a flag to enable constrained VBR. This only applies when the encoder is in CELT mode (i.e. high bitrates) + + + + + Gets or sets a flag to enable Discontinuous Transmission mode. This mode is only available in the SILK encoder + (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, the encoder detects silence and background noise + and reduces the number of output packets, with up to 600ms in between separate packet transmissions. + + + + + Gets or sets a flag to enable Forward Error Correction. This mode is only available in the SILK encoder + (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, lost packets can be partially recovered + by decoding data stored in the following packet. + + + + + Gets or sets a flag to enable Variable Bitrate encoding. This is recommended as it generally improves audio quality + with little impact on average bitrate + + + + + Represents an audio resampler which can process single-channel or interleaved-channel inputs + in either int16 or float32 formats. + + + + + Get the latency introduced by the resampler measured in input samples. + + + + + Gets or sets the input stride. + + + + + Gets the latency introduced by the resampler measured in output samples. + + + + + Gets the latency introduced by the resampler. + + + + + Gets or sets the output stride. + + + + + Gets or sets the resampling quality between 0 and 10, where 0 has poor + quality and 10 has very high quality. + + + + + Gets the current resampling ratio. This will be reduced to the least common denominator + + (Output) numerator of the sampling rate ratio + (Output) denominator of the sampling rate ratio + + + + Get the current input/output sampling rates (integer value). + + (Output) Sampling rate of input + (Output) Sampling rate of output + + + + Clears the resampler buffers so a new (unrelated) stream can be processed. + + + + + Make sure that the first samples to go out of the resamplers don't have + leading zeros. This is only useful before starting to use a newly created + resampler. It is recommended to use that when resampling an audio file, as + it will generate a file with the same length.For real-time processing, + it is probably easier not to use this call (so that the output duration + is the same for the first frame). + + + + + Resample a float32 sample array. The input and output buffers must *not* overlap + + The index of the channel to process (for multichannel input, 0 otherwise) + Input buffer + Number of input samples in the input buffer. After this function returns, this value + will be set to the number of input samples actually processed + Output buffer + Size of the output buffer. After this function returns, this value will be set to the number + of output samples actually produced + + + + Resample an int16 sample array. The input and output buffers must *not* overlap + + The index of the channel to process (for multichannel input, 0 otherwise) + Input buffer + Number of input samples in the input buffer. After this function returns, this value + will be set to the number of input samples actually processed + Output buffer + Size of the output buffer. After this function returns, this value will be set to the number + of output samples actually produced + + + + Resamples an interleaved float32 array. The stride is automatically determined by the number of channels of the resampler. + + Input buffer + The number of samples *PER-CHANNEL* in the input buffer. After this function returns, this + value will be set to the number of input samples actually processed + Output buffer + The size of the output buffer in samples-per-channel. After this function returns, this value + will be set to the number of samples per channel actually produced + + + + Resamples an interleaved int16 array. The stride is automatically determined by the number of channels of the resampler. + + Input buffer + The number of samples *PER-CHANNEL* in the input buffer. After this function returns, this + value will be set to the number of input samples actually processed + Output buffer + The size of the output buffer in samples-per-channel. After this function returns, this value + will be set to the number of samples per channel actually produced + + + + Sets the input/output sampling rates and resampling ration (fractional values in Hz supported) + + Numerator of the sampling rate ratio + Denominator of the sampling rate ratio + Input sampling rate rounded to the nearest integer (in Hz) + Output sampling rate rounded to the nearest integer (in Hz) + + + + Sets the input and output rates + + Input sampling rate, in hertz + Output sampling rate, in hertz + + + + Represents the status of loading a native library. + + + + + The library may or may not be available. + + + + + The library is available and ready to invoke. + + + + + The library is not available on this system. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Global helpers for handling platform and OS-specific tasks regarding native P/Invoke libraries + (mostly to make sure that the right one for the current platform actually gets invoked). + + + + + Gets information about the current runtime OS and processor, in parity with .Net's Runtime Identifier (RID) system. + + The current OS and architecture. + + + + Given a native developer-provided library name, such as "mynativelib", + search the current runtime directory + /runtimes/{runtime ID}/native for files like "mynativelib.dll" / "mynativelib.so", + matching the given library name and current runtime OS and architecture, and then prepare that library file + in such a way that future P/Invoke calls to that library should succeed and should invoke the correct + architecture-specific code. + + The library name to prepare (without platform-specific extensions such as ".dll") + A logger to log the result of the operation + Whether the runtime believes the given library is now available for loading or not. + + + + Gets the runtime ID string for a given architecture, e.g. "arm64" + + + + + + + + Gets the runtime ID string for a given operating system, e.g. "osx" + + + + + + + + Given a runtime ID, such as "android-arm64", get the list of all inherited runtime IDs in descending order of specificity, + not including the requested ID. + + + + + + + Parses the output from NetCore's RuntimeInformation.RuntimeIdentifier into an struct. + + The runtime identifier. + A parsed identifier struct. + + + + Attempts to parse a runtime OS identifier string (e.g. "win10", "osx") into a structured + operating system enum. Returns if parsing failed. + + The OS identifier string (should be lowercase but not strictly necessary) + A parsed OS enumeration + + + + Attempts to parse a runtime OS identifier string (e.g. "win10", "osx") into a structured + operating system enum. Returns if parsing failed. + + The OS identifier string (should be lowercase but not strictly necessary) + A parsed OS enumeration + + + + Attempts to parse a runtime platform architecture string (e.g. "x64", "arm") into a structured + architecture enum. Returns if parsing failed. + + The architecture identifier string (should be lowercase but not strictly necessary) + A parsed architecture enumeration + + + + Attempts to parse a runtime platform architecture string (e.g. "x64", "arm") into a structured + architecture enum. Returns if parsing failed. + + The architecture identifier string (should be lowercase but not strictly necessary) + A parsed architecture enumeration + + + + Attempts to load the given library using kernel hooks for the current runtime operating system. + + The name of the library to open, e.g. "libc" + The currently running platform + A logger + The availability of the given library after the probe attempt (it may load a locally provided or system-installed version of the requested library). + + + + Represents a tuple combination of operating system + processor architecture. + + + + + An operating system specifier, e.g. Linux + + + + + A processor architecture specifier, e.g. i386 + + + + + Constructs a new . + + An operating system specifier. + A proceses architecture specifier. + + + + An enumerated value for the current platform's processor architecture. + + + + + Error case. + + + + + "any" platform identifier + + + + + "x86" platform identifier + + + + + "x64" platform identifier + + + + + "arm" platform identifier (Implies hard float support) + + + + + "arm64" platform identifier, also called AArch64 + + + + + "armel" platform identifier (ARM v5 or older) + + + + + "armv6" platform identifier + + + + + "mips64" platform identifier + + + + + "ppc64le" platform identifier + + + + + "riscv64" platform identifier + + + + + "s390x" platform identifier + + + + + "loongarch64" platform identifier + + + + + Nobody supports this + + + + + An enumerated value for the current platform's operating system. + + + + + Error case. + + + + + "any" OS identifier + + + + + "win" OS identifier + + + + + "linux" OS identifier + + + + + "osx" OS identifier + + + + + "ios" OS identifier + + + + + "iossimulator" OS identifier + + + + + "android" OS identifier + + + + + "freebsd" OS identifier + + + + + "illumos" OS identifier + + + + + "linux-bionic" OS identifier + + + + + "linux-musl" OS identifier + + + + + "maccatalyst" OS identifier + + + + + "solaris" OS identifier + + + + + "tvos" OS identifier + + + + + "tvossimulator" OS identifier + + + + + "unix" OS identifier + + + + + "browser" OS identifier + + + + + "wasi" OS identifier + + + + + Central factory class for creating Opus encoder / decoder structs. + Using these methods allows the runtime to decide the most appropriate + implementation for your platform based on what is available (generally, + this means using a P/Invoke native adapter if native libopus is present) + + + + + Creates an IOpusEncoder appropriate for the current platform. + This could potentially involve a native code layer. + + The input sample rate. Must be a valid Opus samplerate (8K, 12K, 16K, 24K, 48K) + The number of channels of input (1 or 2) + The hint for the type of audio or application this encoder will be used for. + An optional logger for debugging messages about native library bindings. + A newly created opus encoder. + + + + Creates an IOpusEncoder appropriate for the current platform. + This could potentially involve a native code layer. + + The output sample rate to decode to. + Doesn't have to be the same sample rate the audio was encoded at. + Must be a valid Opus samplerate (8K, 12K, 16K, 24K, 48K) + The number of channels to decode to (1 or 2). + Doesn't have to be the same channel count the audio was encoded at. + An optional logger for debugging messages about native library bindings. + A newly created opus decoder. + + + + Creates a multichannel Opus encoder using the "new API". This constructor allows you to use predefined Vorbis channel mappings, or specify your own. + + The samples rate of the input + The total number of channels to encode (1 - 255) + The mapping family to use. 0 = mono/stereo, 1 = use Vorbis mappings, 255 = use raw channel mapping + The number of streams to encode + The number of coupled streams + A channel mapping describing which streams go to which channels: see + The application to use for the encoders + An optional logger for debugging messages about native library bindings. + A newly created opus multistream encoder. + + + + Creates a new multichannel decoder + + The sample rate to decode to. + The total number of channels being decoded. + The number of streams being decoded. + The number of coupled streams being decoded. + A channel mapping describing which streams go to which channels: see + An optional logger for debugging messages about native library bindings. + A newly created opus multistream decoder. + + + + Gets or sets a global flag that determines whether the codec factory should attempt to + use a native opus.dll or libopus implementation. True by default, but you can override + the value to false if the library probe causes problems or something. + + + + + + + The type of signal being handled (either short or float) - changes based on which API is used + + + + + + + + + + + + + + Returns the version number of this library + + + + + + + + The type of signal being handled (either short or float) + + + + + + + + + + + Best for most VoIP/videoconference applications where listening quality and intelligibility matter most + + + + + Best for broadcast/high-fidelity application where the decoded audio should be as close as possible to the input + + + + + Only use when lowest-achievable latency is what matters most. Voice-optimized modes cannot be used. + + + + + These are the actual Encoder CTL ID numbers. + They should not be used directly by applications. + In general, SETs should be even and GETs should be odd. + + + + + Resets the codec state to be equivalent to a freshly initialized state. + This should be called when switching streams in order to prevent + the back to back decoding from giving different results from + one at a time decoding. + + + + + Note that since most API-level errors are detected and thrown as + OpusExceptions, direct use of this class is not usually needed + unless you need to interop with existing C-style error handlers. + + + + + No error + + + + + -1: One or more invalid/out of range arguments + + + + + -2: Not enough bytes allocated in the buffer + + + + + -3: An public error was detected + + + + + -4: The compressed data passed is corrupted + + + + + -5: Invalid/unsupported request number + + + + + -6: An encoder or decoder structure is invalid or already freed + + + + + -7: Memory allocation has failed (This is typically not possible in the C# implementation). + + + + + -8: Used in rare cases where Concentus throws an error that is not covered by the + original Opus spec. + + + + + Select frame size from the argument (default) + + + + + Use 2.5 ms frames + + + + + Use 5 ms frames + + + + + Use 10 ms frames + + + + + Use 20 ms frames + + + + + Use 40 ms frames + + + + + Use 60 ms frames + + + + + Do not use - not fully implemented. Optimize the frame size dynamically. + + + + + Signal being encoded is voice + + + + + Signal being encoded is music + + + + + multi-layer perceptron processor + + + + + Auto/default setting + + + + + Maximum bitrate + + + + + An exception type which wraps a raw Opus error code. + + + + + Gets the raw Opus error code as defined in the C spec. These codes can + be found in the enumeration. + + + + + Creates a new empty . + This constructor is discouraged as it does not set the raw error code. + + + + + Creates a new with a custom error message. + This constructor is discouraged as it does not set the raw error code. + so it reports + + + + + Creates a new with a custom error message. + This constructor is discouraged as it does not set the raw error code. + so it reports + + + + + Creates a new with a custom error message and matching Opus error code. + + The entire error message string. + The raw error code that can be passed to other C-style error handlers + if necessary (it is not used to format the error string). + + + + state object for multi-layer perceptron + + + + + The Opus decoder structure. + + Opus is a stateful codec with overlapping blocks and as a result Opus + packets are not coded independently of each other. Packets must be + passed into the decoder serially and in the correct order for a correct + decode. Lost packets can be replaced with loss concealment by calling + the decoder with a null reference and zero length for the missing packet. + + A single codec state may only be accessed from a single thread at + a time and any required locking must be performed by the caller. Separate + streams must be decoded with separate decoder states and can be decoded + in parallel. + + + + Sampling rate (at the API level) + + + + OPUS_DECODER_RESET_START + + + + + Allocates and initializes a decoder state. + Internally Opus stores data at 48000 Hz, so that should be the default + value for Fs. However, the decoder can efficiently decode to buffers + at 8, 12, 16, and 24 kHz so if for some reason the caller cannot use + data at the full sample rate, or knows the compressed data doesn't + use the full frequency range, it can request decoding at a reduced + rate. Likewise, the decoder is capable of filling in either mono or + interleaved stereo pcm buffers, at the caller's request. + + Sample rate to decode at (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000. + Number of channels (1 or 2) to decode + The created encoder + + + Initializes a previously allocated decoder state. + The state must be at least the size returned by opus_decoder_get_size(). + This is intended for applications which use their own allocator instead of malloc. @see opus_decoder_create,opus_decoder_get_size + To reset a previously initialized state, use the #OPUS_RESET_STATE CTL. + @param [in] st OpusDecoder*: Decoder state. + @param [in] Fs opus_int32: Sampling rate to decode to (Hz). + This must be one of 8000, 12000, 16000, + 24000, or 48000. + @param [in] channels int: Number of channels (1 or 2) to decode + @retval #OPUS_OK Success or @ref opus_errorcodes + + + + Decodes an Opus packet. + + The input payload. This may be NULL if the previous packet was lost in transit (when PLC is enabled) + The offset to use when reading the input payload. Usually 0 + The number of bytes in the payload + A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + The offset to use when writing to the output buffer + The number of samples (per channel) of available space in the output PCM buf. + If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Decodes an Opus packet. + + The input payload. This may be empty if the previous packet was lost in transit (when PLC is enabled) + A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + The number of samples (per channel) of available space in the output PCM buf. + If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Decodes an Opus packet, putting the output data into a floating-point buffer. + + The input payload. This may be NULL if that previous packet was lost in transit (when PLC is enabled) + The offset to use when reading the input payload. Usually 0 + The number of bytes in the payload + A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + The offset to use when writing to the output buffer + The number of samples (per channel) of available space in the output PCM buf. + If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. In that case, + the length of frame_size must be EXACTLY the length of the audio that was lost, or else the decoder will be in an inconsistent state. + The number of decoded samples (per channel) + + + + Decodes an Opus packet. + + The input payload. This may be empty if the previous packet was lost in transit (when PLC is enabled) + A buffer to put the output PCM. The output size is (# of samples) * (# of channels). + You can use the OpusPacketInfo helpers to get a hint of the frame size before you decode the packet if you need + exact sizing. Otherwise, the minimum safe buffer size is 5760 samples + The number of samples (per channel) of available space in the output PCM buf. + If this is less than the maximum packet duration (120ms; 5760 for 48khz), this function will + not be capable of decoding some packets. In the case of PLC (data == NULL) or FEC (decode_fec == true), + then frame_size needs to be exactly the duration of the audio that is missing, otherwise the decoder will + not be in an optimal state to decode the next incoming packet. For the PLC and FEC cases, frame_size *must* + be a multiple of 10 ms. + Indicates that we want to recreate the PREVIOUS (lost) packet using FEC data from THIS packet. Using this packet + recovery scheme, you will actually decode this packet twice, first with decode_fec TRUE and then again with FALSE. If FEC data is not + available in this packet, the decoder will simply generate a best-effort recreation of the lost packet. + The number of decoded samples + + + + Gets the encoded bandwidth of the last packet decoded. This may be lower than the actual decoding sample rate, + and is only an indicator of the encoded audio's quality + + + + + + + + Gets the sample rate that this decoder decodes to. Always constant for the lifetime of the decoder + + + + + Gets the number of channels that this decoder decodes to. Always constant for the lifetime of the decoder. + + + + + Gets the last estimated pitch value of the decoded audio + + + + + Gets or sets the gain (Q8) to use in decoding + + + + + Gets the duration of the last packet, in PCM samples per channel + + + + + Resets all buffers and prepares this decoder to process a fresh (unrelated) stream + + + + + + + + + + + The Opus encoder structure + + + + + OPUS_ENCODER_RESET_START + + + + + + + + Allocates and initializes an encoder state. + Note that regardless of the sampling rate and number channels selected, the Opus encoder + can switch to a lower audio bandwidth or number of channels if the bitrate + selected is too low. This also means that it is safe to always use 48 kHz stereo input + and let the encoder optimize the encoding. The decoder will not be constrained later on + by the mode that you select here for the encoder. + + Sampling rate of input signal (Hz). This must be one of 8000, 12000, 16000, 24000, or 48000. + Number of channels (1 or 2) in input signal + There are three coding modes: + + OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice + signals. It enhances the input signal by high-pass filtering and + emphasizing formants and harmonics. Optionally it includes in-band + forward error correction to protect against packet loss. Use this + mode for typical VoIP applications.Because of the enhancement, + even at high bitrates the output may sound different from the input. + + + OPUS_APPLICATION_AUDIO gives best quality at a given bitrate for most + non-voice signals like music. Use this mode for music and mixed + (music/voice) content, broadcast, and applications requiring less + than 15 ms of coding delay. + + + OPUS_APPLICATION_RESTRICTED_LOWDELAY configures low-delay mode that + disables the speech-optimized mode in exchange for slightly reduced delay. + This mode can only be set on an newly initialized or freshly reset encoder + because it changes the codec delay. + + The created encoder + + + + + + The storage type of analysis_pcm, either short or float + + + + + + + + + + + + + + + + + Encodes an Opus frame. + + Input signal (Interleaved if stereo). Length should be at least frame_size * channels + Offset to use when reading the in_pcm buffer + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + Destination buffer for the output payload. This must contain at least max_data_bytes + The offset to use when writing to the output data buffer + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Encodes an Opus frame. + + Input signal (Interleaved if stereo). Length should be at least frame_size * channels + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + Destination buffer for the output payload. This must contain at least max_data_bytes + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Encodes an Opus frame using floating point input. + + Input signal in float format (Interleaved if stereo). Length should be at least frame_size * channels. + Value should be normalized to the +/- 1.0 range. Samples with a range beyond +/-1.0 will be clipped. + Offset to use when reading from in_pcm buffer + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + Destination buffer for the output payload. This must contain at least max_data_bytes + Offset to use when writing into output data buffer + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Encodes an Opus frame using floating point input. + + Input signal in float format (Interleaved if stereo). Length should be at least frame_size * channels. + Value should be normalized to the +/- 1.0 range. Samples with a range beyond +/-1.0 will be clipped. + The number of samples per channel in the inpus signal. + The frame size must be a valid Opus framesize for the given sample rate. + For example, at 48Khz the permitted values are 120, 240, 480, 960, 1920, and 2880. Passing in a duration of less than 10ms + (480 samples at 48Khz) will prevent the encoder from using FEC, DTX, or hybrid modes. + Destination buffer for the output payload. This must contain at least max_data_bytes + The maximum amount of space allocated for the output payload. This may be used to impose + an upper limit on the instant bitrate, but should not be used as the only bitrate control (use the Bitrate parameter for that) + The length of the encoded packet, in bytes. This value will always be less than or equal to 1275, the maximum Opus packet size. + + + + Gets or sets the application (or signal type) of the input signal. This hints + to the encoder what type of details we want to preserve in the encoding. + This cannot be changed after the encoder has started + + + + + Gets or sets the bitrate for encoder, in bits per second. Valid bitrates are between 6K (6144) and 510K (522240) + + + + + Gets or sets the maximum number of channels to be encoded. This can be used to force a downmix from stereo to mono if stereo + separation is not important + + + + + Gets or sets the maximum bandwidth to be used by the encoder. This can be used if + high-frequency audio is not important to your application (e.g. telephony) + + + + + Gets or sets the "preferred" encoded bandwidth. This does not affect the sample rate of the input audio, + only the encoding cutoffs + + + + + Gets or sets a flag to enable Discontinuous Transmission mode. This mode is only available in the SILK encoder + (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, the encoder detects silence and background noise + and reduces the number of output packets, with up to 600ms in between separate packet transmissions. + + + + + Gets or sets the encoder complexity, between 0 and 10 + + + + + Gets or sets a flag to enable Forward Error Correction. This mode is only available in the SILK encoder + (Bitrate < 40Kbit/s and/or ForceMode == SILK). When enabled, lost packets can be partially recovered + by decoding data stored in the following packet. + + + + + Gets or sets the expected amount of packet loss in the transmission medium, from 0 to 100. + Only applies if UseInbandFEC is also enabled, and the encoder is in SILK mode. + + + + + Gets or sets a flag to enable Variable Bitrate encoding. This is recommended as it generally improves audio quality + with little impact on average bitrate + + + + + Gets or sets a flag to enable constrained VBR. This only applies when the encoder is in CELT mode (i.e. high bitrates) + + + + + Gets or sets a hint to the encoder for what type of audio is being processed, voice or music + + + + + Gets the number of samples of audio that are being stored in a buffer and are therefore contributing to latency. + + + + + Gets the encoder's input sample rate. This is fixed for the lifetime of the encoder. + + + + + Gets the number of channels that this encoder expects in its input. Always constant for the lifetime of the decoder. + + + + + Returns the final range of the entropy coder + + + + + Gets or sets the bit resolution of the input audio signal. Though the encoder always uses 16-bit internally, this can help + it make better decisions about bandwidth and cutoff values + + + + + Gets or sets a fixed length for each encoded frame. Typically, the encoder just chooses a frame duration based on the input length + and the current public mode. This can be used to enforce an exact length if it is required by your application (e.g. monotonous transmission) + + + + + Gets or sets a user-forced mode for the encoder. There are three modes, SILK, HYBRID, and CELT. Silk can only encode below 40Kbit/s and is best suited + for speech. Silk also has modes such as FEC which may be desirable. Celt sounds better at higher bandwidth and is comparable to AAC. It also performs somewhat faster. + Hybrid is used to create a smooth transition between the two modes. Note that this value may not always be honored due to other factors such + as frame size and bitrate. + + + + + Gets or sets a value indicating that this stream is a low-frequency channel. This is used when encoding 5.1 surround audio. + + + + + Gets or sets a flag to disable prediction, which does... something with the SILK codec + + + + + Gets or sets a value indicating whether neural net analysis functions should be enabled, increasing encode quality + at the expense of speed. + + + + + EXPERIMENTAL!!! Gets or sets the constant quality encoding parameter. This is a new feature intended to approximate + "Constant Quality VBR" that other codecs such as MP3Lame provide, to let you encode mixed speech and music + (such as a podcast) in the same Opus stream without changing encoder params. + The quality is range from 0 (lowest) to 10 (highest). A setting of "null" means to use the regular Opus bitrate modes. + + + + + EXPERIMENTAL. Returns the probability that the current signal is music, according to the built-in analysis. + Only meaningful if EnableAnalysis is true and quality is above 7 or so + + + + + + + + A managed implementation of the Opus multistream decoder. + + + + + Creates a new multichannel decoder + + + + + + A channel mapping describing which streams go to which channels: see + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gets the internal decoder state of one of the multichannel stream's decoders, indicated by stream ID. + + The stream ID to fetch. + The decoder for that stream ID. + + + + + + + A managed implementation of the Opus multistream encoder. + + + + + + + + Creates a new multichannel Opus encoder using the "old API". + + The sample rate of the input signal + The number of channels to encode (1 - 255) + The number of streams to encode + The number of coupled streams + A raw mapping between input and output channels + The application to use for the encoder + + + + Creates a multichannel Opus encoder using the "new API". This constructor allows you to use predefined Vorbis channel mappings, or specify your own. + + The samples rate of the input + The total number of channels to encode (1 - 255) + The mapping family to use. 0 = mono/stereo, 1 = use Vorbis mappings, 255 = use raw channel mapping + The number of streams to encode + The number of coupled streams + A raw mapping of input/output channels + The application to use for the encoders + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gets the internal encoder state of one of the multichannel stream's enoders, indicated by stream ID. + + The stream ID to fetch. + The encoder for that stream ID. + + + + + + + Contains the parsed information from a single Opus packet, such as the bandwidth, + number of samples, encoder mode, channel count, etc. + + + + + The Table of Contents byte for this packet. Contains info about modes, frame length, etc. + + + + + The list of subframes in this packet + + + + + The index of the start of the payload within the packet + + + + + Parse an opus packet into a packetinfo object containing one or more frames. + Opus decode will perform this operation internally so most applications do + not need to use this function. + + The packet data to be parsed + The index of the beginning of the packet in the data array (usually 0) + The packet's length + A parsed packet info struct + + + + Parse an opus packet into a packetinfo object containing one or more frames. + Opus_decode will perform this operation internally so most applications do + not need to use this function. + + The packet data to be parsed + A parsed packet info struct + + + + Gets the number of samples per frame from an Opus packet. + + Sampling rate in Hz. This must be a multiple of 400, or inaccurate results will be returned. + Number of samples per frame + + + + Gets the number of samples per frame from an Opus packet. + + Opus packet. This must contain at least one byte of data + Sampling rate in Hz. This must be a multiple of 400, or inaccurate results will be returned. + Number of samples per frame + + + + Gets the encoded bandwidth of an Opus packet. Note that you are not forced to decode at this bandwidth + + An OpusBandwidth value + + + + Gets the encoded bandwidth of an Opus packet. Note that you are not forced to decode at this bandwidth + + An Opus packet (must be at least 1 byte). + An OpusBandwidth value + + + + Gets the number of encoded channels of an Opus packet. Note that you are not forced to decode with this channel count. + + The number of channels + + + + Gets the number of encoded channels of an Opus packet. Note that you are not forced to decode with this channel count. + + An opus packet (must be at least 1 byte) + The number of channels + + + + Gets the number of frames in an Opus packet. + + An Opus packet + The number of frames in the packet + + + + Gets the number of samples of an Opus packet. + + An Opus packet + The decoder's sampling rate in Hz. This must be a multiple of 400 + The size of the PCM samples that this packet will be decoded to at the specified sample rate + + + + Gets the number of samples of an Opus packet. + + Your current decoder state + An Opus packet + The start offset in the array for reading the packet from + The packet's length + The size of the PCM samples that this packet will be decoded to by the specified decoder + + + + Gets the number of samples of an Opus packet. + + Your current decoder state + An Opus packet + The size of the PCM samples that this packet will be decoded to by the specified decoder + + + + Gets the mode that was used to encode this packet. + Normally there is nothing you can really do with this, other than debugging. + + An Opus packet + The OpusMode used by the encoder + + + + Gets the mode that was used to encode this packet. + Normally there is nothing you can really do with this, other than debugging. + + The OpusMode used by the encoder + + + (Re)initializes a previously allocated repacketizer state. + The state must be at least the size returned by opus_repacketizer_get_size(). + This can be used for applications which use their own allocator instead of + malloc(). + It must also be called to reset the queue of packets waiting to be + repacketized, which is necessary if the maximum packet duration of 120 ms + is reached or if you wish to submit packets with a different Opus + configuration (coding mode, audio bandwidth, frame size, or channel count). + Failure to do so will prevent a new packet from being added with + opus_repacketizer_cat(). + @see opus_repacketizer_create + @see opus_repacketizer_get_size + @see opus_repacketizer_cat + @param rp OpusRepacketizer*: The repacketizer state to + (re)initialize. + + + + Creates a new repacketizer + + + + opus_repacketizer_cat. Add a packet to the current repacketizer state. + This packet must match the configuration of any packets already submitted + for repacketization since the last call to opus_repacketizer_init(). + This means that it must have the same coding mode, audio bandwidth, frame + size, and channel count. + This can be checked in advance by examining the top 6 bits of the first + byte of the packet, and ensuring they match the top 6 bits of the first + byte of any previously submitted packet. + The total duration of audio in the repacketizer state also must not exceed + 120 ms, the maximum duration of a single packet, after adding this packet. + + The contents of the current repacketizer state can be extracted into new + packets using opus_repacketizer_out() or opus_repacketizer_out_range(). + + In order to add a packet with a different configuration or to add more + audio beyond 120 ms, you must clear the repacketizer state by calling + opus_repacketizer_init(). + If a packet is too large to add to the current repacketizer state, no part + of it is added, even if it contains multiple frames, some of which might + fit. + If you wish to be able to add parts of such packets, you should first use + another repacketizer to split the packet into pieces and add them + individually. + @see opus_repacketizer_out_range + @see opus_repacketizer_out + @see opus_repacketizer_init + @param rp OpusRepacketizer*: The repacketizer state to which to + add the packet. + @param[in] data const unsigned char*: The packet data. + The application must ensure + this pointer remains valid + until the next call to + opus_repacketizer_init() or + opus_repacketizer_destroy(). + @param len opus_int32: The number of bytes in the packet data. + @returns An error code indicating whether or not the operation succeeded. + @retval #OPUS_OK The packet's contents have been added to the repacketizer + state. + @retval #OPUS_INVALID_PACKET The packet did not have a valid TOC sequence, + the packet's TOC sequence was not compatible + with previously submitted packets (because + the coding mode, audio bandwidth, frame size, + or channel count did not match), or adding + this packet would increase the total amount of + audio stored in the repacketizer state to more + than 120 ms. + + + Return the total number of frames contained in packet data submitted to + the repacketizer state so far via opus_repacketizer_cat() since the last + call to opus_repacketizer_init() or opus_repacketizer_create(). + This defines the valid range of packets that can be extracted with + opus_repacketizer_out_range() or opus_repacketizer_out(). + @param rp OpusRepacketizer*: The repacketizer state containing the + frames. + @returns The total number of frames contained in the packet data submitted + to the repacketizer state. + + + Construct a new packet from data previously submitted to the repacketizer + state via opus_repacketizer_cat(). + @param rp OpusRepacketizer*: The repacketizer state from which to + construct the new packet. + @param begin int: The index of the first frame in the current + repacketizer state to include in the output. + @param end int: One past the index of the last frame in the + current repacketizer state to include in the + output. + @param[out] data const unsigned char*: The buffer in which to + store the output packet. + @param maxlen opus_int32: The maximum number of bytes to store in + the output buffer. In order to guarantee + success, this should be at least + 1276 for a single frame, + or for multiple frames, + 1277*(end-begin). + However, 1*(end-begin) plus + the size of all packet data submitted to + the repacketizer since the last call to + opus_repacketizer_init() or + opus_repacketizer_create() is also + sufficient, and possibly much smaller. + @returns The total size of the output packet on success, or an error code + on failure. + @retval #OPUS_BAD_ARG [begin,end) was an invalid range of + frames (begin < 0, begin >= end, or end > + opus_repacketizer_get_nb_frames()). + @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the + complete output packet. + + + Construct a new packet from data previously submitted to the repacketizer + state via opus_repacketizer_cat(). + This is a convenience routine that returns all the data submitted so far + in a single packet. + It is equivalent to calling + @code + opus_repacketizer_out_range(rp, 0, opus_repacketizer_get_nb_frames(rp), + data, maxlen) + @endcode + @param rp OpusRepacketizer*: The repacketizer state from which to + construct the new packet. + @param[out] data const unsigned char*: The buffer in which to + store the output packet. + @param maxlen opus_int32: The maximum number of bytes to store in + the output buffer. In order to guarantee + success, this should be at least + 1277*opus_repacketizer_get_nb_frames(rp). + However, + 1*opus_repacketizer_get_nb_frames(rp) + plus the size of all packet data + submitted to the repacketizer since the + last call to opus_repacketizer_init() or + opus_repacketizer_create() is also + sufficient, and possibly much smaller. + @returns The total size of the output packet on success, or an error code + on failure. + @retval #OPUS_BUFFER_TOO_SMALL \a maxlen was insufficient to contain the + complete output packet. + + + Pads a given Opus packet to a larger size (possibly changing the TOC sequence). + @param[in,out] data const unsigned char*: The buffer containing the + packet to pad. + @param len opus_int32: The size of the packet. + This must be at least 1. + @param new_len opus_int32: The desired size of the packet after padding. + This must be at least as large as len. + @returns an error code + @retval #OPUS_OK \a on success. + @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. + @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + + + Remove all padding from a given Opus packet and rewrite the TOC sequence to + minimize space usage. + @param[in,out] data const unsigned char*: The buffer containing the + packet to strip. + @param len opus_int32: The size of the packet. + This must be at least 1. + @returns The new size of the output packet on success, or an error code + on failure. + @retval #OPUS_BAD_ARG \a len was less than 1. + @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + + + Pads a given Opus multi-stream packet to a larger size (possibly changing the TOC sequence). + @param[in,out] data const unsigned char*: The buffer containing the + packet to pad. + @param len opus_int32: The size of the packet. + This must be at least 1. + @param new_len opus_int32: The desired size of the packet after padding. + This must be at least 1. + @param nb_streams opus_int32: The number of streams (not channels) in the packet. + This must be at least as large as len. + @returns an error code + @retval #OPUS_OK \a on success. + @retval #OPUS_BAD_ARG \a len was less than 1. + @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + + + Remove all padding from a given Opus multi-stream packet and rewrite the TOC sequence to + minimize space usage. + @param[in,out] data const unsigned char*: The buffer containing the + packet to strip. + @param len opus_int32: The size of the packet. + This must be at least 1. + @param nb_streams opus_int32: The number of streams (not channels) in the packet. + This must be at least 1. + @returns The new size of the output packet on success, or an error code + on failure. + @retval #OPUS_BAD_ARG \a len was less than 1 or new_len was less than len. + @retval #OPUS_INVALID_PACKET \a data did not contain a valid Opus packet. + + + Probability of having speech for time i to DETECT_SIZE-1 (and music before). + pspeech[0] is the probability that all frames in the window are speech. + + + Probability of having music for time i to DETECT_SIZE-1 (and speech before). + pmusic[0] is the probability that all frames in the window are music. + + + + Central factory class for creating resamplers. + Using these methods allows the runtime to decide the most appropriate + implementation for your platform based on what is available. Native + interop for resamplers is not yet implemented, but may be in the future. + + + + + Create a new resampler with integer input and output rates (in hertz). + + The number of channels to be processed + Input sampling rate, in hertz + Output sampling rate, in hertz + Resampling quality, from 0 to 10 + An optional logger for the operation + A newly created resampler + + + + Create a new resampler with fractional input/output rates. The sampling + rate ratio is an arbitrary rational number with both the numerator and + denominator being 32-bit integers. + + The number of channels to be processed + Numerator of sampling rate ratio + Denominator of sampling rate ratio + Input sample rate rounded to the nearest integer (in hz) + Output sample rate rounded to the nearest integer (in hz) + Resampling quality, from 0 to 10 + An optional logger for the operation + A newly created resampler + + + + Chirp (bw expand) LP AR filter (Fixed point implementation) + + I/O AR filter to be expanded (without leading 1) + I length of ar + I chirp factor (typically in range (0..1) ) + + + + Chirp (bw expand) LP AR filter (Fixed point implementation) + + I/O AR filter to be expanded (without leading 1) + I length of ar + I chirp factor (typically in range (0..1) ) + + + + Comfort noise generation and estimation + + + + + Generates excitation for CNG LPC synthesis + + O CNG excitation signal Q10 + I Random samples buffer Q10 + I Gain to apply + I Length + I/O Seed to random index generator + + + + Resets CNG state + + I/O Decoder state + + + + Updates CNG estimate, and applies the CNG when packet was lost + + I/O Decoder state + I/O Decoder control + I/O Signal + I Length of residual + + + + Encodes signs of excitation + + I/O Compressor data structure + I pulse signal + I length of input + I Signal type + I Quantization offset type + I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS] + + + + Decodes signs of excitation + + I/O Compressor data structure + I/O pulse signal + I length of input + I Signal type + I Quantization offset type + I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS] + + + + Reset decoder state + + I/O Stat + Returns error code + + + + Init or Reset encoder + + I/O State + O Encoder Status + O Returns error code + + + + Read control structure from encode + + I State + O Encoder Status + Returns error code + + + + Encode frame with Silk + Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what + encControl.payloadSize_ms is set to + + I/O State + I Control status + I Speech sample input vector + I Number of samples in input vector + I/O Compressor data structure + I/O Number of bytes in payload (input: Max bytes) + I Flag to indicate prefilling buffers no coding + error code + + + + Encode side-information parameters to payload + + I/O Encoder state + I/O Compressor data structure + I Frame number + I Flag indicating LBRR data is being encoded + I The type of conditional coding to use + + + + + + (O) + (I) + I max value for sum of pulses + I number of output values + return ok + + + + + + (O) + (I) + I max value for sum of pulses + I number of output values + return ok + + + + Encode quantization indices of excitation + + I/O compressor data structure + I Signal type + I quantOffsetType + I quantization indices + I Frame length + + + + Represents error messages from a silk encoder/decoder + + + + + Second order ARMA filter, alternative implementation + + I input signal + I MA coefficients [3] + I AR coefficients [2] + I/O State vector [2] + O output signal + I signal length (must be even) + I Operate on interleaved signal if > 1 + + + + Split signal into two decimated bands using first-order allpass filters + + I Input signal [N] + I/O State vector [2] + O Low band [N/2] + O High band [N/2] + I Number of input samples + + + + Chirp (bandwidth expand) LP AR filter + + I/O AR filter to be expanded (without leading 1) + I Length of ar + I Chirp factor in Q16 + + + + Elliptic/Cauer filters designed with 0.1 dB passband ripple, + 80 dB minimum stopband attenuation, and + [0.95 : 0.15 : 0.35] normalized cut off frequencies. + Helper function, interpolates the filter taps + + order [TRANSITION_NB] + order [TRANSITION_NA] + + + + + + LPC analysis filter + NB! State is kept internally and the + filter always starts with zero state + first d output samples are set to zero + + O Output signal + I Input signal + I MA prediction coefficients, Q12 [order] + I Signal length + I Filter order + + + + Compute inverse of LPC prediction gain, and + test if LPC coefficients are stable (all poles within unit circle) + + Prediction coefficients, order [2][SILK_MAX_ORDER_LPC] + Prediction order + inverse prediction gain in energy domain, Q30 + + + + For input in Q12 domain + + Prediction coefficients, Q12 [order] + I Prediction order + inverse prediction gain in energy domain, Q30 + + + + Finds linear prediction coeffecients and weights + + + + + + + + + + + + + + + + + [SilkConstants.LTP_ORDER] + [SilkConstants.LTP_ORDER] + + + + + Gain scalar quantization with hysteresis, uniform on log scale + + O gain indices [MAX_NB_SUBFR] + I/O gains (quantized out) [MAX_NB_SUBFR] + I/O last index in previous frame. [Porting note] original implementation passed this as an int8* + I first gain is delta coded if 1 + I number of subframes + + + + Gains scalar dequantization, uniform on log scale + + O quantized gains [MAX_NB_SUBFR] + I gain indices [MAX_NB_SUBFR] + I/O last index in previous frame [Porting note] original implementation passed this as an int8* + I first gain is delta coded if 1 + I number of subframes + + + + Compute unique identifier of gain indices vector + + I gain indices [MAX_NB_SUBFR] + I number of subframes + unique identifier of gains + + + + High-pass filter with cutoff frequency adaptation based on pitch lag statistics + + I/O Encoder states + + + + Normalized line spectrum frequency processor + + + + + Number of binary divisions, when not in low complexity mode + + + + + Compute quantization errors for an LPC_order element input vector for a VQ codebook + + (O) Quantization errors [K] + (I) Input vectors to be quantized [LPC_order] + (I) Codebook vectors [K*LPC_order] + (I) Number of codebook vectors + (I) Number of LPCs + + + + Laroia low complexity NLSF weights + + (O) Pointer to input vector weights [D] + (I) Pointer to input vector [D] + (I) Input vector dimension (even) + + + + Returns RD value in Q30 + + (O) Output [ order ] + (I) Quantization indices [ order ] + (I) Backward predictor coefs [ order ] + (I) Quantization step size + (I) Number of input values + + + + Unpack predictor values and indices for entropy coding tables + + (O) Indices to entropy tables [ LPC_ORDER ] + (O) LSF predictor [ LPC_ORDER ] + (I) Codebook object + (I) Index of vector in first LSF codebook + + + + NLSF stabilizer, for a single input data vector + + (I/O) Unstable/stabilized normalized LSF vector in Q15 [L] + (I) Min distance vector, NDeltaMin_Q15[L] must be >= 1 [L+1] + (I) Number of NLSF parameters in the input vector + + + + NLSF vector decoder + + (O) Quantized NLSF vector [ LPC_ORDER ] + (I) Codebook path vector [ LPC_ORDER + 1 ] + (I) Codebook object + + + + Delayed-decision quantizer for NLSF residuals + + (O) Quantization indices [ order ] + (O) Input [ order ] + (I) Weights [ order ] + (I) Backward predictor coefs [ order ] + (I) Indices to entropy coding tables [ order ] + (I) Rates [] + (I) Quantization step size + (I) Inverse quantization step size + (I) R/D tradeoff + (I) Number of input values + RD value in Q25 + Fixme: Optimize this method! + + + + NLSF vector encoder + + (I) Codebook path vector [ LPC_ORDER + 1 ] + (I/O) Quantized NLSF vector [ LPC_ORDER ] + (I) Codebook object + (I) NLSF weight vector [ LPC_ORDER ] + (I) Rate weight for the RD optimization + (I) Max survivors after first stage + (I) Signal type: 0/1/2 + RD value in Q25 + + + + helper function for NLSF2A(..) + + (O) intermediate polynomial, QA [dd+1] + (I) vector of interleaved 2*cos(LSFs), QA [d] + (I) polynomial order (= 1/2 * filter order) + + + + compute whitening filter coefficients from normalized line spectral frequencies + + (O) monic whitening filter coefficients in Q12, [ d ] + (I) normalized line spectral frequencies in Q15, [ d ] + (I) filter order (should be even) + + + + Helper function for A2NLSF(..) Transforms polynomials from cos(n*f) to cos(f)^n + + (I/O) Polynomial + (I) Polynomial order (= filter order / 2 ) + + + + Helper function for A2NLSF(..) Polynomial evaluation + + (I) Polynomial, Q16 + (I) Evaluation point, Q12 + (I) Order + the polynomial evaluation, in Q16 + + + + Compute Normalized Line Spectral Frequencies (NLSFs) from whitening filter coefficients + If not all roots are found, the a_Q16 coefficients are bandwidth expanded until convergence. + + (O) Normalized Line Spectral Frequencies in Q15 (0..2^15-1) [d] + (I/O) Monic whitening filter coefficients in Q16 [d] + (I) Filter order (must be even) + + + + Limit, stabilize, convert and quantize NLSFs + + I/O Encoder state + O Prediction coefficients [ 2 ][MAX_LPC_ORDER] + I/O Normalized LSFs (quant out) (0 - (2^15-1)) [MAX_LPC_ORDER] + I Previous Normalized LSFs (0 - (2^15-1)) [MAX_LPC_ORDER] + + + + Routines for managing packet loss concealment + + + + + + + O + O + O + O + I + I + I + I + + + + Simple way to make [8000, 12000, 16000, 24000, 48000] to [0, 1, 2, 3, 4] + + + + + + + Initialize/reset the resampler state for a given pair of input/output sampling rates + + I/O Resampler state + I Input sampling rate (Hz) + I Output sampling rate (Hz) + I If 1: encoder; if 0: decoder + + + + + Resampler: convert from one sampling rate to another + Input and output sampling rate are at most 48000 Hz + + I/O Resampler state + O Output signal + I Input signal + I Number of input samples + + + + + Downsample by a factor 2 + + I/O State vector [ 2 ] + O Output signal [ floor(len/2) ] + I Input signal [ len ] + I Number of input samples + + + + Downsample by a factor 2/3, low quality + + I/O State vector [ 6 ] + O Output signal [ floor(2*inLen/3) ] + I Input signal [ inLen ] + I Number of input samples + + + + Second order AR filter with single delay elements + + I/O State vector [ 2 ] + O Output signal + I Input signal + I AR coefficients, Q14 + I Signal length + + + + Resample with a 2nd order AR filter followed by FIR interpolation + + I/O Resampler state + O Output signal + I Input signal + I Number of input samples + + + + Upsample using a combination of allpass-based 2x upsampling and FIR interpolation + + I/O Resampler state + O Output signal + I Input signal + I Number of input samples + + + + Upsample by a factor 2, high quality + Uses 2nd order allpass filters for the 2x upsampling, followed by a + notch filter just above Nyquist. + + I/O Resampler state [ 6 ] + O Output signal [ 2 * len ] + I Input signal [ len ] + I Number of input samples + + + + shell coder; pulse-subframe length is hardcoded + + + + + + O combined pulses vector [len] + I input vector [2 * len] + I number of OUTPUT samples + + + + + O combined pulses vector [len] + I input vector [2 * len] + I number of OUTPUT samples + + + + + + O pulse amplitude of first child subframe + O pulse amplitude of second child subframe + I/O Compressor data structure + I pulse amplitude of current subframe + I table of shell cdfs + + + + Shell encoder, operates on one shell code frame of 16 pulses + + I/O compressor data structure + I data: nonnegative pulse amplitudes + + + + Approximate sigmoid function + + + + + + + (I/O) Unsorted / Sorted vector + (O) Index vector for the sorted elements + (I) Vector length + (I) Number of correctly sorted positions + + + + Insertion sort (fast for already almost sorted arrays): + Best case: O(n) for an already sorted array + Worst case: O(n^2) for an inversely sorted array + + (I/O) Unsorted / Sorted vector + (I) Vector length + + + + Decode mid/side predictors + + I/O Compressor data structure + O Predictors + + + + Decode mid-only flag + + I/O Compressor data structure + O Flag that only mid channel has been coded + + + + Entropy code the mid/side quantization indices + + I/O Compressor data structure + I Quantization indices [ 2 ][ 3 ] + + + + Entropy code the mid-only flag + + I/O Compressor data structure + + + + + Find least-squares prediction gain for one signal based on another and quantize it + + O Ratio of residual and mid energies + I Basis signal + I Target signal + I/O Smoothed mid, residual norms + I Number of samples + I Smoothing coefficient + O Returns predictor in Q13 + + + + Convert Left/Right stereo signal to adaptive Mid/Side representation + + I/O State + I/O Left input signal, becomes mid signal + I/O Right input signal, becomes side signal + O Quantization indices [ 2 ][ 3 ] + O Flag: only mid signal coded + O Bitrates for mid and side signals + I Total bitrate + I Speech activity level in previous frame + I Last frame before a stereo.mono transition + I Sample rate (kHz) + I Number of samples + + + + Convert adaptive Mid/Side representation to Left/Right stereo signal + + I/O State + I/O Left input signal, becomes mid signal + I/O Right input signal, becomes side signal + I Predictors + I Samples rate (kHz) + I Number of samples + + + + Quantize mid/side predictors + + I/O Predictors (out: quantized) + O Quantization indices [ 2 ][ 3 ] + + + + Struct for CNG + + + + + Structure for controlling decoder operation and reading decoder status + + + + + Structure for controlling encoder operation + + + + + Checks this encoder control struct and returns error code, if any + + + + + + Structure containing NLSF codebook + + + + + Quantization step size + + + + + Inverse quantization step size + + + + + POINTER + + + + + POINTER + + + + + POINTER to Backward predictor coefs [ order ] + + + + + POINTER to Indices to entropy coding tables [ order ] + + + + + POINTER + + + + + POINTER + + + + + POINTER + + + + + Struct for Packet Loss Concealment + + + + + Overwrites this struct with values from another one. Equivalent to C struct assignment this = other + + + + + + Decoder state + + + + + Init Decoder State + + + + + + Resets CNG state + + + + + Resets PLC state + + + + + Encoder state + + + + + Control encoder + + I Control structure + I Target max bitrate (bps) + I Flag to allow switching audio bandwidth + I Channel number + + + + + + + + I + + + + + + + I + I + + + + + + + O + + + + + + + I + + + + + Control internal sampling rate + + I Control structure + + + + + Decoder super struct + + + + + Decoder control + + + + + Encoder Super Struct + + + + + Initialize Silk Encoder state + + I/O Pointer to Silk FIX encoder state + + + + + Variable cut-off low-pass filter state + + + + + Low pass filter state + + + + + Counter which is mapped to a cut-off frequency + + + + + Operating mode, <0: switch down, >0: switch up; 0: do nothing + + + + + Noise shaping quantization state + + + + + Buffer for quantized output signal + + + + + Prefilter state + + + + + POINTER + + + + + Noise shaping analysis state + + + + + VAD state + + + + + Analysis filterbank state: 0-8 kHz + + + + + Analysis filterbank state: 0-4 kHz + + + + + Analysis filterbank state: 0-2 kHz + + + + + Subframe energies + + + + + Smoothed energy level in each band + + + + + State of differentiator in the lowest band + + + + + Noise energy level in each band + + + + + Inverse noise energy level in each band + + + + + Noise level estimator bias/offset + + + + + Frame counter used in the initial phase + + + + + Struct for TOC (Table of Contents) + + + + + Voice activity for packet + + + + + Voice activity for each frame in packet + + + + + Flag indicating if packet contains in-band FEC + + + + + Compute number of bits to right shift the sum of squares of a vector + of int16s to make it fit in an int32 + + O Energy of x, after shifting to the right + O Number of bits right shift applied to energy + I Input vector + I Length of input vector + + + + Zero-index variant + Compute number of bits to right shift the sum of squares of a vector + of int16s to make it fit in an int32 + + O Energy of x, after shifting to the right + O Number of bits right shift applied to energy + I Input vector + I Length of input vector + + + + Cosine approximation table for LSF conversion + Q12 values (even) + + + + + Voice Activity Detection module for silk codec + + + + + Weighting factors for tilt measure + + + + + Initialization of the Silk VAD + + O Pointer to Silk VAD state. Cannot be nullptr + 0 if success + + + + Get the speech activity level in Q8 + + I/O Encoder state + I PCM input + 0 if success + + + + Noise level estimation + + I subband energies [VAD_N_BANDS] + I/O Pointer to Silk VAD state + + + diff --git a/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.xml.meta b/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.xml.meta new file mode 100644 index 0000000..f9f2e24 --- /dev/null +++ b/Assets/Packages/Concentus.2.2.2/lib/netstandard2.0/Concentus.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0a605c44bbf3e1442b933570dbd006ec +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/websocketsharp.core.1.0.1.meta b/Assets/Packages/websocketsharp.core.1.0.1.meta new file mode 100644 index 0000000..ef4161a --- /dev/null +++ b/Assets/Packages/websocketsharp.core.1.0.1.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c71850e06bbf8ea409961f68cff755a6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/websocketsharp.core.1.0.1/.signature.p7s b/Assets/Packages/websocketsharp.core.1.0.1/.signature.p7s new file mode 100644 index 0000000000000000000000000000000000000000..49bfa05cd2604e9c5076838d9e601330c068f41e GIT binary patch literal 12924 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 WM}c;Zw_Y*qd>Hv3fByfY!2bdOGQs5l literal 0 HcmV?d00001 diff --git a/Assets/Packages/websocketsharp.core.1.0.1/lib/netstandard2.0/websocket-sharp-core.dll.meta b/Assets/Packages/websocketsharp.core.1.0.1/lib/netstandard2.0/websocket-sharp-core.dll.meta new file mode 100644 index 0000000..e2b2821 --- /dev/null +++ b/Assets/Packages/websocketsharp.core.1.0.1/lib/netstandard2.0/websocket-sharp-core.dll.meta @@ -0,0 +1,29 @@ +fileFormatVersion: 2 +guid: 8cf10ca6bbf149e4197fac6bfb9bcf00 +labels: +- NuGetForUnity +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 1 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec b/Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec new file mode 100644 index 0000000..53b35ff --- /dev/null +++ b/Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec @@ -0,0 +1,13 @@ + + + + websocketsharp.core + 1.0.1 + websocket-sharp-core + websocket-sharp port to net standart + + + + + + \ No newline at end of file diff --git a/Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec.meta b/Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec.meta new file mode 100644 index 0000000..1143e41 --- /dev/null +++ b/Assets/Packages/websocketsharp.core.1.0.1/websocketsharp.core.nuspec.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7d489abb2d2cae6429d0ec59c96d0010 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Readme.asset b/Assets/Readme.asset new file mode 100644 index 0000000..77c2f83 --- /dev/null +++ b/Assets/Readme.asset @@ -0,0 +1,34 @@ +%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: fcf7219bab7fe46a1ad266029b2fee19, type: 3} + m_Name: Readme + m_EditorClassIdentifier: + icon: {fileID: 2800000, guid: 727a75301c3d24613a3ebcec4a24c2c8, type: 3} + title: URP Empty Template + sections: + - heading: Welcome to the Universal Render Pipeline + text: This template includes the settings and assets you need to start creating with the Universal Render Pipeline. + linkText: + url: + - heading: URP Documentation + text: + linkText: Read more about URP + url: https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest + - heading: Forums + text: + linkText: Get answers and support + url: https://forum.unity.com/forums/universal-render-pipeline.383/ + - heading: Report bugs + text: + linkText: Submit a report + url: https://unity3d.com/unity/qa/bug-reporting + loadedLayout: 1 diff --git a/Assets/Readme.asset.meta b/Assets/Readme.asset.meta new file mode 100644 index 0000000..ab3ad45 --- /dev/null +++ b/Assets/Readme.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8105016687592461f977c054a80ce2f2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes.meta b/Assets/Scenes.meta new file mode 100644 index 0000000..e59fb45 --- /dev/null +++ b/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9c53962885c2c4f449125a979d6ad240 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/SampleScene.unity b/Assets/Scenes/SampleScene.unity new file mode 100644 index 0000000..d0bcd6a --- /dev/null +++ b/Assets/Scenes/SampleScene.unity @@ -0,0 +1,585 @@ +%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: 10 + 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_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 0 + 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_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: 20201, guid: 0000000000000000f000000000000000, type: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 3 + 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 + buildHeightMesh: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &330585543 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 330585546} + - component: {fileID: 330585545} + - component: {fileID: 330585544} + - component: {fileID: 330585547} + 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 &330585544 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 330585543} + m_Enabled: 1 +--- !u!20 &330585545 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 330585543} + 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_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + 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 &330585546 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 330585543} + serializedVersion: 2 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &330585547 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 330585543} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_RenderShadows: 1 + m_RequiresDepthTextureOption: 2 + m_RequiresOpaqueTextureOption: 2 + m_CameraType: 0 + m_Cameras: [] + m_RendererIndex: -1 + m_VolumeLayerMask: + serializedVersion: 2 + m_Bits: 1 + m_VolumeTrigger: {fileID: 0} + m_VolumeFrameworkUpdateModeOption: 2 + m_RenderPostProcessing: 1 + m_Antialiasing: 0 + m_AntialiasingQuality: 2 + m_StopNaN: 0 + m_Dithering: 0 + m_ClearDepth: 1 + m_AllowXRRendering: 1 + m_AllowHDROutput: 1 + m_UseScreenCoordOverride: 0 + m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + m_RequiresDepthTexture: 0 + m_RequiresColorTexture: 0 + m_Version: 2 + m_TaaSettings: + m_Quality: 3 + m_FrameInfluence: 0.1 + m_JitterScale: 1 + m_MipBias: 0 + m_VarianceClampScale: 0.9 + m_ContrastAdaptiveSharpening: 0 +--- !u!1 &367748411 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 367748415} + - component: {fileID: 367748418} + - component: {fileID: 367748417} + - component: {fileID: 367748416} + m_Layer: 0 + m_Name: GameObject + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!4 &367748415 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 367748411} + serializedVersion: 2 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &367748416 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 367748411} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7a0109d5fe4f62e4b81d939f9643cdb5, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &367748417 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 367748411} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 830aab62e0babd844bcb5f89e4cfb3b4, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &367748418 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 367748411} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 65e8804865c7c6b4d9ccb1da5ec057fb, type: 3} + m_Name: + m_EditorClassIdentifier: + sampleRate: 16000 + frameSizeMs: 20 + lowFreq: 300 + highFreq: 3400 +--- !u!1 &410087039 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 410087041} + - component: {fileID: 410087040} + - component: {fileID: 410087042} + 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 &410087040 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 410087039} + m_Enabled: 1 + serializedVersion: 11 + m_Type: 1 + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_Intensity: 2 + 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: 5000 + m_UseColorTemperature: 1 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ForceVisible: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 + m_LightUnit: 1 + m_LuxAtDistance: 1 + m_EnableSpotReflector: 1 +--- !u!4 &410087041 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 410087039} + serializedVersion: 2 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!114 &410087042 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 410087039} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Version: 3 + m_UsePipelineSettings: 1 + m_AdditionalLightsShadowResolutionTier: 2 + m_LightLayerMask: 1 + m_RenderingLayers: 1 + m_CustomShadowLayers: 0 + m_ShadowLayerMask: 1 + m_ShadowRenderingLayers: 1 + m_LightCookieSize: {x: 1, y: 1} + m_LightCookieOffset: {x: 0, y: 0} + m_SoftShadowQuality: 1 +--- !u!1 &832575517 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 832575519} + - component: {fileID: 832575518} + m_Layer: 0 + m_Name: Global Volume + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &832575518 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832575517} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IsGlobal: 1 + priority: 0 + blendDistance: 0 + weight: 1 + sharedProfile: {fileID: 11400000, guid: 10fc4df2da32a41aaa32d77bc913491c, type: 2} +--- !u!4 &832575519 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 832575517} + serializedVersion: 2 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1054172748 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1054172752} + - component: {fileID: 1054172755} + - component: {fileID: 1054172754} + - component: {fileID: 1054172753} + m_Layer: 0 + m_Name: with compress + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1054172752 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1054172748} + serializedVersion: 2 + 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_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1054172753 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1054172748} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7a0109d5fe4f62e4b81d939f9643cdb5, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &1054172754 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1054172748} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 830aab62e0babd844bcb5f89e4cfb3b4, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!114 &1054172755 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1054172748} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 65e8804865c7c6b4d9ccb1da5ec057fb, type: 3} + m_Name: + m_EditorClassIdentifier: + sampleRate: 16000 + frameSizeMs: 20 + lowFreq: 300 + highFreq: 3400 +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 330585546} + - {fileID: 410087041} + - {fileID: 832575519} + - {fileID: 367748415} + - {fileID: 1054172752} diff --git a/Assets/Scenes/SampleScene.unity.meta b/Assets/Scenes/SampleScene.unity.meta new file mode 100644 index 0000000..9531828 --- /dev/null +++ b/Assets/Scenes/SampleScene.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 99c9720ab356a0642a771bea13969a05 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings.meta b/Assets/Settings.meta new file mode 100644 index 0000000..39b94dd --- /dev/null +++ b/Assets/Settings.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 709f11a7f3c4041caa4ef136ea32d874 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/DefaultVolumeProfile.asset b/Assets/Settings/DefaultVolumeProfile.asset new file mode 100644 index 0000000..9e4bbfd --- /dev/null +++ b/Assets/Settings/DefaultVolumeProfile.asset @@ -0,0 +1,983 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-9167874883656233139 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 5485954d14dfb9a4c8ead8edb0ded5b1, type: 3} + m_Name: LiftGammaGain + m_EditorClassIdentifier: + active: 1 + lift: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + gamma: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + gain: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} +--- !u!114 &-8270506406425502121 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 70afe9e12c7a7ed47911bb608a23a8ff, type: 3} + m_Name: SplitToning + m_EditorClassIdentifier: + active: 1 + shadows: + m_OverrideState: 1 + m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1} + highlights: + m_OverrideState: 1 + m_Value: {r: 0.5, g: 0.5, b: 0.5, a: 1} + balance: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-8104416584915340131 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0} + m_Name: CopyPasteTestComponent2 + m_EditorClassIdentifier: Unity.RenderPipelines.Core.Editor.Tests:UnityEditor.Rendering.Tests:VolumeComponentCopyPasteTests/CopyPasteTestComponent2 + active: 1 + p1: + m_OverrideState: 1 + m_Value: 0 + p2: + m_OverrideState: 1 + m_Value: 0 + p21: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-7750755424749557576 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 60f3b30c03e6ba64d9a27dc9dba8f28d, type: 3} + m_Name: OutlineVolumeComponent + m_EditorClassIdentifier: + active: 1 + Enabled: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-7743500325797982168 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: ccf1aba9553839d41ae37dd52e9ebcce, type: 3} + m_Name: MotionBlur + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 0 + quality: + m_OverrideState: 1 + m_Value: 0 + intensity: + m_OverrideState: 1 + m_Value: 0 + clamp: + m_OverrideState: 1 + m_Value: 0.05 +--- !u!114 &-7274224791359825572 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0fd9ee276a1023e439cf7a9c393195fa, type: 3} + m_Name: TestAnimationCurveVolumeComponent + m_EditorClassIdentifier: + active: 1 + testParameter: + m_OverrideState: 1 + m_Value: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0.5 + value: 10 + inSlope: 0 + outSlope: 10 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 15 + inSlope: 10 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!114 &-6335409530604852063 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 66f335fb1ffd8684294ad653bf1c7564, type: 3} + m_Name: ColorAdjustments + m_EditorClassIdentifier: + active: 1 + postExposure: + m_OverrideState: 1 + m_Value: 0 + contrast: + m_OverrideState: 1 + m_Value: 0 + colorFilter: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + hueShift: + m_OverrideState: 1 + m_Value: 0 + saturation: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-6288072647309666549 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 29fa0085f50d5e54f8144f766051a691, type: 3} + m_Name: FilmGrain + m_EditorClassIdentifier: + active: 1 + type: + m_OverrideState: 1 + m_Value: 0 + intensity: + m_OverrideState: 1 + m_Value: 0 + response: + m_OverrideState: 1 + m_Value: 0.8 + texture: + m_OverrideState: 1 + m_Value: {fileID: 0} +--- !u!114 &-5520245016509672950 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 97c23e3b12dc18c42a140437e53d3951, type: 3} + m_Name: Tonemapping + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 0 + neutralHDRRangeReductionMode: + m_OverrideState: 1 + m_Value: 2 + acesPreset: + m_OverrideState: 1 + m_Value: 3 + hueShiftAmount: + m_OverrideState: 1 + m_Value: 0 + detectPaperWhite: + m_OverrideState: 1 + m_Value: 0 + paperWhite: + m_OverrideState: 1 + m_Value: 300 + detectBrightnessLimits: + m_OverrideState: 1 + m_Value: 1 + minNits: + m_OverrideState: 1 + m_Value: 0.005 + maxNits: + m_OverrideState: 1 + m_Value: 1000 +--- !u!114 &-5360449096862653589 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0} + m_Name: VolumeComponentSupportedEverywhere + m_EditorClassIdentifier: Unity.RenderPipelines.Core.Editor.Tests:UnityEngine.Rendering.Tests:VolumeComponentEditorSupportedOnTests/VolumeComponentSupportedEverywhere + active: 1 +--- !u!114 &-5139089513906902183 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 5a00a63fdd6bd2a45ab1f2d869305ffd, type: 3} + m_Name: OasisFogVolumeComponent + m_EditorClassIdentifier: + active: 1 + Density: + m_OverrideState: 1 + m_Value: 0 + StartDistance: + m_OverrideState: 1 + m_Value: 0 + HeightRange: + m_OverrideState: 1 + m_Value: {x: 0, y: 50} + Tint: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + SunScatteringIntensity: + m_OverrideState: 1 + m_Value: 2 +--- !u!114 &-4463884970436517307 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: fb60a22f311433c4c962b888d1393f88, type: 3} + m_Name: PaniniProjection + m_EditorClassIdentifier: + active: 1 + distance: + m_OverrideState: 1 + m_Value: 0 + cropToFit: + m_OverrideState: 1 + m_Value: 1 +--- !u!114 &-1410297666881709256 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 6bd486065ce11414fa40e631affc4900, type: 3} + m_Name: ProbeVolumesOptions + m_EditorClassIdentifier: + active: 1 + normalBias: + m_OverrideState: 1 + m_Value: 0.33 + viewBias: + m_OverrideState: 1 + m_Value: 0 + scaleBiasWithMinProbeDistance: + m_OverrideState: 1 + m_Value: 0 + samplingNoise: + m_OverrideState: 1 + m_Value: 0.1 + animateSamplingNoise: + m_OverrideState: 1 + m_Value: 1 + leakReductionMode: + m_OverrideState: 1 + m_Value: 1 + minValidDotProductValue: + m_OverrideState: 1 + m_Value: 0.1 + occlusionOnlyReflectionNormalization: + m_OverrideState: 1 + m_Value: 1 + intensityMultiplier: + m_OverrideState: 1 + m_Value: 1 + skyOcclusionIntensityMultiplier: + m_OverrideState: 1 + m_Value: 1 + worldOffset: + m_OverrideState: 1 + m_Value: {x: 0, y: 0, z: 0} +--- !u!114 &-1216621516061285780 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0b2db86121404754db890f4c8dfe81b2, type: 3} + m_Name: Bloom + m_EditorClassIdentifier: + active: 1 + skipIterations: + m_OverrideState: 1 + m_Value: 1 + threshold: + m_OverrideState: 1 + m_Value: 0.9 + intensity: + m_OverrideState: 1 + m_Value: 0 + scatter: + m_OverrideState: 1 + m_Value: 0.7 + clamp: + m_OverrideState: 1 + m_Value: 65472 + tint: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + highQualityFiltering: + m_OverrideState: 1 + m_Value: 0 + downscale: + m_OverrideState: 1 + m_Value: 0 + maxIterations: + m_OverrideState: 1 + m_Value: 6 + dirtTexture: + m_OverrideState: 1 + m_Value: {fileID: 0} + dimension: 1 + dirtIntensity: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-1170528603972255243 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 221518ef91623a7438a71fef23660601, type: 3} + m_Name: WhiteBalance + m_EditorClassIdentifier: + active: 1 + temperature: + m_OverrideState: 1 + m_Value: 0 + tint: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &-581120513425526550 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0} + m_Name: CopyPasteTestComponent3 + m_EditorClassIdentifier: Unity.RenderPipelines.Core.Editor.Tests:UnityEditor.Rendering.Tests:VolumeComponentCopyPasteTests/CopyPasteTestComponent3 + active: 1 + p1: + m_OverrideState: 1 + m_Value: 0 + p2: + m_OverrideState: 1 + m_Value: 0 + p31: + m_OverrideState: 1 + m_Value: {r: 0, g: 0, b: 0, a: 1} +--- !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: d7fd9488000d3734a9e00ee676215985, type: 3} + m_Name: DefaultVolumeProfile + m_EditorClassIdentifier: + components: + - {fileID: -9167874883656233139} + - {fileID: 1918650496244738858} + - {fileID: 853819529557874667} + - {fileID: 1052315754049611418} + - {fileID: -1170528603972255243} + - {fileID: -8270506406425502121} + - {fileID: -5520245016509672950} + - {fileID: 7173750748008157695} + - {fileID: 1666464333004379222} + - {fileID: 9001657382290151224} + - {fileID: -6335409530604852063} + - {fileID: -1216621516061285780} + - {fileID: 3959858460715838825} + - {fileID: -7743500325797982168} + - {fileID: 4644742534064026673} + - {fileID: -4463884970436517307} + - {fileID: -6288072647309666549} + - {fileID: 7518938298396184218} + - {fileID: -1410297666881709256} +--- !u!114 &853819529557874667 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 06437c1ff663d574d9447842ba0a72e4, type: 3} + m_Name: ScreenSpaceLensFlare + m_EditorClassIdentifier: + active: 1 + intensity: + m_OverrideState: 1 + m_Value: 0 + tintColor: + m_OverrideState: 1 + m_Value: {r: 1, g: 1, b: 1, a: 1} + bloomMip: + m_OverrideState: 1 + m_Value: 1 + firstFlareIntensity: + m_OverrideState: 1 + m_Value: 1 + secondaryFlareIntensity: + m_OverrideState: 1 + m_Value: 1 + warpedFlareIntensity: + m_OverrideState: 1 + m_Value: 1 + warpedFlareScale: + m_OverrideState: 1 + m_Value: {x: 1, y: 1} + samples: + m_OverrideState: 1 + m_Value: 1 + sampleDimmer: + m_OverrideState: 1 + m_Value: 0.5 + vignetteEffect: + m_OverrideState: 1 + m_Value: 1 + startingPosition: + m_OverrideState: 1 + m_Value: 1.25 + scale: + m_OverrideState: 1 + m_Value: 1.5 + streaksIntensity: + m_OverrideState: 1 + m_Value: 0 + streaksLength: + m_OverrideState: 1 + m_Value: 0.5 + streaksOrientation: + m_OverrideState: 1 + m_Value: 0 + streaksThreshold: + m_OverrideState: 1 + m_Value: 0.25 + resolution: + m_OverrideState: 1 + m_Value: 4 + chromaticAbberationIntensity: + m_OverrideState: 1 + m_Value: 0.5 +--- !u!114 &1052315754049611418 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 558a8e2b6826cf840aae193990ba9f2e, type: 3} + m_Name: ShadowsMidtonesHighlights + m_EditorClassIdentifier: + active: 1 + shadows: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + midtones: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + highlights: + m_OverrideState: 1 + m_Value: {x: 1, y: 1, z: 1, w: 0} + shadowsStart: + m_OverrideState: 1 + m_Value: 0 + shadowsEnd: + m_OverrideState: 1 + m_Value: 0.3 + highlightsStart: + m_OverrideState: 1 + m_Value: 0.55 + highlightsEnd: + m_OverrideState: 1 + m_Value: 1 +--- !u!114 &1666464333004379222 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 3eb4b772797da9440885e8bd939e9560, type: 3} + m_Name: ColorCurves + m_EditorClassIdentifier: + active: 1 + master: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + red: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + green: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + blue: + m_OverrideState: 1 + m_Value: + k__BackingField: 2 + m_Loop: 0 + m_ZeroValue: 0 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + - serializedVersion: 3 + time: 1 + value: 1 + inSlope: 1 + outSlope: 1 + tangentMode: 0 + weightedMode: 0 + inWeight: 0 + outWeight: 0 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + hueVsHue: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 1 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + hueVsSat: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 1 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + satVsSat: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 0 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + lumVsSat: + m_OverrideState: 1 + m_Value: + k__BackingField: 0 + m_Loop: 0 + m_ZeroValue: 0.5 + m_Range: 1 + m_Curve: + serializedVersion: 2 + m_Curve: [] + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!114 &1918650496244738858 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: e021b4c809a781e468c2988c016ebbea, type: 3} + m_Name: ColorLookup + m_EditorClassIdentifier: + active: 1 + texture: + m_OverrideState: 1 + m_Value: {fileID: 0} + dimension: 1 + contribution: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &3959858460715838825 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: c01700fd266d6914ababb731e09af2eb, type: 3} + m_Name: DepthOfField + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 0 + gaussianStart: + m_OverrideState: 1 + m_Value: 10 + gaussianEnd: + m_OverrideState: 1 + m_Value: 30 + gaussianMaxRadius: + m_OverrideState: 1 + m_Value: 1 + highQualitySampling: + m_OverrideState: 1 + m_Value: 0 + focusDistance: + m_OverrideState: 1 + m_Value: 10 + aperture: + m_OverrideState: 1 + m_Value: 5.6 + focalLength: + m_OverrideState: 1 + m_Value: 50 + bladeCount: + m_OverrideState: 1 + m_Value: 5 + bladeCurvature: + m_OverrideState: 1 + m_Value: 1 + bladeRotation: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &4251301726029935498 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 74955a4b0b4243bc87231e8b59ed9140, type: 3} + m_Name: TestVolume + m_EditorClassIdentifier: + active: 1 + param: + m_OverrideState: 1 + m_Value: 123 +--- !u!114 &4644742534064026673 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 81180773991d8724ab7f2d216912b564, type: 3} + m_Name: ChromaticAberration + m_EditorClassIdentifier: + active: 1 + intensity: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &6940869943325143175 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0} + m_Name: VolumeComponentSupportedOnAnySRP + m_EditorClassIdentifier: Unity.RenderPipelines.Core.Editor.Tests:UnityEngine.Rendering.Tests:VolumeComponentEditorSupportedOnTests/VolumeComponentSupportedOnAnySRP + active: 1 +--- !u!114 &7173750748008157695 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 899c54efeace73346a0a16faa3afe726, type: 3} + m_Name: Vignette + m_EditorClassIdentifier: + active: 1 + color: + m_OverrideState: 1 + m_Value: {r: 0, g: 0, b: 0, a: 1} + center: + m_OverrideState: 1 + m_Value: {x: 0.5, y: 0.5} + intensity: + m_OverrideState: 1 + m_Value: 0 + smoothness: + m_OverrideState: 1 + m_Value: 0.2 + rounded: + m_OverrideState: 1 + m_Value: 0 +--- !u!114 &7518938298396184218 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: c5e1dc532bcb41949b58bc4f2abfbb7e, type: 3} + m_Name: LensDistortion + m_EditorClassIdentifier: + active: 1 + intensity: + m_OverrideState: 1 + m_Value: 0 + xMultiplier: + m_OverrideState: 1 + m_Value: 1 + yMultiplier: + m_OverrideState: 1 + m_Value: 1 + center: + m_OverrideState: 1 + m_Value: {x: 0.5, y: 0.5} + scale: + m_OverrideState: 1 + m_Value: 1 +--- !u!114 &9001657382290151224 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: cdfbdbb87d3286943a057f7791b43141, type: 3} + m_Name: ChannelMixer + m_EditorClassIdentifier: + active: 1 + redOutRedIn: + m_OverrideState: 1 + m_Value: 100 + redOutGreenIn: + m_OverrideState: 1 + m_Value: 0 + redOutBlueIn: + m_OverrideState: 1 + m_Value: 0 + greenOutRedIn: + m_OverrideState: 1 + m_Value: 0 + greenOutGreenIn: + m_OverrideState: 1 + m_Value: 100 + greenOutBlueIn: + m_OverrideState: 1 + m_Value: 0 + blueOutRedIn: + m_OverrideState: 1 + m_Value: 0 + blueOutGreenIn: + m_OverrideState: 1 + m_Value: 0 + blueOutBlueIn: + m_OverrideState: 1 + m_Value: 100 +--- !u!114 &9122958982931076880 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0} + m_Name: CopyPasteTestComponent1 + m_EditorClassIdentifier: Unity.RenderPipelines.Core.Editor.Tests:UnityEditor.Rendering.Tests:VolumeComponentCopyPasteTests/CopyPasteTestComponent1 + active: 1 + p1: + m_OverrideState: 1 + m_Value: 0 + p2: + m_OverrideState: 1 + m_Value: 0 diff --git a/Assets/Settings/DefaultVolumeProfile.asset.meta b/Assets/Settings/DefaultVolumeProfile.asset.meta new file mode 100644 index 0000000..53b314a --- /dev/null +++ b/Assets/Settings/DefaultVolumeProfile.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ab09877e2e707104187f6f83e2f62510 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/Mobile_RPAsset.asset b/Assets/Settings/Mobile_RPAsset.asset new file mode 100644 index 0000000..0fdefd1 --- /dev/null +++ b/Assets/Settings/Mobile_RPAsset.asset @@ -0,0 +1,135 @@ +%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: bf2edee5c58d82540a51f03df9d42094, type: 3} + m_Name: Mobile_RPAsset + m_EditorClassIdentifier: + k_AssetVersion: 12 + k_AssetPreviousVersion: 12 + m_RendererType: 1 + m_RendererData: {fileID: 0} + m_RendererDataList: + - {fileID: 11400000, guid: 65bc7dbf4170f435aa868c779acfb082, type: 2} + m_DefaultRendererIndex: 0 + m_RequireDepthTexture: 0 + m_RequireOpaqueTexture: 0 + m_OpaqueDownsampling: 0 + m_SupportsTerrainHoles: 1 + m_SupportsHDR: 1 + m_HDRColorBufferPrecision: 0 + m_MSAA: 1 + m_RenderScale: 0.8 + m_UpscalingFilter: 3 + m_FsrOverrideSharpness: 0 + m_FsrSharpness: 0.92 + m_EnableLODCrossFade: 1 + m_LODCrossFadeDitheringType: 1 + m_ShEvalMode: 0 + m_LightProbeSystem: 0 + m_ProbeVolumeMemoryBudget: 1024 + m_ProbeVolumeBlendingMemoryBudget: 256 + m_SupportProbeVolumeGPUStreaming: 0 + m_SupportProbeVolumeDiskStreaming: 0 + m_SupportProbeVolumeScenarios: 0 + m_SupportProbeVolumeScenarioBlending: 0 + m_ProbeVolumeSHBands: 1 + m_MainLightRenderingMode: 1 + m_MainLightShadowsSupported: 1 + m_MainLightShadowmapResolution: 1024 + m_AdditionalLightsRenderingMode: 1 + m_AdditionalLightsPerObjectLimit: 4 + m_AdditionalLightShadowsSupported: 0 + m_AdditionalLightsShadowmapResolution: 2048 + m_AdditionalLightsShadowResolutionTierLow: 256 + m_AdditionalLightsShadowResolutionTierMedium: 512 + m_AdditionalLightsShadowResolutionTierHigh: 1024 + m_ReflectionProbeBlending: 1 + m_ReflectionProbeBoxProjection: 1 + m_ShadowDistance: 50 + m_ShadowCascadeCount: 1 + m_Cascade2Split: 0.25 + m_Cascade3Split: {x: 0.1, y: 0.3} + m_Cascade4Split: {x: 0.067, y: 0.2, z: 0.467} + m_CascadeBorder: 0.2 + m_ShadowDepthBias: 1 + m_ShadowNormalBias: 1 + m_AnyShadowsSupported: 1 + m_SoftShadowsSupported: 0 + m_ConservativeEnclosingSphere: 1 + m_NumIterationsEnclosingSphere: 64 + m_SoftShadowQuality: 2 + m_AdditionalLightsCookieResolution: 1024 + m_AdditionalLightsCookieFormat: 1 + m_UseSRPBatcher: 1 + m_SupportsDynamicBatching: 0 + m_MixedLightingSupported: 1 + m_SupportsLightCookies: 1 + m_SupportsLightLayers: 1 + m_DebugLevel: 0 + m_StoreActionsOptimization: 0 + m_UseAdaptivePerformance: 1 + m_ColorGradingMode: 0 + m_ColorGradingLutSize: 32 + m_UseFastSRGBLinearConversion: 1 + m_SupportDataDrivenLensFlare: 1 + m_SupportScreenSpaceLensFlare: 1 + m_GPUResidentDrawerMode: 0 + m_UseLegacyLightmaps: 0 + m_SmallMeshScreenPercentage: 0 + m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0 + m_ShadowType: 1 + m_LocalShadowsSupported: 0 + m_LocalShadowsAtlasResolution: 256 + m_MaxPixelLights: 0 + m_ShadowAtlasResolution: 256 + m_VolumeFrameworkUpdateMode: 0 + m_VolumeProfile: {fileID: 11400000, guid: 10fc4df2da32a41aaa32d77bc913491c, type: 2} + apvScenesData: + obsoleteSceneBounds: + m_Keys: [] + m_Values: [] + obsoleteHasProbeVolumes: + m_Keys: [] + m_Values: + m_PrefilteringModeMainLightShadows: 3 + m_PrefilteringModeAdditionalLight: 4 + m_PrefilteringModeAdditionalLightShadows: 0 + m_PrefilterXRKeywords: 1 + m_PrefilteringModeForwardPlus: 1 + m_PrefilteringModeDeferredRendering: 0 + m_PrefilteringModeScreenSpaceOcclusion: 0 + m_PrefilterDebugKeywords: 1 + m_PrefilterWriteRenderingLayers: 1 + m_PrefilterHDROutput: 1 + m_PrefilterSSAODepthNormals: 1 + m_PrefilterSSAOSourceDepthLow: 1 + m_PrefilterSSAOSourceDepthMedium: 0 + m_PrefilterSSAOSourceDepthHigh: 1 + m_PrefilterSSAOInterleaved: 0 + m_PrefilterSSAOBlueNoise: 1 + m_PrefilterSSAOSampleCountLow: 1 + m_PrefilterSSAOSampleCountMedium: 0 + m_PrefilterSSAOSampleCountHigh: 1 + m_PrefilterDBufferMRT1: 1 + m_PrefilterDBufferMRT2: 1 + m_PrefilterDBufferMRT3: 1 + m_PrefilterSoftShadowsQualityLow: 1 + m_PrefilterSoftShadowsQualityMedium: 1 + m_PrefilterSoftShadowsQualityHigh: 1 + m_PrefilterSoftShadows: 0 + m_PrefilterScreenCoord: 1 + m_PrefilterNativeRenderPass: 1 + m_PrefilterUseLegacyLightmaps: 0 + m_ShaderVariantLogLevel: 0 + m_ShadowCascades: 0 + m_Textures: + blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3} + bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3} diff --git a/Assets/Settings/Mobile_RPAsset.asset.meta b/Assets/Settings/Mobile_RPAsset.asset.meta new file mode 100644 index 0000000..3660d15 --- /dev/null +++ b/Assets/Settings/Mobile_RPAsset.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5e6cbd92db86f4b18aec3ed561671858 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/Mobile_Renderer.asset b/Assets/Settings/Mobile_Renderer.asset new file mode 100644 index 0000000..99d4fa5 --- /dev/null +++ b/Assets/Settings/Mobile_Renderer.asset @@ -0,0 +1,52 @@ +%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: de640fe3d0db1804a85f9fc8f5cadab6, type: 3} + m_Name: Mobile_Renderer + m_EditorClassIdentifier: + debugShaders: + debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, + type: 3} + hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3} + probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, + type: 3} + probeVolumeResources: + probeVolumeDebugShader: {fileID: 0} + probeVolumeFragmentationDebugShader: {fileID: 0} + probeVolumeOffsetDebugShader: {fileID: 0} + probeVolumeSamplingDebugShader: {fileID: 0} + probeSamplingDebugMesh: {fileID: 0} + probeSamplingDebugTexture: {fileID: 0} + probeVolumeBlendStatesCS: {fileID: 0} + m_RendererFeatures: [] + m_RendererFeatureMap: + m_UseNativeRenderPass: 1 + postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2} + m_AssetVersion: 2 + m_OpaqueLayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_TransparentLayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_DefaultStencilState: + overrideStencilState: 0 + stencilReference: 0 + stencilCompareFunction: 8 + passOperation: 2 + failOperation: 0 + zFailOperation: 0 + m_ShadowTransparentReceive: 0 + m_RenderingMode: 2 + m_DepthPrimingMode: 0 + m_CopyDepthMode: 0 + m_AccurateGbufferNormals: 0 + m_IntermediateTextureMode: 0 diff --git a/Assets/Settings/Mobile_Renderer.asset.meta b/Assets/Settings/Mobile_Renderer.asset.meta new file mode 100644 index 0000000..a3588b1 --- /dev/null +++ b/Assets/Settings/Mobile_Renderer.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 65bc7dbf4170f435aa868c779acfb082 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/PC_RPAsset.asset b/Assets/Settings/PC_RPAsset.asset new file mode 100644 index 0000000..fb240e0 --- /dev/null +++ b/Assets/Settings/PC_RPAsset.asset @@ -0,0 +1,136 @@ +%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: bf2edee5c58d82540a51f03df9d42094, type: 3} + m_Name: PC_RPAsset + m_EditorClassIdentifier: + k_AssetVersion: 12 + k_AssetPreviousVersion: 12 + m_RendererType: 1 + m_RendererData: {fileID: 0} + m_RendererDataList: + - {fileID: 11400000, guid: f288ae1f4751b564a96ac7587541f7a2, type: 2} + m_DefaultRendererIndex: 0 + m_RequireDepthTexture: 1 + m_RequireOpaqueTexture: 1 + m_OpaqueDownsampling: 1 + m_SupportsTerrainHoles: 1 + m_SupportsHDR: 1 + m_HDRColorBufferPrecision: 0 + m_MSAA: 1 + m_RenderScale: 1 + m_UpscalingFilter: 0 + m_FsrOverrideSharpness: 0 + m_FsrSharpness: 0.92 + m_EnableLODCrossFade: 1 + m_LODCrossFadeDitheringType: 1 + m_ShEvalMode: 0 + m_LightProbeSystem: 0 + m_ProbeVolumeMemoryBudget: 1024 + m_ProbeVolumeBlendingMemoryBudget: 256 + m_SupportProbeVolumeGPUStreaming: 0 + m_SupportProbeVolumeDiskStreaming: 0 + m_SupportProbeVolumeScenarios: 0 + m_SupportProbeVolumeScenarioBlending: 0 + m_ProbeVolumeSHBands: 1 + m_MainLightRenderingMode: 1 + m_MainLightShadowsSupported: 1 + m_MainLightShadowmapResolution: 2048 + m_AdditionalLightsRenderingMode: 1 + m_AdditionalLightsPerObjectLimit: 4 + m_AdditionalLightShadowsSupported: 1 + m_AdditionalLightsShadowmapResolution: 2048 + m_AdditionalLightsShadowResolutionTierLow: 256 + m_AdditionalLightsShadowResolutionTierMedium: 512 + m_AdditionalLightsShadowResolutionTierHigh: 1024 + m_ReflectionProbeBlending: 1 + m_ReflectionProbeBoxProjection: 1 + m_ShadowDistance: 50 + m_ShadowCascadeCount: 4 + m_Cascade2Split: 0.25 + m_Cascade3Split: {x: 0.1, y: 0.3} + m_Cascade4Split: {x: 0.12299999, y: 0.2926, z: 0.53599995} + m_CascadeBorder: 0.107758604 + m_ShadowDepthBias: 0.1 + m_ShadowNormalBias: 0.5 + m_AnyShadowsSupported: 1 + m_SoftShadowsSupported: 1 + m_ConservativeEnclosingSphere: 1 + m_NumIterationsEnclosingSphere: 64 + m_SoftShadowQuality: 3 + m_AdditionalLightsCookieResolution: 2048 + m_AdditionalLightsCookieFormat: 3 + m_UseSRPBatcher: 1 + m_SupportsDynamicBatching: 0 + m_MixedLightingSupported: 1 + m_SupportsLightCookies: 1 + m_SupportsLightLayers: 1 + m_DebugLevel: 0 + m_StoreActionsOptimization: 0 + m_UseAdaptivePerformance: 1 + m_ColorGradingMode: 0 + m_ColorGradingLutSize: 32 + m_AllowPostProcessAlphaOutput: 0 + m_UseFastSRGBLinearConversion: 0 + m_SupportDataDrivenLensFlare: 1 + m_SupportScreenSpaceLensFlare: 1 + m_GPUResidentDrawerMode: 0 + m_SmallMeshScreenPercentage: 0 + m_GPUResidentDrawerEnableOcclusionCullingInCameras: 0 + m_ShadowType: 1 + m_LocalShadowsSupported: 0 + m_LocalShadowsAtlasResolution: 256 + m_MaxPixelLights: 0 + m_ShadowAtlasResolution: 256 + m_VolumeFrameworkUpdateMode: 0 + m_VolumeProfile: {fileID: 11400000, guid: 10fc4df2da32a41aaa32d77bc913491c, type: 2} + apvScenesData: + obsoleteSceneBounds: + m_Keys: [] + m_Values: [] + obsoleteHasProbeVolumes: + m_Keys: [] + m_Values: + m_PrefilteringModeMainLightShadows: 3 + m_PrefilteringModeAdditionalLight: 0 + m_PrefilteringModeAdditionalLightShadows: 2 + m_PrefilterXRKeywords: 1 + m_PrefilteringModeForwardPlus: 2 + m_PrefilteringModeDeferredRendering: 0 + m_PrefilteringModeScreenSpaceOcclusion: 2 + m_PrefilterDebugKeywords: 1 + m_PrefilterWriteRenderingLayers: 1 + m_PrefilterHDROutput: 1 + m_PrefilterAlphaOutput: 1 + m_PrefilterSSAODepthNormals: 0 + m_PrefilterSSAOSourceDepthLow: 1 + m_PrefilterSSAOSourceDepthMedium: 1 + m_PrefilterSSAOSourceDepthHigh: 1 + m_PrefilterSSAOInterleaved: 1 + m_PrefilterSSAOBlueNoise: 0 + m_PrefilterSSAOSampleCountLow: 1 + m_PrefilterSSAOSampleCountMedium: 0 + m_PrefilterSSAOSampleCountHigh: 1 + m_PrefilterDBufferMRT1: 1 + m_PrefilterDBufferMRT2: 1 + m_PrefilterDBufferMRT3: 1 + m_PrefilterSoftShadowsQualityLow: 1 + m_PrefilterSoftShadowsQualityMedium: 1 + m_PrefilterSoftShadowsQualityHigh: 1 + m_PrefilterSoftShadows: 0 + m_PrefilterScreenCoord: 1 + m_PrefilterNativeRenderPass: 1 + m_PrefilterUseLegacyLightmaps: 0 + m_ShaderVariantLogLevel: 0 + m_ShadowCascades: 0 + m_Textures: + blueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3} + bayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3} diff --git a/Assets/Settings/PC_RPAsset.asset.meta b/Assets/Settings/PC_RPAsset.asset.meta new file mode 100644 index 0000000..e286b2f --- /dev/null +++ b/Assets/Settings/PC_RPAsset.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b83569d67af61e458304325a23e5dfd +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/PC_Renderer.asset b/Assets/Settings/PC_Renderer.asset new file mode 100644 index 0000000..475b02e --- /dev/null +++ b/Assets/Settings/PC_Renderer.asset @@ -0,0 +1,95 @@ +%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: de640fe3d0db1804a85f9fc8f5cadab6, type: 3} + m_Name: PC_Renderer + m_EditorClassIdentifier: + debugShaders: + debugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, + type: 3} + hdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3} + probeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, + type: 3} + probeVolumeResources: + probeVolumeDebugShader: {fileID: 4800000, guid: e5c6678ed2aaa91408dd3df699057aae, + type: 3} + probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 03cfc4915c15d504a9ed85ecc404e607, + type: 3} + probeVolumeOffsetDebugShader: {fileID: 4800000, guid: 53a11f4ebaebf4049b3638ef78dc9664, + type: 3} + probeVolumeSamplingDebugShader: {fileID: 4800000, guid: 8f96cd657dc40064aa21efcc7e50a2e7, + type: 3} + probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 57d7c4c16e2765b47a4d2069b311bffe, + type: 3} + probeSamplingDebugTexture: {fileID: 2800000, guid: 24ec0e140fb444a44ab96ee80844e18e, + type: 3} + probeVolumeBlendStatesCS: {fileID: 7200000, guid: b9a23f869c4fd45f19c5ada54dd82176, + type: 3} + m_RendererFeatures: + - {fileID: 7833122117494664109} + m_RendererFeatureMap: ad6b866f10d7b46c + m_UseNativeRenderPass: 1 + postProcessData: {fileID: 11400000, guid: 41439944d30ece34e96484bdb6645b55, type: 2} + m_AssetVersion: 2 + m_OpaqueLayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_TransparentLayerMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_DefaultStencilState: + overrideStencilState: 0 + stencilReference: 1 + stencilCompareFunction: 3 + passOperation: 2 + failOperation: 0 + zFailOperation: 0 + m_ShadowTransparentReceive: 1 + m_RenderingMode: 2 + m_DepthPrimingMode: 0 + m_CopyDepthMode: 0 + m_AccurateGbufferNormals: 0 + m_IntermediateTextureMode: 0 +--- !u!114 &7833122117494664109 +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: f62c9c65cf3354c93be831c8bc075510, type: 3} + m_Name: ScreenSpaceAmbientOcclusion + m_EditorClassIdentifier: + m_Active: 1 + m_Settings: + AOMethod: 0 + Downsample: 0 + AfterOpaque: 0 + Source: 1 + NormalSamples: 1 + Intensity: 0.4 + DirectLightingStrength: 0.25 + Radius: 0.3 + Samples: 1 + BlurQuality: 0 + Falloff: 100 + SampleCount: -1 + m_BlueNoise256Textures: + - {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3} + - {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3} + - {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3} + - {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3} + - {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3} + - {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3} + - {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3} + m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3} diff --git a/Assets/Settings/PC_Renderer.asset.meta b/Assets/Settings/PC_Renderer.asset.meta new file mode 100644 index 0000000..ddae6a5 --- /dev/null +++ b/Assets/Settings/PC_Renderer.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f288ae1f4751b564a96ac7587541f7a2 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/SampleSceneProfile.asset b/Assets/Settings/SampleSceneProfile.asset new file mode 100644 index 0000000..c1b0f63 --- /dev/null +++ b/Assets/Settings/SampleSceneProfile.asset @@ -0,0 +1,159 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &-7893295128165547882 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 0b2db86121404754db890f4c8dfe81b2, type: 3} + m_Name: Bloom + m_EditorClassIdentifier: + active: 1 + skipIterations: + m_OverrideState: 1 + m_Value: 0 + threshold: + m_OverrideState: 1 + m_Value: 1 + intensity: + m_OverrideState: 1 + m_Value: 0.25 + scatter: + m_OverrideState: 1 + m_Value: 0.5 + clamp: + m_OverrideState: 0 + m_Value: 65472 + tint: + m_OverrideState: 0 + m_Value: {r: 1, g: 1, b: 1, a: 1} + highQualityFiltering: + m_OverrideState: 1 + m_Value: 1 + downscale: + m_OverrideState: 0 + m_Value: 0 + maxIterations: + m_OverrideState: 0 + m_Value: 6 + dirtTexture: + m_OverrideState: 0 + m_Value: {fileID: 0} + dimension: 1 + dirtIntensity: + m_OverrideState: 0 + m_Value: 0 +--- !u!114 &-3357603926938260329 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 899c54efeace73346a0a16faa3afe726, type: 3} + m_Name: Vignette + m_EditorClassIdentifier: + active: 1 + color: + m_OverrideState: 0 + m_Value: {r: 0, g: 0, b: 0, a: 1} + center: + m_OverrideState: 0 + m_Value: {x: 0.5, y: 0.5} + intensity: + m_OverrideState: 1 + m_Value: 0.2 + smoothness: + m_OverrideState: 0 + m_Value: 0.2 + rounded: + m_OverrideState: 0 + m_Value: 0 +--- !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: d7fd9488000d3734a9e00ee676215985, type: 3} + m_Name: SampleSceneProfile + m_EditorClassIdentifier: + components: + - {fileID: 849379129802519247} + - {fileID: -7893295128165547882} + - {fileID: 7391319092446245454} + - {fileID: -3357603926938260329} +--- !u!114 &849379129802519247 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: 97c23e3b12dc18c42a140437e53d3951, type: 3} + m_Name: Tonemapping + m_EditorClassIdentifier: + active: 1 + mode: + m_OverrideState: 1 + m_Value: 1 + neutralHDRRangeReductionMode: + m_OverrideState: 0 + m_Value: 2 + acesPreset: + m_OverrideState: 0 + m_Value: 3 + hueShiftAmount: + m_OverrideState: 0 + m_Value: 0 + detectPaperWhite: + m_OverrideState: 1 + m_Value: 0 + paperWhite: + m_OverrideState: 1 + m_Value: 234 + detectBrightnessLimits: + m_OverrideState: 1 + m_Value: 1 + minNits: + m_OverrideState: 1 + m_Value: 0.005 + maxNits: + m_OverrideState: 1 + m_Value: 647 +--- !u!114 &7391319092446245454 +MonoBehaviour: + m_ObjectHideFlags: 3 + 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: ccf1aba9553839d41ae37dd52e9ebcce, type: 3} + m_Name: MotionBlur + m_EditorClassIdentifier: + active: 0 + mode: + m_OverrideState: 0 + m_Value: 0 + quality: + m_OverrideState: 1 + m_Value: 2 + intensity: + m_OverrideState: 1 + m_Value: 0.6 + clamp: + m_OverrideState: 0 + m_Value: 0.05 diff --git a/Assets/Settings/SampleSceneProfile.asset.meta b/Assets/Settings/SampleSceneProfile.asset.meta new file mode 100644 index 0000000..b82270c --- /dev/null +++ b/Assets/Settings/SampleSceneProfile.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 10fc4df2da32a41aaa32d77bc913491c +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset b/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset new file mode 100644 index 0000000..6255cff --- /dev/null +++ b/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset @@ -0,0 +1,367 @@ +%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: 2ec995e51a6e251468d2a3fd8a686257, type: 3} + m_Name: UniversalRenderPipelineGlobalSettings + m_EditorClassIdentifier: + m_ShaderStrippingSetting: + m_Version: 0 + m_ExportShaderVariants: 1 + m_ShaderVariantLogLevel: 0 + m_StripRuntimeDebugShaders: 1 + m_URPShaderStrippingSetting: + m_Version: 0 + m_StripUnusedPostProcessingVariants: 1 + m_StripUnusedVariants: 1 + m_StripScreenCoordOverrideVariants: 1 + m_ShaderVariantLogLevel: 0 + m_ExportShaderVariants: 1 + m_StripDebugVariants: 1 + m_StripUnusedPostProcessingVariants: 1 + m_StripUnusedVariants: 1 + m_StripScreenCoordOverrideVariants: 1 + supportRuntimeDebugDisplay: 0 + m_EnableRenderGraph: 0 + m_Settings: + m_SettingsList: + m_List: + - rid: 6852985685364965376 + - rid: 6852985685364965377 + - rid: 6852985685364965378 + - rid: 6852985685364965379 + - rid: 6852985685364965380 + - rid: 6852985685364965381 + - rid: 6852985685364965382 + - rid: 6852985685364965383 + - rid: 6852985685364965384 + - rid: 6852985685364965385 + - rid: 6852985685364965386 + - rid: 6852985685364965387 + - rid: 6852985685364965388 + - rid: 6852985685364965389 + - rid: 6852985685364965390 + - rid: 6852985685364965391 + - rid: 6852985685364965392 + - rid: 6852985685364965393 + - rid: 6852985685364965394 + - rid: 8712630790384254976 + - rid: 6142483855146221568 + - rid: 6142483855146221569 + - rid: 6142483855146221570 + - rid: 6142483855146221571 + - rid: 6142483855146221572 + - rid: 6142483855146221573 + m_RuntimeSettings: + m_List: + - rid: 6852985685364965378 + - rid: 6852985685364965379 + - rid: 6852985685364965380 + - rid: 6852985685364965381 + - rid: 6852985685364965384 + - rid: 6852985685364965385 + - rid: 6852985685364965392 + - rid: 6852985685364965394 + - rid: 8712630790384254976 + - rid: 6142483855146221568 + - rid: 6142483855146221570 + - rid: 6142483855146221573 + m_AssetVersion: 8 + m_ObsoleteDefaultVolumeProfile: {fileID: 0} + m_RenderingLayerNames: + - Light Layer default + - Light Layer 1 + - Light Layer 2 + - Light Layer 3 + - Light Layer 4 + - Light Layer 5 + - Light Layer 6 + - Light Layer 7 + m_ValidRenderingLayers: 0 + lightLayerName0: Light Layer default + lightLayerName1: Light Layer 1 + lightLayerName2: Light Layer 2 + lightLayerName3: Light Layer 3 + lightLayerName4: Light Layer 4 + lightLayerName5: Light Layer 5 + lightLayerName6: Light Layer 6 + lightLayerName7: Light Layer 7 + apvScenesData: + obsoleteSceneBounds: + m_Keys: [] + m_Values: [] + obsoleteHasProbeVolumes: + m_Keys: [] + m_Values: + references: + version: 2 + RefIds: + - rid: 6142483855146221568 + type: {class: ScreenSpaceAmbientOcclusionPersistentResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Shader: {fileID: 4800000, guid: 0849e84e3d62649e8882e9d6f056a017, type: 3} + m_Version: 0 + - rid: 6142483855146221569 + type: {class: PostProcessData/TextureResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + blueNoise16LTex: + - {fileID: 2800000, guid: 81200413a40918d4d8702e94db29911c, type: 3} + - {fileID: 2800000, guid: d50c5e07c9911a74982bddf7f3075e7b, type: 3} + - {fileID: 2800000, guid: 1134690bf9216164dbc75050e35b7900, type: 3} + - {fileID: 2800000, guid: 7ce2118f74614a94aa8a0cdf2e6062c3, type: 3} + - {fileID: 2800000, guid: 2ca97df9d1801e84a8a8f2c53cb744f0, type: 3} + - {fileID: 2800000, guid: e63eef8f54aa9dc4da9a5ac094b503b5, type: 3} + - {fileID: 2800000, guid: 39451254daebd6d40b52899c1f1c0c1b, type: 3} + - {fileID: 2800000, guid: c94ad916058dff743b0f1c969ddbe660, type: 3} + - {fileID: 2800000, guid: ed5ea7ce59ca8ec4f9f14bf470a30f35, type: 3} + - {fileID: 2800000, guid: 071e954febf155243a6c81e48f452644, type: 3} + - {fileID: 2800000, guid: 96aaab9cc247d0b4c98132159688c1af, type: 3} + - {fileID: 2800000, guid: fc3fa8f108657e14486697c9a84ccfc5, type: 3} + - {fileID: 2800000, guid: bfed3e498947fcb4890b7f40f54d85b9, type: 3} + - {fileID: 2800000, guid: d512512f4af60a442ab3458489412954, type: 3} + - {fileID: 2800000, guid: 47a45908f6db0cb44a0d5e961143afec, type: 3} + - {fileID: 2800000, guid: 4dcc0502f8586f941b5c4a66717205e8, type: 3} + - {fileID: 2800000, guid: 9d92991794bb5864c8085468b97aa067, type: 3} + - {fileID: 2800000, guid: 14381521ff11cb74abe3fe65401c23be, type: 3} + - {fileID: 2800000, guid: d36f0fe53425e08499a2333cf423634c, type: 3} + - {fileID: 2800000, guid: d4044ea2490d63b43aa1765f8efbf8a9, type: 3} + - {fileID: 2800000, guid: c9bd74624d8070f429e3f46d161f9204, type: 3} + - {fileID: 2800000, guid: d5c9b274310e5524ebe32a4e4da3df1f, type: 3} + - {fileID: 2800000, guid: f69770e54f2823f43badf77916acad83, type: 3} + - {fileID: 2800000, guid: 10b6c6d22e73dea46a8ab36b6eebd629, type: 3} + - {fileID: 2800000, guid: a2ec5cbf5a9b64345ad3fab0912ddf7b, type: 3} + - {fileID: 2800000, guid: 1c3c6d69a645b804fa232004b96b7ad3, type: 3} + - {fileID: 2800000, guid: d18a24d7b4ed50f4387993566d9d3ae2, type: 3} + - {fileID: 2800000, guid: c989e1ed85cf7154caa922fec53e6af6, type: 3} + - {fileID: 2800000, guid: ff47e5a0f105eb34883b973e51f4db62, type: 3} + - {fileID: 2800000, guid: fa042edbfc40fbd4bad0ab9d505b1223, type: 3} + - {fileID: 2800000, guid: 896d9004736809c4fb5973b7c12eb8b9, type: 3} + - {fileID: 2800000, guid: 179f794063d2a66478e6e726f84a65bc, type: 3} + filmGrainTex: + - {fileID: 2800000, guid: 654c582f7f8a5a14dbd7d119cbde215d, type: 3} + - {fileID: 2800000, guid: dd77ffd079630404e879388999033049, type: 3} + - {fileID: 2800000, guid: 1097e90e1306e26439701489f391a6c0, type: 3} + - {fileID: 2800000, guid: f0b67500f7fad3b4c9f2b13e8f41ba6e, type: 3} + - {fileID: 2800000, guid: 9930fb4528622b34687b00bbe6883de7, type: 3} + - {fileID: 2800000, guid: bd9e8c758250ef449a4b4bfaad7a2133, type: 3} + - {fileID: 2800000, guid: 510a2f57334933e4a8dbabe4c30204e4, type: 3} + - {fileID: 2800000, guid: b4db8180660810945bf8d55ab44352ad, type: 3} + - {fileID: 2800000, guid: fd2fd78b392986e42a12df2177d3b89c, type: 3} + - {fileID: 2800000, guid: 5cdee82a77d13994f83b8fdabed7c301, type: 3} + smaaAreaTex: {fileID: 2800000, guid: d1f1048909d55cd4fa1126ab998f617e, type: 3} + smaaSearchTex: {fileID: 2800000, guid: 51eee22c2a633ef4aada830eed57c3fd, type: 3} + m_TexturesResourcesVersion: 0 + - rid: 6142483855146221570 + type: {class: ScreenSpaceAmbientOcclusionDynamicResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_BlueNoise256Textures: + - {fileID: 2800000, guid: 36f118343fc974119bee3d09e2111500, type: 3} + - {fileID: 2800000, guid: 4b7b083e6b6734e8bb2838b0b50a0bc8, type: 3} + - {fileID: 2800000, guid: c06cc21c692f94f5fb5206247191eeee, type: 3} + - {fileID: 2800000, guid: cb76dd40fa7654f9587f6a344f125c9a, type: 3} + - {fileID: 2800000, guid: e32226222ff144b24bf3a5a451de54bc, type: 3} + - {fileID: 2800000, guid: 3302065f671a8450b82c9ddf07426f3a, type: 3} + - {fileID: 2800000, guid: 56a77a3e8d64f47b6afe9e3c95cb57d5, type: 3} + m_Version: 0 + - rid: 6142483855146221571 + type: {class: PostProcessData/ShaderResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + stopNanPS: {fileID: 4800000, guid: 1121bb4e615ca3c48b214e79e841e823, type: 3} + subpixelMorphologicalAntialiasingPS: {fileID: 4800000, guid: 63eaba0ebfb82cc43bde059b4a8c65f6, type: 3} + gaussianDepthOfFieldPS: {fileID: 4800000, guid: 5e7134d6e63e0bc47a1dd2669cedb379, type: 3} + bokehDepthOfFieldPS: {fileID: 4800000, guid: 2aed67ad60045d54ba3a00c91e2d2631, type: 3} + cameraMotionBlurPS: {fileID: 4800000, guid: 1edcd131364091c46a17cbff0b1de97a, type: 3} + paniniProjectionPS: {fileID: 4800000, guid: a15b78cf8ca26ca4fb2090293153c62c, type: 3} + lutBuilderLdrPS: {fileID: 4800000, guid: 65df88701913c224d95fc554db28381a, type: 3} + lutBuilderHdrPS: {fileID: 4800000, guid: ec9fec698a3456d4fb18cf8bacb7a2bc, type: 3} + bloomPS: {fileID: 4800000, guid: 5f1864addb451f54bae8c86d230f736e, type: 3} + temporalAntialiasingPS: {fileID: 4800000, guid: 9c70c1a35ff15f340b38ea84842358bf, type: 3} + LensFlareDataDrivenPS: {fileID: 4800000, guid: 6cda457ac28612740adb23da5d39ea92, type: 3} + LensFlareScreenSpacePS: {fileID: 4800000, guid: 701880fecb344ea4c9cd0db3407ab287, type: 3} + scalingSetupPS: {fileID: 4800000, guid: e8ee25143a34b8c4388709ea947055d1, type: 3} + easuPS: {fileID: 4800000, guid: 562b7ae4f629f144aa97780546fce7c6, type: 3} + uberPostPS: {fileID: 4800000, guid: e7857e9d0c934dc4f83f270f8447b006, type: 3} + finalPostPassPS: {fileID: 4800000, guid: c49e63ed1bbcb334780a3bd19dfed403, type: 3} + m_ShaderResourcesVersion: 0 + - rid: 6142483855146221572 + type: {class: UniversalRenderPipelineEditorAssets, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_DefaultSettingsVolumeProfile: {fileID: 11400000, guid: eda47df5b85f4f249abf7abd73db2cb2, type: 2} + - rid: 6142483855146221573 + type: {class: UniversalRenderPipelineRuntimeXRResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_xrOcclusionMeshPS: {fileID: 4800000, guid: 4431b1f1f743fbf4eb310a967890cbea, type: 3} + m_xrMirrorViewPS: {fileID: 4800000, guid: d5a307c014552314b9f560906d708772, type: 3} + m_xrMotionVector: {fileID: 4800000, guid: f89aac1e4f84468418fe30e611dff395, type: 3} + - rid: 6852985685364965376 + type: {class: URPShaderStrippingSetting, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 0 + m_StripUnusedPostProcessingVariants: 1 + m_StripUnusedVariants: 1 + m_StripScreenCoordOverrideVariants: 1 + - rid: 6852985685364965377 + type: {class: UniversalRenderPipelineEditorShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_AutodeskInteractive: {fileID: 4800000, guid: 0e9d5a909a1f7e84882a534d0d11e49f, type: 3} + m_AutodeskInteractiveTransparent: {fileID: 4800000, guid: 5c81372d981403744adbdda4433c9c11, type: 3} + m_AutodeskInteractiveMasked: {fileID: 4800000, guid: 80aa867ac363ac043847b06ad71604cd, type: 3} + m_TerrainDetailLit: {fileID: 4800000, guid: f6783ab646d374f94b199774402a5144, type: 3} + m_TerrainDetailGrassBillboard: {fileID: 4800000, guid: 29868e73b638e48ca99a19ea58c48d90, type: 3} + m_TerrainDetailGrass: {fileID: 4800000, guid: e507fdfead5ca47e8b9a768b51c291a1, type: 3} + m_DefaultSpeedTree7Shader: {fileID: 4800000, guid: 0f4122b9a743b744abe2fb6a0a88868b, type: 3} + m_DefaultSpeedTree8Shader: {fileID: -6465566751694194690, guid: 9920c1f1781549a46ba081a2a15a16ec, type: 3} + m_DefaultSpeedTree9Shader: {fileID: -6465566751694194690, guid: cbd3e1cc4ae141c42a30e33b4d666a61, type: 3} + - rid: 6852985685364965378 + type: {class: UniversalRendererResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 0 + m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3} + m_CameraMotionVector: {fileID: 4800000, guid: c56b7e0d4c7cb484e959caeeedae9bbf, type: 3} + m_StencilDeferredPS: {fileID: 4800000, guid: e9155b26e1bc55942a41e518703fe304, type: 3} + m_DBufferClear: {fileID: 4800000, guid: f056d8bd2a1c7e44e9729144b4c70395, type: 3} + - rid: 6852985685364965379 + type: {class: UniversalRenderPipelineDebugShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_DebugReplacementPS: {fileID: 4800000, guid: cf852408f2e174538bcd9b7fda1c5ae7, type: 3} + m_HdrDebugViewPS: {fileID: 4800000, guid: 573620ae32aec764abd4d728906d2587, type: 3} + m_ProbeVolumeSamplingDebugComputeShader: {fileID: 7200000, guid: 53626a513ea68ce47b59dc1299fe3959, type: 3} + - rid: 6852985685364965380 + type: {class: UniversalRenderPipelineRuntimeShaders, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 0 + m_FallbackErrorShader: {fileID: 4800000, guid: e6e9a19c3678ded42a3bc431ebef7dbd, type: 3} + m_BlitHDROverlay: {fileID: 4800000, guid: a89bee29cffa951418fc1e2da94d1959, type: 3} + m_CoreBlitPS: {fileID: 4800000, guid: 93446b5c5339d4f00b85c159e1159b7c, type: 3} + m_CoreBlitColorAndDepthPS: {fileID: 4800000, guid: d104b2fc1ca6445babb8e90b0758136b, type: 3} + m_SamplingPS: {fileID: 4800000, guid: 04c410c9937594faa893a11dceb85f7e, type: 3} + m_TerrainDetailLit: {fileID: 4800000, guid: f6783ab646d374f94b199774402a5144, type: 3} + m_TerrainDetailGrassBillboard: {fileID: 4800000, guid: 29868e73b638e48ca99a19ea58c48d90, type: 3} + m_TerrainDetailGrass: {fileID: 4800000, guid: e507fdfead5ca47e8b9a768b51c291a1, type: 3} + - rid: 6852985685364965381 + type: {class: UniversalRenderPipelineRuntimeTextures, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 1 + m_BlueNoise64LTex: {fileID: 2800000, guid: e3d24661c1e055f45a7560c033dbb837, type: 3} + m_BayerMatrixTex: {fileID: 2800000, guid: f9ee4ed84c1d10c49aabb9b210b0fc44, type: 3} + m_DebugFontTex: {fileID: 2800000, guid: 26a413214480ef144b2915d6ff4d0beb, type: 3} + - rid: 6852985685364965382 + type: {class: Renderer2DResources, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 0 + m_LightShader: {fileID: 4800000, guid: 3f6c848ca3d7bca4bbe846546ac701a1, type: 3} + m_ProjectedShadowShader: {fileID: 4800000, guid: ce09d4a80b88c5a4eb9768fab4f1ee00, type: 3} + m_SpriteShadowShader: {fileID: 4800000, guid: 44fc62292b65ab04eabcf310e799ccf6, type: 3} + m_SpriteUnshadowShader: {fileID: 4800000, guid: de02b375720b5c445afe83cd483bedf3, type: 3} + m_GeometryShadowShader: {fileID: 4800000, guid: 19349a0f9a7ed4c48a27445bcf92e5e1, type: 3} + m_GeometryUnshadowShader: {fileID: 4800000, guid: 77774d9009bb81447b048c907d4c6273, type: 3} + m_FallOffLookup: {fileID: 2800000, guid: 5688ab254e4c0634f8d6c8e0792331ca, type: 3} + m_CopyDepthPS: {fileID: 4800000, guid: d6dae50ee9e1bfa4db75f19f99355220, type: 3} + m_DefaultLitMaterial: {fileID: 2100000, guid: a97c105638bdf8b4a8650670310a4cd3, type: 2} + m_DefaultUnlitMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2} + m_DefaultMaskMaterial: {fileID: 2100000, guid: 15d0c3709176029428a0da2f8cecf0b5, type: 2} + - rid: 6852985685364965383 + type: {class: UniversalRenderPipelineEditorMaterials, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_DefaultMaterial: {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2} + m_DefaultParticleMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2} + m_DefaultLineMaterial: {fileID: 2100000, guid: e823cd5b5d27c0f4b8256e7c12ee3e6d, type: 2} + m_DefaultTerrainMaterial: {fileID: 2100000, guid: 594ea882c5a793440b60ff72d896021e, type: 2} + m_DefaultDecalMaterial: {fileID: 2100000, guid: 31d0dcc6f2dd4e4408d18036a2c93862, type: 2} + m_DefaultSpriteMaterial: {fileID: 2100000, guid: 9dfc825aed78fcd4ba02077103263b40, type: 2} + - rid: 6852985685364965384 + type: {class: URPDefaultVolumeProfileSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 0 + m_VolumeProfile: {fileID: 11400000, guid: ab09877e2e707104187f6f83e2f62510, type: 2} + - rid: 6852985685364965385 + type: {class: RenderGraphSettings, ns: UnityEngine.Rendering.Universal, asm: Unity.RenderPipelines.Universal.Runtime} + data: + m_Version: 0 + m_EnableRenderCompatibilityMode: 0 + - rid: 6852985685364965386 + type: {class: GPUResidentDrawerResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.GPUDriven.Runtime} + data: + m_Version: 0 + m_InstanceDataBufferCopyKernels: {fileID: 7200000, guid: f984aeb540ded8b4fbb8a2047ab5b2e2, type: 3} + m_InstanceDataBufferUploadKernels: {fileID: 7200000, guid: 53864816eb00f2343b60e1a2c5a262ef, type: 3} + m_TransformUpdaterKernels: {fileID: 7200000, guid: 2a567b9b2733f8d47a700c3c85bed75b, type: 3} + m_WindDataUpdaterKernels: {fileID: 7200000, guid: fde76746e4fd0ed418c224f6b4084114, type: 3} + m_OccluderDepthPyramidKernels: {fileID: 7200000, guid: 08b2b5fb307b0d249860612774a987da, type: 3} + m_InstanceOcclusionCullingKernels: {fileID: 7200000, guid: f6d223acabc2f974795a5a7864b50e6c, type: 3} + m_OcclusionCullingDebugKernels: {fileID: 7200000, guid: b23e766bcf50ca4438ef186b174557df, type: 3} + m_DebugOcclusionTestPS: {fileID: 4800000, guid: d3f0849180c2d0944bc71060693df100, type: 3} + m_DebugOccluderPS: {fileID: 4800000, guid: b3c92426a88625841ab15ca6a7917248, type: 3} + - rid: 6852985685364965387 + type: {class: STP/RuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_setupCS: {fileID: 7200000, guid: 33be2e9a5506b2843bdb2bdff9cad5e1, type: 3} + m_preTaaCS: {fileID: 7200000, guid: a679dba8ec4d9ce45884a270b0e22dda, type: 3} + m_taaCS: {fileID: 7200000, guid: 3923900e2b41b5e47bc25bfdcbcdc9e6, type: 3} + - rid: 6852985685364965388 + type: {class: ProbeVolumeBakingResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_Version: 1 + dilationShader: {fileID: 7200000, guid: 6bb382f7de370af41b775f54182e491d, type: 3} + subdivideSceneCS: {fileID: 7200000, guid: bb86f1f0af829fd45b2ebddda1245c22, type: 3} + voxelizeSceneShader: {fileID: 4800000, guid: c8b6a681c7b4e2e4785ffab093907f9e, type: 3} + traceVirtualOffsetCS: {fileID: -6772857160820960102, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3} + traceVirtualOffsetRT: {fileID: -5126288278712620388, guid: ff2cbab5da58bf04d82c5f34037ed123, type: 3} + skyOcclusionCS: {fileID: -6772857160820960102, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3} + skyOcclusionRT: {fileID: -5126288278712620388, guid: 5a2a534753fbdb44e96c3c78b5a6999d, type: 3} + renderingLayerCS: {fileID: -6772857160820960102, guid: 94a070d33e408384bafc1dea4a565df9, type: 3} + renderingLayerRT: {fileID: -5126288278712620388, guid: 94a070d33e408384bafc1dea4a565df9, type: 3} + - rid: 6852985685364965389 + type: {class: ProbeVolumeGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_Version: 1 + m_ProbeVolumeDisableStreamingAssets: 0 + - rid: 6852985685364965390 + type: {class: ProbeVolumeDebugResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_Version: 1 + probeVolumeDebugShader: {fileID: 4800000, guid: 3b21275fd12d65f49babb5286f040f2d, type: 3} + probeVolumeFragmentationDebugShader: {fileID: 4800000, guid: 3a80877c579b9144ebdcc6d923bca303, type: 3} + probeVolumeSamplingDebugShader: {fileID: 4800000, guid: bf54e6528c79a224e96346799064c393, type: 3} + probeVolumeOffsetDebugShader: {fileID: 4800000, guid: db8bd7436dc2c5f4c92655307d198381, type: 3} + probeSamplingDebugMesh: {fileID: -3555484719484374845, guid: 20be25aac4e22ee49a7db76fb3df6de2, type: 3} + numbersDisplayTex: {fileID: 2800000, guid: 73fe53b428c5b3440b7e87ee830b608a, type: 3} + - rid: 6852985685364965391 + type: {class: IncludeAdditionalRPAssets, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_version: 0 + m_IncludeReferencedInScenes: 0 + m_IncludeAssetsByLabel: 0 + m_LabelToInclude: + - rid: 6852985685364965392 + type: {class: ShaderStrippingSetting, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_Version: 0 + m_ExportShaderVariants: 1 + m_ShaderVariantLogLevel: 0 + m_StripRuntimeDebugShaders: 1 + - rid: 6852985685364965393 + type: {class: ProbeVolumeRuntimeResources, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_Version: 1 + probeVolumeBlendStatesCS: {fileID: 7200000, guid: a3f7b8c99de28a94684cb1daebeccf5d, type: 3} + probeVolumeUploadDataCS: {fileID: 7200000, guid: 0951de5992461754fa73650732c4954c, type: 3} + probeVolumeUploadDataL2CS: {fileID: 7200000, guid: 6196f34ed825db14b81fb3eb0ea8d931, type: 3} + - rid: 6852985685364965394 + type: {class: RenderGraphGlobalSettings, ns: UnityEngine.Rendering, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_version: 0 + m_EnableCompilationCaching: 1 + m_EnableValidityChecks: 1 + - rid: 8712630790384254976 + type: {class: RenderGraphUtilsResources, ns: UnityEngine.Rendering.RenderGraphModule.Util, asm: Unity.RenderPipelines.Core.Runtime} + data: + m_Version: 0 + m_CoreCopyPS: {fileID: 4800000, guid: 12dc59547ea167a4ab435097dd0f9add, type: 3} diff --git a/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset.meta b/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset.meta new file mode 100644 index 0000000..81b84f2 --- /dev/null +++ b/Assets/Settings/UniversalRenderPipelineGlobalSettings.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 18dc0cd2c080841dea60987a38ce93fa +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TutorialInfo.meta b/Assets/TutorialInfo.meta new file mode 100644 index 0000000..a700bca --- /dev/null +++ b/Assets/TutorialInfo.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba062aa6c92b140379dbc06b43dd3b9b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TutorialInfo/Icons.meta b/Assets/TutorialInfo/Icons.meta new file mode 100644 index 0000000..1d19fb9 --- /dev/null +++ b/Assets/TutorialInfo/Icons.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8a0c9218a650547d98138cd835033977 +folderAsset: yes +timeCreated: 1484670163 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/TutorialInfo/Icons/URP.png b/Assets/TutorialInfo/Icons/URP.png new file mode 100644 index 0000000000000000000000000000000000000000..6194a807e27158f864a7c7677f4cbf62d8b94503 GIT binary patch literal 24069 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 HejoV + + + + \ No newline at end of file diff --git a/Assets/packages.config.meta b/Assets/packages.config.meta new file mode 100644 index 0000000..3e6604a --- /dev/null +++ b/Assets/packages.config.meta @@ -0,0 +1,28 @@ +fileFormatVersion: 2 +guid: 3fddc994a025a3e449fc97f1e04db56b +labels: +- NuGetForUnity +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/prefab.meta b/Assets/prefab.meta new file mode 100644 index 0000000..3f59c6b --- /dev/null +++ b/Assets/prefab.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 828f898ae102d234fac01a68c88b9200 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/prefab/player.prefab b/Assets/prefab/player.prefab new file mode 100644 index 0000000..4d66336 --- /dev/null +++ b/Assets/prefab/player.prefab @@ -0,0 +1,208 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &604015591144777768 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6988467046820125282} + - component: {fileID: 3314084087282559572} + - component: {fileID: 3031688745158166643} + - component: {fileID: 2766164182483648505} + - component: {fileID: 3509177926358252686} + - component: {fileID: 3007804520713623267} + - component: {fileID: -4122472446151524419} + m_Layer: 0 + m_Name: player + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &6988467046820125282 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + serializedVersion: 2 + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -2.383171, y: 2.46, z: 0.04616074} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!33 &3314084087282559572 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + m_Mesh: {fileID: 10208, guid: 0000000000000000e000000000000000, type: 0} +--- !u!23 &3031688745158166643 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, 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 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!136 &2766164182483648505 +CapsuleCollider: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_IsTrigger: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 2 + m_Radius: 0.5 + m_Height: 2 + m_Direction: 1 + m_Center: {x: 0, y: 0, z: 0} +--- !u!143 &3509177926358252686 +CharacterController: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 + m_ProvidesContacts: 0 + m_Enabled: 1 + serializedVersion: 3 + 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 &3007804520713623267 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3} + m_Name: + m_EditorClassIdentifier: + k__BackingField: 0 + WasActiveDuringEdit: 0 + WasActiveDuringEdit_Set1: 1 + k__BackingField: 0 + k__BackingField: {fileID: 0} + k__BackingField: {fileID: 0} + NetworkBehaviours: + - {fileID: -4122472446151524419} + InitializedParentNetworkBehaviour: {fileID: 0} + InitializedNestedNetworkObjects: [] + RuntimeParentNetworkBehaviour: {fileID: 0} + RuntimeChildNetworkBehaviours: [] + _isNetworked: 1 + _isSpawnable: 1 + _isGlobal: 0 + _initializeOrder: 0 + _preventDespawnOnDisconnect: 0 + _defaultDespawnType: 0 + NetworkObserver: {fileID: 0} + _enablePrediction: 0 + _predictionType: 0 + _graphicalObject: {fileID: 0} + _detachGraphicalObject: 0 + _enableStateForwarding: 1 + _networkTransform: {fileID: 0} + _ownerInterpolation: 1 + _ownerSmoothedProperties: 255 + _adaptiveInterpolation: 3 + _spectatorSmoothedProperties: 255 + _spectatorInterpolation: 2 + _enableTeleport: 0 + _teleportThreshold: 1 + k__BackingField: 11 + k__BackingField: 0 + k__BackingField: 16827585925996473256 + k__BackingField: 0 + SerializedTransformProperties: + Position: {x: -2.383171, y: 2.46, z: 0.04616074} + Rotation: {x: 0, y: 0, z: 0, w: 1} + Scale: {x: 1, y: 1, z: 1} + IsValid: 1 +--- !u!114 &-4122472446151524419 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 604015591144777768} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8a6a39c46bf52104ba8efe3100bce3f7, type: 3} + m_Name: + m_EditorClassIdentifier: + _componentIndexCache: 0 + _addedNetworkObject: {fileID: 3007804520713623267} + _networkObjectCache: {fileID: 3007804520713623267} diff --git a/Assets/prefab/player.prefab.meta b/Assets/prefab/player.prefab.meta new file mode 100644 index 0000000..0c73768 --- /dev/null +++ b/Assets/prefab/player.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: e3ebf1e5214cbb941b2da4f7add0060a +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/manifest.json b/Packages/manifest.json new file mode 100644 index 0000000..7384855 --- /dev/null +++ b/Packages/manifest.json @@ -0,0 +1,49 @@ +{ + "dependencies": { + "com.github-glitchenzo.nugetforunity": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", + "com.unity.ai.navigation": "2.0.7", + "com.unity.collab-proxy": "2.8.2", + "com.unity.ide.rider": "3.0.36", + "com.unity.ide.visualstudio": "2.0.23", + "com.unity.inputsystem": "1.14.0", + "com.unity.multiplayer.center": "1.0.0", + "com.unity.render-pipelines.universal": "17.0.4", + "com.unity.test-framework": "1.5.1", + "com.unity.timeline": "1.8.7", + "com.unity.toolchain.win-x86_64-linux-x86_64": "2.0.10", + "com.unity.ugui": "2.0.0", + "com.unity.visualscripting": "1.9.6", + "com.unity.modules.accessibility": "1.0.0", + "com.unity.modules.ai": "1.0.0", + "com.unity.modules.androidjni": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.cloth": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.physics2d": "1.0.0", + "com.unity.modules.screencapture": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.terrainphysics": "1.0.0", + "com.unity.modules.tilemap": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.uielements": "1.0.0", + "com.unity.modules.umbra": "1.0.0", + "com.unity.modules.unityanalytics": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0", + "com.unity.modules.unitywebrequestwww": "1.0.0", + "com.unity.modules.vehicles": "1.0.0", + "com.unity.modules.video": "1.0.0", + "com.unity.modules.vr": "1.0.0", + "com.unity.modules.wind": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } +} diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json new file mode 100644 index 0000000..4be34fa --- /dev/null +++ b/Packages/packages-lock.json @@ -0,0 +1,496 @@ +{ + "dependencies": { + "com.github-glitchenzo.nugetforunity": { + "version": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", + "depth": 0, + "source": "git", + "dependencies": {}, + "hash": "f789083c31250c83082da9b29be0c976152d699b" + }, + "com.unity.ai.navigation": { + "version": "2.0.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.ai": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.burst": { + "version": "1.8.21", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.collab-proxy": { + "version": "2.8.2", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.collections": { + "version": "2.5.1", + "depth": 2, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.8.17", + "com.unity.test-framework": "1.4.5", + "com.unity.nuget.mono-cecil": "1.11.4", + "com.unity.test-framework.performance": "3.0.3" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ext.nunit": { + "version": "2.0.5", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.ide.rider": { + "version": "3.0.36", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ext.nunit": "1.0.6" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ide.visualstudio": { + "version": "2.0.23", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.9" + }, + "url": "https://packages.unity.com" + }, + "com.unity.inputsystem": { + "version": "1.14.0", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.uielements": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.mathematics": { + "version": "1.3.2", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.multiplayer.center": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.uielements": "1.0.0" + } + }, + "com.unity.nuget.mono-cecil": { + "version": "1.11.4", + "depth": 3, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.render-pipelines.core": { + "version": "17.0.4", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.burst": "1.8.20", + "com.unity.mathematics": "1.3.2", + "com.unity.ugui": "2.0.0", + "com.unity.collections": "2.4.3", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.rendering.light-transport": "1.0.1" + } + }, + "com.unity.render-pipelines.universal": { + "version": "17.0.4", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.render-pipelines.core": "17.0.4", + "com.unity.shadergraph": "17.0.4", + "com.unity.render-pipelines.universal-config": "17.0.3" + } + }, + "com.unity.render-pipelines.universal-config": { + "version": "17.0.3", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.render-pipelines.core": "17.0.3" + } + }, + "com.unity.rendering.light-transport": { + "version": "1.0.1", + "depth": 2, + "source": "builtin", + "dependencies": { + "com.unity.collections": "2.2.0", + "com.unity.mathematics": "1.2.4", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.searcher": { + "version": "4.9.3", + "depth": 2, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.shadergraph": { + "version": "17.0.4", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.render-pipelines.core": "17.0.4", + "com.unity.searcher": "4.9.3" + } + }, + "com.unity.sysroot": { + "version": "2.0.10", + "depth": 1, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, + "com.unity.sysroot.linux-x86_64": { + "version": "2.0.9", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.sysroot": "2.0.10" + }, + "url": "https://packages.unity.com" + }, + "com.unity.test-framework": { + "version": "1.5.1", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.ext.nunit": "2.0.3", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.test-framework.performance": { + "version": "3.1.0", + "depth": 3, + "source": "registry", + "dependencies": { + "com.unity.test-framework": "1.1.33", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.timeline": { + "version": "1.8.7", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.director": "1.0.0", + "com.unity.modules.animation": "1.0.0", + "com.unity.modules.particlesystem": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.toolchain.win-x86_64-linux-x86_64": { + "version": "2.0.10", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.sysroot": "2.0.10", + "com.unity.sysroot.linux-x86_64": "2.0.9" + }, + "url": "https://packages.unity.com" + }, + "com.unity.ugui": { + "version": "2.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0" + } + }, + "com.unity.visualscripting": { + "version": "1.9.6", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.ugui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, + "com.unity.modules.accessibility": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.ai": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.androidjni": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.animation": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.assetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.audio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.cloth": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.director": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.animation": "1.0.0" + } + }, + "com.unity.modules.hierarchycore": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imageconversion": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.imgui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.jsonserialize": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.particlesystem": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.physics2d": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.screencapture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.subsystems": { + "version": "1.0.0", + "depth": 1, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.terrain": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.terrainphysics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.terrain": "1.0.0" + } + }, + "com.unity.modules.tilemap": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics2d": "1.0.0" + } + }, + "com.unity.modules.ui": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.uielements": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.hierarchycore": "1.0.0" + } + }, + "com.unity.modules.umbra": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unityanalytics": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0" + } + }, + "com.unity.modules.unitywebrequest": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.unitywebrequestassetbundle": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestaudio": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.audio": "1.0.0" + } + }, + "com.unity.modules.unitywebrequesttexture": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.unitywebrequestwww": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequestassetbundle": "1.0.0", + "com.unity.modules.unitywebrequestaudio": "1.0.0", + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.assetbundle": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0" + } + }, + "com.unity.modules.vehicles": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0" + } + }, + "com.unity.modules.video": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.audio": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0" + } + }, + "com.unity.modules.vr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.xr": "1.0.0" + } + }, + "com.unity.modules.wind": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": {} + }, + "com.unity.modules.xr": { + "version": "1.0.0", + "depth": 0, + "source": "builtin", + "dependencies": { + "com.unity.modules.physics": "1.0.0", + "com.unity.modules.jsonserialize": "1.0.0", + "com.unity.modules.subsystems": "1.0.0" + } + } + } +} diff --git a/ProjectSettings/AudioManager.asset b/ProjectSettings/AudioManager.asset new file mode 100644 index 0000000..27287fe --- /dev/null +++ b/ProjectSettings/AudioManager.asset @@ -0,0 +1,19 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 1024 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 + m_RequestedDSPBufferSize: 0 diff --git a/ProjectSettings/ClusterInputManager.asset b/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000..e7886b2 --- /dev/null +++ b/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/ProjectSettings/DynamicsManager.asset b/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000..50c0a96 --- /dev/null +++ b/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 18 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_DefaultMaxDepenetrationVelocity: 10 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0.1 + m_ClothInterCollisionStiffness: 0.2 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_SimulationMode: 0 + m_AutoSyncTransforms: 0 + m_ReuseCollisionCallbacks: 1 + m_InvokeCollisionCallbacks: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ClothGravity: {x: 0, y: -9.81, z: 0} + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_FrictionType: 0 + m_EnableEnhancedDeterminism: 0 + m_ImprovedPatchFriction: 0 + m_SolverType: 0 + m_DefaultMaxAngularSpeed: 50 + m_ScratchBufferChunkCount: 4 + m_CurrentBackendId: 4072204805 + m_FastMotionThreshold: 3.4028235e+38 diff --git a/ProjectSettings/EditorBuildSettings.asset b/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 0000000..d057ba3 --- /dev/null +++ b/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,13 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Scenes/SampleScene.unity + guid: 99c9720ab356a0642a771bea13969a05 + m_configObjects: + com.unity.input.settings.actions: {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3} + m_UseUCBPForAssetBundles: 0 diff --git a/ProjectSettings/EditorSettings.asset b/ProjectSettings/EditorSettings.asset new file mode 100644 index 0000000..fc70035 --- /dev/null +++ b/ProjectSettings/EditorSettings.asset @@ -0,0 +1,49 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 14 + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 0 + m_DefaultBehaviorMode: 0 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 0 + m_SpritePackerCacheSize: 10 + m_SpritePackerPaddingPower: 1 + m_Bc7TextureCompressor: 0 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref + m_ProjectGenerationRootNamespace: + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_EnableEditorAsyncCPUTextureLoading: 0 + m_AsyncShaderCompilation: 1 + m_PrefabModeAllowAutoSave: 1 + m_EnterPlayModeOptionsEnabled: 1 + m_EnterPlayModeOptions: 0 + m_GameObjectNamingDigits: 1 + m_GameObjectNamingScheme: 0 + m_AssetNamingUsesSpace: 1 + m_InspectorUseIMGUIDefaultInspector: 0 + m_UseLegacyProbeSampleCount: 0 + m_SerializeInlineMappingsOnOneLine: 1 + m_DisableCookiesInLightmapper: 0 + m_ShadowmaskStitching: 0 + m_AssetPipelineMode: 1 + m_RefreshImportMode: 0 + m_CacheServerMode: 0 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 + m_CacheServerEnableAuth: 0 + m_CacheServerEnableTls: 0 + m_CacheServerValidationMode: 2 + m_CacheServerDownloadBatchSize: 128 + m_EnableEnlightenBakedGI: 0 + m_ReferencedClipsExactNaming: 1 diff --git a/ProjectSettings/GraphicsSettings.asset b/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000..a148e49 --- /dev/null +++ b/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,67 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 16 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_VideoShadersIncludeMode: 2 + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10783, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_PreloadShadersBatchTimeLimit: -1 + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0} + m_CustomRenderPipeline: {fileID: 11400000, guid: 4b83569d67af61e458304325a23e5dfd, type: 2} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_BrgStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_RenderPipelineGlobalSettingsMap: + UnityEngine.Rendering.Universal.UniversalRenderPipeline: {fileID: 11400000, guid: 18dc0cd2c080841dea60987a38ce93fa, type: 2} + m_LightsUseLinearIntensity: 1 + m_LightsUseColorTemperature: 1 + m_LogWhenShaderIsCompiled: 0 + m_LightProbeOutsideHullStrategy: 0 + m_CameraRelativeLightCulling: 0 + m_CameraRelativeShadowCulling: 0 diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset new file mode 100644 index 0000000..b16147e --- /dev/null +++ b/ProjectSettings/InputManager.asset @@ -0,0 +1,487 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: joystick button 8 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Enable Debug Button 2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: backspace + altNegativeButton: + altPositiveButton: joystick button 9 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Reset + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Next + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page down + altNegativeButton: + altPositiveButton: joystick button 5 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Previous + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: page up + altNegativeButton: + altPositiveButton: joystick button 4 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Validate + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Persistent + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: right shift + altNegativeButton: + altPositiveButton: joystick button 2 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Multiplier + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: joystick button 3 + gravity: 0 + dead: 0 + sensitivity: 0 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 6 + joyNum: 0 + - serializedVersion: 3 + m_Name: Debug Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 2 + axis: 5 + joyNum: 0 diff --git a/ProjectSettings/MemorySettings.asset b/ProjectSettings/MemorySettings.asset new file mode 100644 index 0000000..5b5face --- /dev/null +++ b/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/ProjectSettings/MultiplayerManager.asset b/ProjectSettings/MultiplayerManager.asset new file mode 100644 index 0000000..2a93664 --- /dev/null +++ b/ProjectSettings/MultiplayerManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!655991488 &1 +MultiplayerManager: + m_ObjectHideFlags: 0 + m_EnableMultiplayerRoles: 0 + m_StrippingTypes: {} diff --git a/ProjectSettings/NavMeshAreas.asset b/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000..3b0b7c3 --- /dev/null +++ b/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/ProjectSettings/PackageManagerSettings.asset b/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 0000000..be4a797 --- /dev/null +++ b/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + 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: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreviewPackages: 0 + m_EnablePackageDependencies: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Capabilities: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/ProjectSettings/Physics2DSettings.asset b/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000..2b0ea7b --- /dev/null +++ b/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,56 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 6 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_BounceThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_ContactThreshold: 0 + m_JobOptions: + serializedVersion: 2 + useMultithreading: 0 + useConsistencySorting: 0 + m_InterpolationPosesPerJob: 100 + m_NewContactsPerJob: 30 + m_CollideContactsPerJob: 100 + m_ClearFlagsPerJob: 200 + m_ClearBodyForcesPerJob: 200 + m_SyncDiscreteFixturesPerJob: 50 + m_SyncContinuousFixturesPerJob: 50 + m_FindNearestContactsPerJob: 100 + m_UpdateTriggerContactsPerJob: 100 + m_IslandSolverCostThreshold: 100 + m_IslandSolverBodyCostScale: 1 + m_IslandSolverContactCostScale: 10 + m_IslandSolverJointCostScale: 10 + m_IslandSolverBodiesPerJob: 50 + m_IslandSolverContactsPerJob: 50 + m_SimulationMode: 0 + m_SimulationLayers: + serializedVersion: 2 + m_Bits: 4294967295 + m_MaxSubStepCount: 4 + m_MinSubStepFPS: 30 + m_UseSubStepping: 0 + m_UseSubStepContacts: 0 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_CallbacksOnDisable: 1 + m_ReuseCollisionCallbacks: 0 + m_AutoSyncTransforms: 0 + m_GizmoOptions: 10 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/ProjectSettings/PresetManager.asset b/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000..67a94da --- /dev/null +++ b/ProjectSettings/PresetManager.asset @@ -0,0 +1,7 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_DefaultPresets: {} diff --git a/ProjectSettings/ProjectSettings.asset b/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000..5b2625c --- /dev/null +++ b/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,932 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 28 + productGUID: 2a5e35a47d8fa6249b52b7a8605f5fd0 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: testmultiplayer + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 1 + unsupportedMSAAFallback: 0 + m_SpriteBatchMaxVertexCount: 65535 + m_SpriteBatchVertexThreshold: 300 + m_MTRendering: 1 + mipStripping: 0 + numberOfMipsStripped: 0 + numberOfMipsStrippedPerMipmapLimitGroup: {} + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 1 + androidUseSwappy: 0 + androidBlitType: 0 + androidResizeableActivity: 1 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + androidAutoRotationBehavior: 1 + androidPredictiveBackSupport: 1 + androidApplicationEntry: 2 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + dedicatedServerOptimizations: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 1 + meshDeformation: 2 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchGpuScratchPoolGranularity: 2097152 + switchAllowGpuScratchShrinking: 0 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + switchMaxWorkerMultiple: 8 + switchNVNGraphicsFirmwareMemory: 32 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnablePreTransform: 1 + vulkanEnableLateAcquireNextImage: 0 + vulkanEnableCommandBufferRecycling: 1 + loadStoreDebugModeEnabled: 0 + visionOSBundleVersion: 1.0 + tvOSBundleVersion: 1.0 + bundleVersion: 0.1.0 + preloadedAssets: + - {fileID: -944628639613478452, guid: 052faaac586de48259a63d0c4782560b, type: 3} + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 1 + xboxOneEnable7thCore: 1 + vrSettings: + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + enableOpenGLProfilerGPURecorders: 1 + allowHDRDisplaySupport: 0 + useHDRDisplay: 0 + hdrBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + resetResolutionOnWindowResize: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.4 + androidMinAspectRatio: 1 + applicationIdentifier: + Android: com.UnityTechnologies.com.unity.template.urpblank + Standalone: com.Unity-Technologies.com.unity.template.urp-blank + iPhone: com.Unity-Technologies.com.unity.template.urp-blank + buildNumber: + Standalone: 0 + VisionOS: 0 + iPhone: 0 + tvOS: 0 + overrideDefaultApplicationIdentifier: 1 + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 23 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 1 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + androidSplitApplicationBinary: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + strictShaderVariantMatching: 0 + VertexChannelCompressionMask: 4054 + iPhoneSdkVersion: 988 + iOSSimulatorArchitecture: 0 + iOSTargetOSVersionString: 13.0 + tvOSSdkVersion: 0 + tvOSSimulatorArchitecture: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 13.0 + VisionOSSdkVersion: 0 + VisionOSTargetOSVersionString: 1.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreenCustomStoryboardPath: + iOSLaunchScreeniPadCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + macOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + metalCompileShaderBinary: 0 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + VisionOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + VisionOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + shaderPrecisionModel: 0 + clonedFromGUID: 3c72c65a16f0acb438eed22b8b16c24a + templatePackageId: com.unity.template.urp-blank@17.0.11 + templateDefaultScene: Assets/Scenes/SampleScene.unity + useCustomMainManifest: 0 + useCustomLauncherManifest: 0 + useCustomMainGradleTemplate: 0 + useCustomLauncherGradleManifest: 0 + useCustomBaseGradleTemplate: 0 + useCustomGradlePropertiesTemplate: 0 + useCustomGradleSettingsTemplate: 0 + useCustomProguardFile: 0 + AndroidTargetArchitectures: 2 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidEnableArmv9SecurityFeatures: 0 + AndroidEnableArm64MTE: 0 + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 0 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + AndroidMinifyRelease: 0 + AndroidMinifyDebug: 0 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + AndroidReportGooglePlayAppDependencies: 1 + androidSymbolsSizeThreshold: 800 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: + - m_BuildTarget: iPhone + m_Icons: + - m_Textures: [] + m_Width: 180 + m_Height: 180 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 0 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 167 + m_Height: 167 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 152 + m_Height: 152 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 76 + m_Height: 76 + m_Kind: 0 + m_SubKind: iPad + - m_Textures: [] + m_Width: 120 + m_Height: 120 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 80 + m_Height: 80 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 3 + m_SubKind: iPad + - m_Textures: [] + m_Width: 87 + m_Height: 87 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 58 + m_Height: 58 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 29 + m_Height: 29 + m_Kind: 1 + m_SubKind: iPad + - m_Textures: [] + m_Width: 60 + m_Height: 60 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPhone + - m_Textures: [] + m_Width: 40 + m_Height: 40 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 20 + m_Height: 20 + m_Kind: 2 + m_SubKind: iPad + - m_Textures: [] + m_Width: 1024 + m_Height: 1024 + m_Kind: 4 + m_SubKind: App Store + - m_BuildTarget: Android + m_Icons: + - m_Textures: [] + m_Width: 432 + m_Height: 432 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 324 + m_Height: 324 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 216 + m_Height: 216 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 162 + m_Height: 162 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 108 + m_Height: 108 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 81 + m_Height: 81 + m_Kind: 2 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 192 + m_Height: 192 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 144 + m_Height: 144 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 96 + m_Height: 96 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 72 + m_Height: 72 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 48 + m_Height: 48 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 36 + m_Height: 36 + m_Kind: 0 + m_SubKind: + - m_BuildTarget: tvOS + m_Icons: + - m_Textures: [] + m_Width: 1280 + m_Height: 768 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 800 + m_Height: 480 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 400 + m_Height: 240 + m_Kind: 0 + m_SubKind: + - m_Textures: [] + m_Width: 4640 + m_Height: 1440 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 2320 + m_Height: 720 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 3840 + m_Height: 1440 + m_Kind: 1 + m_SubKind: + - m_Textures: [] + m_Width: 1920 + m_Height: 720 + m_Kind: 1 + m_SubKind: + m_BuildTargetBatching: [] + m_BuildTargetShaderSettings: [] + m_BuildTargetGraphicsJobs: [] + m_BuildTargetGraphicsJobMode: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: iOSSupport + m_APIs: 10000000 + m_Automatic: 1 + - m_BuildTarget: AndroidPlayer + m_APIs: 150000000b000000 + m_Automatic: 0 + m_BuildTargetVRSettings: [] + m_DefaultShaderChunkSizeInMB: 16 + m_DefaultShaderChunkCount: 0 + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: + - serializedVersion: 2 + m_BuildTarget: Android + m_EncodingQuality: 1 + m_BuildTargetGroupLightmapSettings: [] + m_BuildTargetGroupLoadStoreDebugModeSettings: [] + m_BuildTargetNormalMapEncoding: + - m_BuildTarget: Android + m_Encoding: 1 + m_BuildTargetDefaultTextureCompressionFormat: + - serializedVersion: 3 + m_BuildTarget: Android + m_Formats: 03000000 + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + editorGfxJobOverride: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + bluetoothUsageDescription: + macOSTargetOSVersion: 11.0 + switchNMETAOverride: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchEnableFileSystemTrace: 0 + switchLTOSetting: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchCompilerFlags: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 22 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchEnableTouchScreen: 1 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchDisableHTCSPlayerConnection: 0 + switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 0 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + switchUpgradedPlayerSettingsToNMETA: 0 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 2 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 2 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 32 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLShowDiagnostics: 0 + webGLDataCaching: 1 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 0 + webGLWasmArithmeticExceptions: 0 + webGLLinkerTarget: 1 + webGLThreadsSupport: 0 + webGLDecompressionFallback: 0 + webGLInitialMemorySize: 32 + webGLMaximumMemorySize: 2048 + webGLMemoryGrowthMode: 2 + webGLMemoryLinearGrowthStep: 16 + webGLMemoryGeometricGrowthStep: 0.2 + webGLMemoryGeometricGrowthCap: 96 + webGLEnableWebGPU: 0 + webGLPowerPreference: 2 + webGLWebAssemblyTable: 0 + webGLWebAssemblyBigInt: 0 + webGLCloseOnQuit: 0 + webWasm2023: 0 + scriptingDefineSymbols: + Standalone: FISHNET;FISHNET_V4 + additionalCompilerArguments: {} + platformArchitecture: {} + scriptingBackend: + Android: 1 + il2cppCompilerConfiguration: {} + il2cppCodeGeneration: {} + il2cppStacktraceInformation: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + useDeterministicCompilation: 1 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + editorAssembliesCompatibilityLevel: 1 + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: testmultiplayer + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: testmultiplayer + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} + metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + vcxProjDefaultLanguage: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 1 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: {} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + hmiPlayerDataPath: + hmiForceSRGBBlit: 1 + embeddedLinuxEnableGamepadInput: 0 + hmiCpuConfiguration: + hmiLogStartupTiming: 0 + qnxGraphicConfPath: + apiCompatibilityLevel: 6 + captureStartupLogs: {} + activeInputHandler: 2 + windowsGamepadBackendHint: 0 + cloudProjectId: 77ef2a1d-120d-4316-a465-82d1d7f77cec + framebufferDepthMemorylessMode: 0 + qualitySettingsNames: [] + projectName: testmultiplayer + organizationId: unitydadechin + cloudEnabled: 0 + legacyClampBlendShapeWeights: 0 + hmiLoadingImage: {fileID: 0} + platformRequiresReadableAssets: 0 + virtualTexturingSupportEnabled: 0 + insecureHttpOption: 0 + androidVulkanDenyFilterList: [] + androidVulkanAllowFilterList: [] diff --git a/ProjectSettings/ProjectVersion.txt b/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..593b461 --- /dev/null +++ b/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 6000.0.48f1 +m_EditorVersionWithRevision: 6000.0.48f1 (170d2541580d) diff --git a/ProjectSettings/QualitySettings.asset b/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000..f55198a --- /dev/null +++ b/ProjectSettings/QualitySettings.asset @@ -0,0 +1,134 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 1 + m_QualitySettings: + - serializedVersion: 4 + name: Mobile + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + skinWeights: 2 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 1 + useLegacyDetailDistribution: 1 + adaptiveVsync: 0 + vSyncCount: 0 + realtimeGICPUUsage: 100 + adaptiveVsyncExtraA: 0 + adaptiveVsyncExtraB: 0 + lodBias: 1 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 11400000, guid: 5e6cbd92db86f4b18aec3ed561671858, + type: 2} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: + - Standalone + - serializedVersion: 4 + name: PC + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + skinWeights: 4 + globalTextureMipmapLimit: 0 + textureMipmapLimitSettings: [] + anisotropicTextures: 2 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 1 + useLegacyDetailDistribution: 1 + adaptiveVsync: 0 + vSyncCount: 0 + realtimeGICPUUsage: 100 + adaptiveVsyncExtraA: 0 + adaptiveVsyncExtraB: 0 + lodBias: 2 + maximumLODLevel: 0 + enableLODCrossFade: 1 + streamingMipmapsActive: 0 + streamingMipmapsAddAllCameras: 1 + streamingMipmapsMemoryBudget: 512 + streamingMipmapsRenderersPerFrame: 512 + streamingMipmapsMaxLevelReduction: 2 + streamingMipmapsMaxFileIORequests: 1024 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 16 + asyncUploadPersistentBuffer: 1 + resolutionScalingFixedDPIFactor: 1 + customRenderPipeline: {fileID: 11400000, guid: 4b83569d67af61e458304325a23e5dfd, + type: 2} + terrainQualityOverrides: 0 + terrainPixelError: 1 + terrainDetailDensityScale: 1 + terrainBasemapDistance: 1000 + terrainDetailDistance: 80 + terrainTreeDistance: 5000 + terrainBillboardStart: 50 + terrainFadeLength: 5 + terrainMaxTrees: 50 + excludedTargetPlatforms: + - Android + - iPhone + m_TextureMipmapLimitGroupNames: [] + m_PerPlatformDefaultQuality: + Android: 0 + GameCoreScarlett: 1 + GameCoreXboxOne: 1 + Lumin: 0 + Nintendo Switch: 1 + PS4: 1 + PS5: 1 + Server: 0 + Stadia: 0 + Standalone: 1 + WebGL: 0 + Windows Store Apps: 0 + XboxOne: 0 + iPhone: 0 + tvOS: 0 diff --git a/ProjectSettings/SceneTemplateSettings.json b/ProjectSettings/SceneTemplateSettings.json new file mode 100644 index 0000000..ede5887 --- /dev/null +++ b/ProjectSettings/SceneTemplateSettings.json @@ -0,0 +1,121 @@ +{ + "templatePinStates": [], + "dependencyTypeInfos": [ + { + "userAdded": false, + "type": "UnityEngine.AnimationClip", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.Animations.AnimatorController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.AnimatorOverrideController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.Audio.AudioMixerController", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.ComputeShader", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Cubemap", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.GameObject", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.LightingDataAsset", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.LightingSettings", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Material", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.MonoScript", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.PhysicsMaterial2D", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessProfile", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.PostProcessing.PostProcessResources", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Rendering.VolumeProfile", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEditor.SceneAsset", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Shader", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.ShaderVariantCollection", + "defaultInstantiationMode": 1 + }, + { + "userAdded": false, + "type": "UnityEngine.Texture", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Texture2D", + "defaultInstantiationMode": 0 + }, + { + "userAdded": false, + "type": "UnityEngine.Timeline.TimelineAsset", + "defaultInstantiationMode": 0 + } + ], + "defaultDependencyTypeInfo": { + "userAdded": false, + "type": "", + "defaultInstantiationMode": 1 + }, + "newSceneOverride": 0 +} \ No newline at end of file diff --git a/ProjectSettings/ShaderGraphSettings.asset b/ProjectSettings/ShaderGraphSettings.asset new file mode 100644 index 0000000..e66042a --- /dev/null +++ b/ProjectSettings/ShaderGraphSettings.asset @@ -0,0 +1,18 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + 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: de02f9e1d18f588468e474319d09a723, type: 3} + m_Name: + m_EditorClassIdentifier: + shaderVariantLimit: 128 + customInterpolatorErrorThreshold: 32 + customInterpolatorWarningThreshold: 16 + customHeatmapValues: {fileID: 0} diff --git a/ProjectSettings/TagManager.asset b/ProjectSettings/TagManager.asset new file mode 100644 index 0000000..6413d11 --- /dev/null +++ b/ProjectSettings/TagManager.asset @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 + m_RenderingLayers: + - Default + - Light Layer 1 + - Light Layer 2 + - Light Layer 3 + - Light Layer 4 + - Light Layer 5 + - Light Layer 6 + - Light Layer 7 + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - diff --git a/ProjectSettings/TimeManager.asset b/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000..558a017 --- /dev/null +++ b/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/ProjectSettings/URPProjectSettings.asset b/ProjectSettings/URPProjectSettings.asset new file mode 100644 index 0000000..08faf03 --- /dev/null +++ b/ProjectSettings/URPProjectSettings.asset @@ -0,0 +1,15 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + 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: 247994e1f5a72c2419c26a37e9334c01, type: 3} + m_Name: + m_EditorClassIdentifier: + m_LastMaterialVersion: 9 diff --git a/ProjectSettings/UnityConnectSettings.asset b/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 0000000..a88bee0 --- /dev/null +++ b/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 1 + m_Enabled: 0 + m_TestMode: 0 + m_EventOldUrl: https://api.uca.cloud.unity3d.com/v1/events + m_EventUrl: https://cdp.cloud.unity3d.com/v1/events + m_ConfigUrl: https://config.uca.cloud.unity3d.com + m_DashboardUrl: https://dashboard.unity3d.com + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com + m_Enabled: 0 + m_LogBufferSize: 10 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_TestMode: 0 + m_InitializeOnStartup: 1 + m_PackageRequiringCoreStatsPresent: 0 + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/ProjectSettings/VFXManager.asset b/ProjectSettings/VFXManager.asset new file mode 100644 index 0000000..3a95c98 --- /dev/null +++ b/ProjectSettings/VFXManager.asset @@ -0,0 +1,12 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_StripUpdateShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/ProjectSettings/VersionControlSettings.asset b/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 0000000..dca2881 --- /dev/null +++ b/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,8 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/ProjectSettings/XRSettings.asset b/ProjectSettings/XRSettings.asset new file mode 100644 index 0000000..482590c --- /dev/null +++ b/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/UserSettings/EditorUserSettings.asset b/UserSettings/EditorUserSettings.asset new file mode 100644 index 0000000..f5abec4 --- /dev/null +++ b/UserSettings/EditorUserSettings.asset @@ -0,0 +1,63 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!162 &1 +EditorUserSettings: + m_ObjectHideFlags: 0 + serializedVersion: 4 + m_ConfigSettings: + GraphicsSettingsInspector_UserSettings: + value: 18134705175a055722080a3115371d4a0d55006876786860616b0471b8b07a68ffab74f9ee2a3a30300cea1a11320d0beb1a0c25f7060f494b4cc80018eb09361fc211cb1f862d19c51d19dcc413d6ade0d8ddfcddf9f4d9d29195fcfde6ebeae6f0a9c9afa6f8c5b89ff7a1aacececac4eba4d7c9d28bda + flags: 0 + RecentlyUsedSceneGuid-0: + value: 5353575657575f09555d5a7114260a444f4e1c7f297f25607d2b1965b1e2653a + flags: 0 + RecentlyUsedSceneGuid-1: + value: 5a0705045c035f085b0b582147750b44434f4c2e2f70223629791963e3e1306e + flags: 0 + RecentlyUsedSceneGuid-2: + value: 02505654500d585809080e2748250b44404e402f2a7177347e7f1b61e6b6376e + flags: 0 + RecentlyUsedSceneGuid-3: + value: 5407005506510a08080b0a2041765944104f4e7a742b25667e7a1e66b7e6633d + flags: 0 + RecentlyUsedSceneGuid-4: + value: 0052005e5c570c030f0c5a2315770b44444f4b2c7f717233782c4835e3b8316a + flags: 0 + RecentlyUsedSceneGuid-5: + value: 0005075e56000f0a5f5a0821467b084441164a7a757925642f711e67b0e66c68 + flags: 0 + RecentlyUsedSceneGuid-6: + value: 0606505504565b5f585b5a7b41710f4444161b2b75292761747d4a32e7e1646f + flags: 0 + RecentlyUsedSceneGuid-7: + value: 000350070100500a54080e2744260644104e482e7e7c25617c2b4f66e1b5653a + flags: 0 + RecentlyUsedSceneGuid-8: + value: 5a080d525253590e5f5f0f2049755e44464f4f2e787c7f662f714a65b3e1666b + flags: 0 + RecentlyUsedSceneGuid-9: + value: 5a08575f5207595a0f5d59741173094444164f7d7d2a23317c7a4465bbe1646d + flags: 0 + lightmappingBakingProfile: + value: 51 + flags: 0 + vcSharedLogLevel: + value: 0d5e400f0650 + flags: 0 + m_VCAutomaticAdd: 1 + m_VCDebugCom: 0 + m_VCDebugCmd: 0 + m_VCDebugOut: 0 + m_SemanticMergeMode: 2 + m_DesiredImportWorkerCount: 3 + m_StandbyImportWorkerCount: 2 + m_IdleImportWorkerShutdownDelay: 60000 + m_VCShowFailedCheckout: 1 + m_VCOverwriteFailedCheckoutAssets: 1 + m_VCProjectOverlayIcons: 1 + m_VCHierarchyOverlayIcons: 1 + m_VCOtherOverlayIcons: 1 + m_VCAllowAsyncUpdate: 1 + m_VCScanLocalPackagesOnConnect: 1 + m_ArtifactGarbageCollection: 1 + m_CompressAssetsOnImport: 1 diff --git a/UserSettings/Layouts/default-6000.dwlt b/UserSettings/Layouts/default-6000.dwlt new file mode 100644 index 0000000..9bc648b --- /dev/null +++ b/UserSettings/Layouts/default-6000.dwlt @@ -0,0 +1,1413 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12004, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_PixelRect: + serializedVersion: 2 + x: 7.2000003 + y: 50.4 + width: 1784.8 + height: 1046.4 + m_ShowMode: 4 + m_Title: Inspector + m_RootView: {fileID: 2} + m_MinSize: {x: 875, y: 472} + m_MaxSize: {x: 10000, y: 10000} + m_Maximized: 0 +--- !u!114 &2 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12008, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: + - {fileID: 3} + - {fileID: 5} + - {fileID: 4} + m_Position: + serializedVersion: 2 + x: 0 + y: 0 + width: 1784.8 + height: 1046.4 + m_MinSize: {x: 875, y: 300} + m_MaxSize: {x: 10000, y: 10000} + m_UseTopView: 1 + m_TopViewHeight: 36 + m_UseBottomView: 1 + m_BottomViewHeight: 20 +--- !u!114 &3 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12011, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 0 + y: 0 + width: 1784.8 + height: 36 + m_MinSize: {x: 0, y: 0} + m_MaxSize: {x: 0, y: 0} + m_LastLoadedLayoutName: +--- !u!114 &4 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12042, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 0 + y: 1026.4 + width: 1784.8 + height: 20 + m_MinSize: {x: 0, y: 0} + m_MaxSize: {x: 0, y: 0} +--- !u!114 &5 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: + - {fileID: 6} + - {fileID: 13} + m_Position: + serializedVersion: 2 + x: 0 + y: 36 + width: 1784.8 + height: 990.4 + m_MinSize: {x: 300, y: 100} + m_MaxSize: {x: 24288, y: 16192} + vertical: 0 + controlID: 105 + draggingID: 0 +--- !u!114 &6 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12010, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: + - {fileID: 7} + - {fileID: 10} + m_Position: + serializedVersion: 2 + x: 0 + y: 0 + width: 1364.8 + height: 990.4 + m_MinSize: {x: 200, y: 100} + m_MaxSize: {x: 16192, y: 16192} + vertical: 1 + controlID: 463 + draggingID: 0 +--- !u!114 &7 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12010, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: + - {fileID: 8} + - {fileID: 9} + m_Position: + serializedVersion: 2 + x: 0 + y: 0 + width: 1364.8 + height: 634.4 + m_MinSize: {x: 200, y: 50} + m_MaxSize: {x: 16192, y: 8096} + vertical: 0 + controlID: 31 + draggingID: 0 +--- !u!114 &8 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12006, guid: 0000000000000000e000000000000000, type: 0} + m_Name: SceneView + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 0 + y: 0 + width: 669.6 + height: 634.4 + m_MinSize: {x: 201, y: 226} + m_MaxSize: {x: 4001, y: 4026} + m_ActualView: {fileID: 14} + m_Panes: + - {fileID: 14} + m_Selected: 0 + m_LastSelected: 0 +--- !u!114 &9 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12006, guid: 0000000000000000e000000000000000, type: 0} + m_Name: GameView + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 669.6 + y: 0 + width: 695.2001 + height: 634.4 + m_MinSize: {x: 102, y: 126} + m_MaxSize: {x: 4002, y: 4026} + m_ActualView: {fileID: 15} + m_Panes: + - {fileID: 15} + m_Selected: 0 + m_LastSelected: 0 +--- !u!114 &10 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12010, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_Children: + - {fileID: 11} + - {fileID: 12} + m_Position: + serializedVersion: 2 + x: 0 + y: 634.4 + width: 1364.8 + height: 356 + m_MinSize: {x: 200, y: 50} + m_MaxSize: {x: 16192, y: 8096} + vertical: 0 + controlID: 464 + draggingID: 0 +--- !u!114 &11 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12006, guid: 0000000000000000e000000000000000, type: 0} + m_Name: ProjectBrowser + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 0 + y: 0 + width: 987.2 + height: 356 + m_MinSize: {x: 231, y: 276} + m_MaxSize: {x: 10001, y: 10026} + m_ActualView: {fileID: 16} + m_Panes: + - {fileID: 16} + - {fileID: 17} + m_Selected: 0 + m_LastSelected: 1 +--- !u!114 &12 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12006, guid: 0000000000000000e000000000000000, type: 0} + m_Name: SceneHierarchyWindow + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 987.2 + y: 0 + width: 377.60004 + height: 356 + m_MinSize: {x: 202, y: 226} + m_MaxSize: {x: 4002, y: 4026} + m_ActualView: {fileID: 18} + m_Panes: + - {fileID: 18} + m_Selected: 0 + m_LastSelected: 0 +--- !u!114 &13 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12006, guid: 0000000000000000e000000000000000, type: 0} + m_Name: InspectorWindow + m_EditorClassIdentifier: + m_Children: [] + m_Position: + serializedVersion: 2 + x: 1364.8 + y: 0 + width: 420 + height: 990.4 + m_MinSize: {x: 276, y: 76} + m_MaxSize: {x: 4001, y: 4026} + m_ActualView: {fileID: 19} + m_Panes: + - {fileID: 19} + - {fileID: 20} + m_Selected: 0 + m_LastSelected: 1 +--- !u!114 &14 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12013, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 200, y: 200} + m_MaxSize: {x: 4000, y: 4000} + m_TitleContent: + m_Text: Scene + m_Image: {fileID: 8634526014445323508, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Scene\u200B" + m_Pos: + serializedVersion: 2 + x: 7.2000003 + y: 86.4 + width: 668.6 + height: 608.4 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: + - dockPosition: 0 + containerId: overlay-toolbar__top + displayed: 1 + id: Tool Settings + index: 0 + contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-171.0,"y":-26.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":3,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: -171, y: -26} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 3 + layout: 1 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-toolbar__top + displayed: 0 + id: unity-grid-and-snap-toolbar + index: 1 + contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-141.0,"y":-110.4000244140625},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":3,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: -141, y: -110.400024} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 3 + layout: 1 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-toolbar__top + displayed: 1 + id: unity-scene-view-toolbar + index: 0 + contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":25.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24, y: 25} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 1 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-toolbar__top + displayed: 0 + id: unity-search-toolbar + index: 1 + contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":1,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: -24, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 1 + layout: 1 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-toolbar__left + displayed: 1 + id: unity-transform-toolbar + index: 0 + contents: '{"m_Layout":2,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.79998779296875,"y":24.799999237060548},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24.799988, y: 24.8} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 2 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-container--left + displayed: 1 + id: unity-component-tools + index: 1 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 197} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 2 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-toolbar__top + displayed: 0 + id: Orientation + index: 2 + contents: '{"m_Layout":4,"m_Collapsed":true,"m_Floating":false,"m_FloatingSnapOffset":{"x":-111.0,"y":26.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":1,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: -111, y: 26} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 1 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Light Settings + index: 0 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Camera + index: 1 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Cloth Constraints + index: 1 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Cloth Collisions + index: 2 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Navmesh Display + index: 4 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Agent Display + index: 5 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Obstacle Display + index: 6 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Occlusion Culling + index: 3 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Physics Debugger + index: 4 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Scene Visibility + index: 5 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Particles + index: 6 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":25.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24, y: 25} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Tilemap + index: 11 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/Tilemap Palette Helper + index: 12 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: APV Overlay + index: 7 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":48.0,"y":48.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 48, y: 48} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: Floating + displayed: 0 + id: Scene View/TrailRenderer + index: 0 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":true,"m_FloatingSnapOffset":{"x":-266.0,"y":-177.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":3,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 1 + collapsed: 0 + snapOffset: {x: -266, y: -177} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 3 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 1 + id: UnityEditor.SceneViewCameraOverlay + index: 9 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 48, y: 25} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: XR Building Blocks + index: 8 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":48.0,"y":48.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 48, y: 48} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 1 + id: UnityEditor.SceneViewCameraOverlay (0) + index: 10 + contents: + floating: 0 + collapsed: 0 + snapOffset: {x: 48, y: 48} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-toolbar__top + displayed: 0 + id: Brush Attributes + index: 2 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: Floating + displayed: 0 + id: unity-scene-view-camera-mode-toolbar + index: 2 + contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":true,"m_FloatingSnapOffset":{"x":280.8000183105469,"y":-143.1999969482422},"m_SnapOffsetDelta":{"x":0.0,"y":-0.0000152587890625},"m_FloatingSnapCorner":2,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 1 + collapsed: 0 + snapOffset: {x: 280.80002, y: -143.2} + snapOffsetDelta: {x: 0, y: -0.000015258789} + snapCorner: 2 + layout: 1 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-toolbar__left + displayed: 0 + id: Terrain Tools + index: 1 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-toolbar__left + displayed: 0 + id: Brush Masks + index: 2 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--left + displayed: 0 + id: Scene View/Lighting Visualization Colors + index: 0 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--left + displayed: 1 + id: Overlays/OverlayMenu + index: 1 + contents: '{"m_Layout":1,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":43.20000076293945,"y":-31.20001220703125},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":2,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 43.2, y: -31.200012} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 2 + layout: 1 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: Floating + displayed: 0 + id: SceneView/CamerasOverlay + index: 1 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":true,"m_FloatingSnapOffset":{"x":-255.20001220703126,"y":39.199981689453128},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":1,"m_Size":{"x":250.39999389648438,"y":350.3999938964844},"m_SizeOverridden":true}' + floating: 1 + collapsed: 0 + snapOffset: {x: -255.20001, y: 39.19998} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 1 + layout: 4 + size: {x: 250.4, y: 350.4} + sizeOverridden: 1 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: Scene View/PBR Validation Settings + index: 8 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":0.0,"y":0.0},"m_SnapOffsetDelta":{"x":24.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 0, y: 0} + snapOffsetDelta: {x: 24, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 1 + containerId: overlay-container--right + displayed: 0 + id: AINavigationOverlay + index: 9 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":24.0,"y":0.0},"m_SnapOffsetDelta":{"x":0.0,"y":0.0},"m_FloatingSnapCorner":0,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: 24, y: 0} + snapOffsetDelta: {x: 0, y: 0} + snapCorner: 0 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + - dockPosition: 0 + containerId: overlay-toolbar__right + displayed: 0 + id: Scene View/Animation Rigging + index: 0 + contents: '{"m_Layout":4,"m_Collapsed":false,"m_Floating":false,"m_FloatingSnapOffset":{"x":-224.80003356933595,"y":-204.0},"m_SnapOffsetDelta":{"x":-0.0000152587890625,"y":0.0},"m_FloatingSnapCorner":3,"m_Size":{"x":0.0,"y":0.0},"m_SizeOverridden":false}' + floating: 0 + collapsed: 0 + snapOffset: {x: -224.80003, y: -204} + snapOffsetDelta: {x: -0.000015258789, y: 0} + snapCorner: 3 + layout: 4 + size: {x: 0, y: 0} + sizeOverridden: 0 + m_ContainerData: + - containerId: overlay-toolbar__top + scrollOffset: 0 + - containerId: overlay-toolbar__left + scrollOffset: 0 + - containerId: overlay-container--left + scrollOffset: 0 + - containerId: overlay-container--right + scrollOffset: 0 + - containerId: overlay-toolbar__right + scrollOffset: 0 + - containerId: overlay-toolbar__bottom + scrollOffset: 0 + - containerId: Floating + scrollOffset: 0 + m_OverlaysVisible: 1 + m_WindowGUID: cc27987af1a868c49b0894db9c0f5429 + m_Gizmos: 1 + m_OverrideSceneCullingMask: 6917529027641081856 + m_SceneIsLit: 1 + m_SceneLighting: 1 + m_2DMode: 0 + m_isRotationLocked: 0 + m_PlayAudio: 0 + m_AudioPlay: 0 + m_DebugDrawModesUseInteractiveLightBakingData: 0 + m_Position: + m_Target: {x: -1.2030611, y: -1.8370533, z: -7.180372} + speed: 2 + m_Value: {x: -1.2030611, y: -1.8370533, z: -7.180372} + m_RenderMode: 0 + m_CameraMode: + drawMode: 0 + name: Shaded + section: Shading Mode + m_ValidateTrueMetals: 0 + m_DoValidateTrueMetals: 0 + m_SceneViewState: + m_AlwaysRefresh: 0 + showFog: 1 + showSkybox: 1 + showFlares: 1 + showImageEffects: 1 + showParticleSystems: 1 + showVisualEffectGraphs: 1 + m_FxEnabled: 1 + m_Grid: + xGrid: + m_Fade: + m_Target: 0 + speed: 2 + m_Value: 0 + m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} + m_Pivot: {x: 0, y: 0, z: 0} + m_Size: {x: 0, y: 0} + yGrid: + m_Fade: + m_Target: 0 + speed: 2 + m_Value: 0 + m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} + m_Pivot: {x: 0, y: 0, z: 0} + m_Size: {x: 1, y: 1} + zGrid: + m_Fade: + m_Target: 0 + speed: 2 + m_Value: 0 + m_Color: {r: 0.5, g: 0.5, b: 0.5, a: 0.4} + m_Pivot: {x: 0, y: 0, z: 0} + m_Size: {x: 0, y: 0} + m_ShowGrid: 0 + m_GridAxis: 1 + m_gridOpacity: 0.5 + m_Rotation: + m_Target: {x: 0.09139655, y: -0.9059066, z: 0.32679638, w: 0.25335845} + speed: 2 + m_Value: {x: 0.0903102, y: -0.90690094, z: 0.3259257, w: 0.25129128} + m_Size: + m_Target: 10 + speed: 2 + m_Value: 9.999999 + m_Ortho: + m_Target: 0 + speed: 2 + m_Value: 0 + m_CameraSettings: + m_Speed: 0.52074033 + m_SpeedNormalized: 0.26000017 + m_SpeedMin: 0.001 + m_SpeedMax: 2 + m_EasingEnabled: 1 + m_EasingDuration: 0.4 + m_AccelerationEnabled: 1 + m_FieldOfViewHorizontalOrVertical: 60 + m_NearClip: 0.03 + m_FarClip: 10000 + m_DynamicClip: 1 + m_OcclusionCulling: 0 + m_LastSceneViewRotation: {x: -0.08717229, y: 0.89959055, z: -0.21045254, w: -0.3726226} + m_LastSceneViewOrtho: 0 + m_Viewpoint: + m_SceneView: {fileID: 14} + m_CameraOverscanSettings: + m_Opacity: 50 + m_Scale: 1 + m_ReplacementShader: {fileID: 0} + m_ReplacementString: + m_SceneVisActive: 1 + m_LastLockedObject: {fileID: 0} + m_LastDebugDrawMode: + drawMode: 35 + name: + section: + m_ViewIsLockedToObject: 0 +--- !u!114 &15 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12015, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 100, y: 100} + m_MaxSize: {x: 4000, y: 4000} + m_TitleContent: + m_Text: Game + m_Image: {fileID: 4621777727084837110, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Game\u200B" + m_Pos: + serializedVersion: 2 + x: 676.8 + y: 86.4 + width: 693.2001 + height: 608.4 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: [] + m_ContainerData: [] + m_OverlaysVisible: 1 + m_SerializedViewNames: [] + m_SerializedViewValues: [] + m_PlayModeViewName: GameView + m_ShowGizmos: 0 + m_TargetDisplay: 0 + m_ClearColor: {r: 0, g: 0, b: 0, a: 0} + m_TargetSize: {x: 693.2001, y: 587.4} + m_TextureFilterMode: 0 + m_TextureHideFlags: 61 + m_RenderIMGUI: 1 + m_EnterPlayModeBehavior: 0 + m_UseMipMap: 0 + m_VSyncEnabled: 0 + m_Gizmos: 0 + m_Stats: 0 + m_SelectedSizes: 00000000000000000000000006000000000000000000000000000000000000000000000000000000 + m_ZoomArea: + m_HRangeLocked: 0 + m_VRangeLocked: 0 + hZoomLockedByDefault: 0 + vZoomLockedByDefault: 0 + m_HBaseRangeMin: -277.28003 + m_HBaseRangeMax: 277.28003 + m_VBaseRangeMin: -234.96 + m_VBaseRangeMax: 234.96 + m_HAllowExceedBaseRangeMin: 1 + m_HAllowExceedBaseRangeMax: 1 + m_VAllowExceedBaseRangeMin: 1 + m_VAllowExceedBaseRangeMax: 1 + m_ScaleWithWindow: 0 + m_HSlider: 0 + m_VSlider: 0 + m_IgnoreScrollWheelUntilClicked: 0 + m_EnableMouseInput: 1 + m_EnableSliderZoomHorizontal: 0 + m_EnableSliderZoomVertical: 0 + m_UniformScale: 1 + m_UpDirection: 1 + m_DrawArea: + serializedVersion: 2 + x: 0 + y: 21 + width: 693.2001 + height: 587.4 + m_Scale: {x: 1.25, y: 1.25} + m_Translation: {x: 346.60004, y: 293.7} + m_MarginLeft: 0 + m_MarginRight: 0 + m_MarginTop: 0 + m_MarginBottom: 0 + m_LastShownAreaInsideMargins: + serializedVersion: 2 + x: -277.28003 + y: -234.96 + width: 554.56006 + height: 469.92 + m_MinimalGUI: 1 + m_defaultScale: 1 + m_LastWindowPixelSize: {x: 866.5001, y: 760.5} + m_ClearInEditMode: 1 + m_NoCameraWarning: 1 + m_LowResolutionForAspectRatios: 01000001000000000000 + m_XRRenderMode: -1 + m_RenderTexture: {fileID: 0} + m_showToolbar: 1 +--- !u!114 &16 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12014, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 230, y: 250} + m_MaxSize: {x: 10000, y: 10000} + m_TitleContent: + m_Text: Project + m_Image: {fileID: -5179483145760003458, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Project\u200B" + m_Pos: + serializedVersion: 2 + x: 7.2000003 + y: 720.8 + width: 986.2 + height: 330 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: [] + m_ContainerData: [] + m_OverlaysVisible: 1 + m_SearchFilter: + m_NameFilter: + m_ClassNames: [] + m_AssetLabels: [] + m_AssetBundleNames: [] + m_ReferencingInstanceIDs: + m_SceneHandles: + m_ShowAllHits: 0 + m_SkipHidden: 0 + m_SearchArea: 1 + m_Folders: + - Assets + m_Globs: [] + m_ProductIds: + m_AnyWithAssetOrigin: 0 + m_OriginalText: + m_ImportLogFlags: 0 + m_FilterByTypeIntersection: 0 + m_ViewMode: 1 + m_StartGridSize: 96 + m_LastFolders: + - Assets + m_LastFoldersGridSize: 96 + m_LastProjectPath: D:\mg\testassetbundle\testmultiplayer + m_LockTracker: + m_IsLocked: 0 + m_FolderTreeState: + scrollPos: {x: 0, y: 45.99994} + m_SelectedIDs: eeb30000 + m_LastClickedID: 46062 + m_ExpandedIDs: 00000000eeb3000000ca9a3bffffff7f + m_RenameOverlay: + m_UserAcceptedRename: 0 + m_Name: + m_OriginalName: + m_EditFieldRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 0 + height: 0 + m_UserData: 0 + m_IsWaitingForDelay: 0 + m_IsRenaming: 0 + m_OriginalEventType: 11 + m_IsRenamingFilename: 1 + m_TrimLeadingAndTrailingWhitespace: 0 + m_ClientGUIView: {fileID: 11} + m_SearchString: + m_CreateAssetUtility: + m_EndAction: {fileID: 0} + m_InstanceID: 0 + m_Path: + m_Icon: {fileID: 0} + m_ResourceFile: + m_AssetTreeState: + scrollPos: {x: 0, y: 0} + m_SelectedIDs: + m_LastClickedID: 0 + m_ExpandedIDs: 00000000eeb30000 + m_RenameOverlay: + m_UserAcceptedRename: 0 + m_Name: + m_OriginalName: + m_EditFieldRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 0 + height: 0 + m_UserData: 0 + m_IsWaitingForDelay: 0 + m_IsRenaming: 0 + m_OriginalEventType: 11 + m_IsRenamingFilename: 1 + m_TrimLeadingAndTrailingWhitespace: 0 + m_ClientGUIView: {fileID: 0} + m_SearchString: + m_CreateAssetUtility: + m_EndAction: {fileID: 0} + m_InstanceID: 0 + m_Path: + m_Icon: {fileID: 0} + m_ResourceFile: + m_ListAreaState: + m_SelectedInstanceIDs: + m_LastClickedInstanceID: 0 + m_HadKeyboardFocusLastEvent: 1 + m_ExpandedInstanceIDs: c623000080a10000e4a0000034b00000c8140100e06e0300e6940000481e0300e84b0400163701003438010050380100463a01005c3a0100e43801004a3b0100aa140100de96000078950000404201007c3d01007a9a01001c020000501001000ef42000faf6200040ff1600fc3a0100ec8e000062ce01002cd5010096020000cceb0000d61801009e400100ee020000d4830800e414010048c20100e8140100420c0100ea14010086090100fa080100e45f0100e01401004a910100dc08020038090200262c010078960000dc320100089500004c980000a89f00000c9a0000cc9b0000ac56160052fe1300a6371d003a3a1e00a09c00009efe000004b40000f4ac0000ec9b000032a200006ae604008cbd01006a0e0100ac0e01003ab50100889b0100f40c0100cc0a0300c822010036d9000008f90300d6090300822f0400ba3c02002e200100008e060078e8070004e0120006e50000964b0100d44b01000e4c0100644b010086540100680f0100660f0100e6f60800feef1900d24c0e003c9d01008ca1270000000000ec5801000e05010070580100b6180100dc690100 + m_RenameOverlay: + m_UserAcceptedRename: 0 + m_Name: + m_OriginalName: + m_EditFieldRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 0 + height: 0 + m_UserData: 0 + m_IsWaitingForDelay: 0 + m_IsRenaming: 0 + m_OriginalEventType: 11 + m_IsRenamingFilename: 1 + m_TrimLeadingAndTrailingWhitespace: 0 + m_ClientGUIView: {fileID: 11} + m_CreateAssetUtility: + m_EndAction: {fileID: 0} + m_InstanceID: 0 + m_Path: + m_Icon: {fileID: 0} + m_ResourceFile: + m_NewAssetIndexInList: -1 + m_ScrollPosition: {x: 0, y: 113} + m_GridSize: 96 + m_SkipHiddenPackages: 0 + m_DirectoriesAreaWidth: 190.40002 +--- !u!114 &17 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12003, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 100, y: 100} + m_MaxSize: {x: 4000, y: 4000} + m_TitleContent: + m_Text: Console + m_Image: {fileID: -4950941429401207979, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Console\u200B" + m_Pos: + serializedVersion: 2 + x: 7.2000003 + y: 628 + width: 850.2 + height: 422.80005 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: [] + m_ContainerData: [] + m_OverlaysVisible: 1 +--- !u!114 &18 +MonoBehaviour: + m_ObjectHideFlags: 52 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 1 + m_Script: {fileID: 12061, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 200, y: 200} + m_MaxSize: {x: 4000, y: 4000} + m_TitleContent: + m_Text: Hierarchy + m_Image: {fileID: -3734745235275155857, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Hierarchy\u200B" + m_Pos: + serializedVersion: 2 + x: 994.4 + y: 720.8 + width: 375.60004 + height: 330 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: [] + m_ContainerData: [] + m_OverlaysVisible: 1 + m_SceneHierarchy: + m_TreeViewState: + scrollPos: {x: 0, y: 0} + m_SelectedIDs: eeb30000 + m_LastClickedID: 0 + m_ExpandedIDs: 14fbffff + m_RenameOverlay: + m_UserAcceptedRename: 0 + m_Name: + m_OriginalName: + m_EditFieldRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 0 + height: 0 + m_UserData: 0 + m_IsWaitingForDelay: 0 + m_IsRenaming: 0 + m_OriginalEventType: 11 + m_IsRenamingFilename: 0 + m_TrimLeadingAndTrailingWhitespace: 0 + m_ClientGUIView: {fileID: 8} + m_SearchString: + m_ExpandedScenes: [] + m_CurrenRootInstanceID: 0 + m_LockTracker: + m_IsLocked: 0 + m_CurrentSortingName: TransformSorting + m_WindowGUID: 4c969a2b90040154d917609493e03593 +--- !u!114 &19 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12019, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 275, y: 50} + m_MaxSize: {x: 4000, y: 4000} + m_TitleContent: + m_Text: Inspector + m_Image: {fileID: -440750813802333266, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Inspector\u200B" + m_Pos: + serializedVersion: 2 + x: 1372 + y: 86.4 + width: 419 + height: 964.4 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: [] + m_ContainerData: [] + m_OverlaysVisible: 1 + m_ObjectsLockedBeforeSerialization: [] + m_InstanceIDsLockedBeforeSerialization: + m_PreviewResizer: + m_CachedPref: -224.60004 + m_ControlHash: 1412526313 + m_PrefName: Preview_InspectorPreview + m_LastInspectedObjectInstanceID: -1 + m_LastVerticalScrollValue: 0 + m_GlobalObjectId: + m_InspectorMode: 0 + m_LockTracker: + m_IsLocked: 0 + m_PreviewWindow: {fileID: 0} +--- !u!114 &20 +MonoBehaviour: + m_ObjectHideFlags: 52 + 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: 12079, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_MinSize: {x: 390, y: 390} + m_MaxSize: {x: 4000, y: 4000} + m_TitleContent: + m_Text: Lighting + m_Image: {fileID: -1477008817101679558, guid: 0000000000000000d000000000000000, type: 0} + m_Tooltip: + m_TextWithWhitespace: "Lighting\u200B" + m_Pos: + serializedVersion: 2 + x: 1332 + y: 86.4 + width: 601.19995 + height: 964.4 + m_SerializedDataModeController: + m_DataMode: 0 + m_PreferredDataMode: 0 + m_SupportedDataModes: + isAutomatic: 1 + m_ViewDataDictionary: {fileID: 0} + m_OverlayCanvas: + m_LastAppliedPresetName: Default + m_SaveData: [] + m_ContainerData: [] + m_OverlaysVisible: 1 diff --git a/UserSettings/Search.index b/UserSettings/Search.index new file mode 100644 index 0000000..af3867a --- /dev/null +++ b/UserSettings/Search.index @@ -0,0 +1,13 @@ +{ + "name": "Assets", + "roots": ["Assets"], + "includes": [], + "excludes": ["Assets/Temp/", "Assets/External/"], + "options": { + "types": true, + "properties": true, + "extended": false, + "dependencies": true + }, + "baseScore": 999 +} \ No newline at end of file diff --git a/UserSettings/Search.settings b/UserSettings/Search.settings new file mode 100644 index 0000000..289538e --- /dev/null +++ b/UserSettings/Search.settings @@ -0,0 +1,81 @@ +trackSelection = true +refreshSearchWindowsInPlayMode = false +pickerAdvancedUI = false +fetchPreview = true +defaultFlags = 0 +keepOpen = true +queryFolder = "Assets" +onBoardingDoNotAskAgain = true +showPackageIndexes = false +showStatusBar = false +scopes = { +} +providers = { + asset = { + active = true + priority = 25 + defaultAction = null + } + scene = { + active = true + priority = 50 + defaultAction = null + } + adb = { + active = false + priority = 2500 + defaultAction = null + } + presets_provider = { + active = false + priority = -10 + defaultAction = null + } + find = { + active = true + priority = 25 + defaultAction = null + } + packages = { + active = false + priority = 90 + defaultAction = null + } + profilermarkers = { + active = false + priority = 100 + defaultAction = null + } + performance = { + active = false + priority = 100 + defaultAction = null + } + store = { + active = false + priority = 100 + defaultAction = null + } + log = { + active = false + priority = 210 + defaultAction = null + } +} +objectSelectors = { +} +recentSearches = [ +] +searchItemFavorites = [ +] +savedSearchesSortOrder = 0 +showSavedSearchPanel = false +hideTabs = false +expandedQueries = [ +] +queryBuilder = true +ignoredProperties = "id;name;classname;imagecontentshash" +helperWidgetCurrentArea = "all" +disabledIndexers = "" +minIndexVariations = 2 +findProviderIndexHelper = true \ No newline at end of file