mirror of
				https://github.com/DerTyp7/defrain-shooter-unity.git
				synced 2025-10-31 05:27:07 +01:00 
			
		
		
		
	CHANGED TO MIRROR
This commit is contained in:
		
							
								
								
									
										383
									
								
								Assets/Mirror/Editor/Weaver/Readers.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										383
									
								
								Assets/Mirror/Editor/Weaver/Readers.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,383 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using Mono.CecilX; | ||||
| using Mono.CecilX.Cil; | ||||
| // to use Mono.CecilX.Rocks here, we need to 'override references' in the | ||||
| // Unity.Mirror.CodeGen assembly definition file in the Editor, and add CecilX.Rocks. | ||||
| // otherwise we get an unknown import exception. | ||||
| using Mono.CecilX.Rocks; | ||||
|  | ||||
| namespace Mirror.Weaver | ||||
| { | ||||
|     // not static, because ILPostProcessor is multithreaded | ||||
|     public class Readers | ||||
|     { | ||||
|         // Readers are only for this assembly. | ||||
|         // can't be used from another assembly, otherwise we will get: | ||||
|         // "System.ArgumentException: Member ... is declared in another module and needs to be imported" | ||||
|         AssemblyDefinition assembly; | ||||
|         WeaverTypes weaverTypes; | ||||
|         TypeDefinition GeneratedCodeClass; | ||||
|         Logger Log; | ||||
|  | ||||
|         Dictionary<TypeReference, MethodReference> readFuncs = | ||||
|             new Dictionary<TypeReference, MethodReference>(new TypeReferenceComparer()); | ||||
|  | ||||
|         public Readers(AssemblyDefinition assembly, WeaverTypes weaverTypes, TypeDefinition GeneratedCodeClass, Logger Log) | ||||
|         { | ||||
|             this.assembly = assembly; | ||||
|             this.weaverTypes = weaverTypes; | ||||
|             this.GeneratedCodeClass = GeneratedCodeClass; | ||||
|             this.Log = Log; | ||||
|         } | ||||
|  | ||||
|         internal void Register(TypeReference dataType, MethodReference methodReference) | ||||
|         { | ||||
|             if (readFuncs.ContainsKey(dataType)) | ||||
|             { | ||||
|                 // TODO enable this again later. | ||||
|                 // Reader has some obsolete functions that were renamed. | ||||
|                 // Don't want weaver warnings for all of them. | ||||
|                 //Log.Warning($"Registering a Read method for {dataType.FullName} when one already exists", methodReference); | ||||
|             } | ||||
|  | ||||
|             // we need to import type when we Initialize Readers so import here in case it is used anywhere else | ||||
|             TypeReference imported = assembly.MainModule.ImportReference(dataType); | ||||
|             readFuncs[imported] = methodReference; | ||||
|         } | ||||
|  | ||||
|         void RegisterReadFunc(TypeReference typeReference, MethodDefinition newReaderFunc) | ||||
|         { | ||||
|             Register(typeReference, newReaderFunc); | ||||
|             GeneratedCodeClass.Methods.Add(newReaderFunc); | ||||
|         } | ||||
|  | ||||
|         // Finds existing reader for type, if non exists trys to create one | ||||
|         public MethodReference GetReadFunc(TypeReference variable, ref bool WeavingFailed) | ||||
|         { | ||||
|             if (readFuncs.TryGetValue(variable, out MethodReference foundFunc)) | ||||
|                 return foundFunc; | ||||
|  | ||||
|             TypeReference importedVariable = assembly.MainModule.ImportReference(variable); | ||||
|             return GenerateReader(importedVariable, ref WeavingFailed); | ||||
|         } | ||||
|  | ||||
|         MethodReference GenerateReader(TypeReference variableReference, ref bool WeavingFailed) | ||||
|         { | ||||
|             // Arrays are special,  if we resolve them, we get the element type, | ||||
|             // so the following ifs might choke on it for scriptable objects | ||||
|             // or other objects that require a custom serializer | ||||
|             // thus check if it is an array and skip all the checks. | ||||
|             if (variableReference.IsArray) | ||||
|             { | ||||
|                 if (variableReference.IsMultidimensionalArray()) | ||||
|                 { | ||||
|                     Log.Error($"{variableReference.Name} is an unsupported type. Multidimensional arrays are not supported", variableReference); | ||||
|                     WeavingFailed = true; | ||||
|                     return null; | ||||
|                 } | ||||
|  | ||||
|                 return GenerateReadCollection(variableReference, variableReference.GetElementType(), nameof(NetworkReaderExtensions.ReadArray), ref WeavingFailed); | ||||
|             } | ||||
|  | ||||
|             TypeDefinition variableDefinition = variableReference.Resolve(); | ||||
|  | ||||
|             // check if the type is completely invalid | ||||
|             if (variableDefinition == null) | ||||
|             { | ||||
|                 Log.Error($"{variableReference.Name} is not a supported type", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|             else if (variableReference.IsByReference) | ||||
|             { | ||||
|                 // error?? | ||||
|                 Log.Error($"Cannot pass type {variableReference.Name} by reference", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             // use existing func for known types | ||||
|             if (variableDefinition.IsEnum) | ||||
|             { | ||||
|                 return GenerateEnumReadFunc(variableReference, ref WeavingFailed); | ||||
|             } | ||||
|             else if (variableDefinition.Is(typeof(ArraySegment<>))) | ||||
|             { | ||||
|                 return GenerateArraySegmentReadFunc(variableReference, ref WeavingFailed); | ||||
|             } | ||||
|             else if (variableDefinition.Is(typeof(List<>))) | ||||
|             { | ||||
|                 GenericInstanceType genericInstance = (GenericInstanceType)variableReference; | ||||
|                 TypeReference elementType = genericInstance.GenericArguments[0]; | ||||
|  | ||||
|                 return GenerateReadCollection(variableReference, elementType, nameof(NetworkReaderExtensions.ReadList), ref WeavingFailed); | ||||
|             } | ||||
|             else if (variableReference.IsDerivedFrom<NetworkBehaviour>()) | ||||
|             { | ||||
|                 return GetNetworkBehaviourReader(variableReference); | ||||
|             } | ||||
|  | ||||
|             // check if reader generation is applicable on this type | ||||
|             if (variableDefinition.IsDerivedFrom<UnityEngine.Component>()) | ||||
|             { | ||||
|                 Log.Error($"Cannot generate reader for component type {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|             if (variableReference.Is<UnityEngine.Object>()) | ||||
|             { | ||||
|                 Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|             if (variableReference.Is<UnityEngine.ScriptableObject>()) | ||||
|             { | ||||
|                 Log.Error($"Cannot generate reader for {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|             if (variableDefinition.HasGenericParameters) | ||||
|             { | ||||
|                 Log.Error($"Cannot generate reader for generic variable {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|             if (variableDefinition.IsInterface) | ||||
|             { | ||||
|                 Log.Error($"Cannot generate reader for interface {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|             if (variableDefinition.IsAbstract) | ||||
|             { | ||||
|                 Log.Error($"Cannot generate reader for abstract class {variableReference.Name}. Use a supported type or provide a custom reader", variableReference); | ||||
|                 WeavingFailed = true; | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             return GenerateClassOrStructReadFunction(variableReference, ref WeavingFailed); | ||||
|         } | ||||
|  | ||||
|         MethodReference GetNetworkBehaviourReader(TypeReference variableReference) | ||||
|         { | ||||
|             // uses generic ReadNetworkBehaviour rather than having weaver create one for each NB | ||||
|             MethodReference generic = weaverTypes.readNetworkBehaviourGeneric; | ||||
|  | ||||
|             MethodReference readFunc = generic.MakeGeneric(assembly.MainModule, variableReference); | ||||
|  | ||||
|             // register function so it is added to Reader<T> | ||||
|             // use Register instead of RegisterWriteFunc because this is not a generated function | ||||
|             Register(variableReference, readFunc); | ||||
|  | ||||
|             return readFunc; | ||||
|         } | ||||
|  | ||||
|         MethodDefinition GenerateEnumReadFunc(TypeReference variable, ref bool WeavingFailed) | ||||
|         { | ||||
|             MethodDefinition readerFunc = GenerateReaderFunction(variable); | ||||
|  | ||||
|             ILProcessor worker = readerFunc.Body.GetILProcessor(); | ||||
|  | ||||
|             worker.Emit(OpCodes.Ldarg_0); | ||||
|  | ||||
|             TypeReference underlyingType = variable.Resolve().GetEnumUnderlyingType(); | ||||
|             MethodReference underlyingFunc = GetReadFunc(underlyingType, ref WeavingFailed); | ||||
|  | ||||
|             worker.Emit(OpCodes.Call, underlyingFunc); | ||||
|             worker.Emit(OpCodes.Ret); | ||||
|             return readerFunc; | ||||
|         } | ||||
|  | ||||
|         MethodDefinition GenerateArraySegmentReadFunc(TypeReference variable, ref bool WeavingFailed) | ||||
|         { | ||||
|             GenericInstanceType genericInstance = (GenericInstanceType)variable; | ||||
|             TypeReference elementType = genericInstance.GenericArguments[0]; | ||||
|  | ||||
|             MethodDefinition readerFunc = GenerateReaderFunction(variable); | ||||
|  | ||||
|             ILProcessor worker = readerFunc.Body.GetILProcessor(); | ||||
|  | ||||
|             // $array = reader.Read<[T]>() | ||||
|             ArrayType arrayType = elementType.MakeArrayType(); | ||||
|             worker.Emit(OpCodes.Ldarg_0); | ||||
|             worker.Emit(OpCodes.Call, GetReadFunc(arrayType, ref WeavingFailed)); | ||||
|  | ||||
|             // return new ArraySegment<T>($array); | ||||
|             worker.Emit(OpCodes.Newobj, weaverTypes.ArraySegmentConstructorReference.MakeHostInstanceGeneric(assembly.MainModule, genericInstance)); | ||||
|             worker.Emit(OpCodes.Ret); | ||||
|             return readerFunc; | ||||
|         } | ||||
|  | ||||
|         MethodDefinition GenerateReaderFunction(TypeReference variable) | ||||
|         { | ||||
|             string functionName = $"_Read_{variable.FullName}"; | ||||
|  | ||||
|             // create new reader for this type | ||||
|             MethodDefinition readerFunc = new MethodDefinition(functionName, | ||||
|                     MethodAttributes.Public | | ||||
|                     MethodAttributes.Static | | ||||
|                     MethodAttributes.HideBySig, | ||||
|                     variable); | ||||
|  | ||||
|             readerFunc.Parameters.Add(new ParameterDefinition("reader", ParameterAttributes.None, weaverTypes.Import<NetworkReader>())); | ||||
|             readerFunc.Body.InitLocals = true; | ||||
|             RegisterReadFunc(variable, readerFunc); | ||||
|  | ||||
|             return readerFunc; | ||||
|         } | ||||
|  | ||||
|         MethodDefinition GenerateReadCollection(TypeReference variable, TypeReference elementType, string readerFunction, ref bool WeavingFailed) | ||||
|         { | ||||
|             MethodDefinition readerFunc = GenerateReaderFunction(variable); | ||||
|             // generate readers for the element | ||||
|             GetReadFunc(elementType, ref WeavingFailed); | ||||
|  | ||||
|             ModuleDefinition module = assembly.MainModule; | ||||
|             TypeReference readerExtensions = module.ImportReference(typeof(NetworkReaderExtensions)); | ||||
|             MethodReference listReader = Resolvers.ResolveMethod(readerExtensions, assembly, Log, readerFunction, ref WeavingFailed); | ||||
|  | ||||
|             GenericInstanceMethod methodRef = new GenericInstanceMethod(listReader); | ||||
|             methodRef.GenericArguments.Add(elementType); | ||||
|  | ||||
|             // generates | ||||
|             // return reader.ReadList<T>(); | ||||
|  | ||||
|             ILProcessor worker = readerFunc.Body.GetILProcessor(); | ||||
|             worker.Emit(OpCodes.Ldarg_0); // reader | ||||
|             worker.Emit(OpCodes.Call, methodRef); // Read | ||||
|  | ||||
|             worker.Emit(OpCodes.Ret); | ||||
|  | ||||
|             return readerFunc; | ||||
|         } | ||||
|  | ||||
|         MethodDefinition GenerateClassOrStructReadFunction(TypeReference variable, ref bool WeavingFailed) | ||||
|         { | ||||
|             MethodDefinition readerFunc = GenerateReaderFunction(variable); | ||||
|  | ||||
|             // create local for return value | ||||
|             readerFunc.Body.Variables.Add(new VariableDefinition(variable)); | ||||
|  | ||||
|             ILProcessor worker = readerFunc.Body.GetILProcessor(); | ||||
|  | ||||
|             TypeDefinition td = variable.Resolve(); | ||||
|  | ||||
|             if (!td.IsValueType) | ||||
|                 GenerateNullCheck(worker, ref WeavingFailed); | ||||
|  | ||||
|             CreateNew(variable, worker, td, ref WeavingFailed); | ||||
|             ReadAllFields(variable, worker, ref WeavingFailed); | ||||
|  | ||||
|             worker.Emit(OpCodes.Ldloc_0); | ||||
|             worker.Emit(OpCodes.Ret); | ||||
|             return readerFunc; | ||||
|         } | ||||
|  | ||||
|         void GenerateNullCheck(ILProcessor worker, ref bool WeavingFailed) | ||||
|         { | ||||
|             // if (!reader.ReadBoolean()) { | ||||
|             //   return null; | ||||
|             // } | ||||
|             worker.Emit(OpCodes.Ldarg_0); | ||||
|             worker.Emit(OpCodes.Call, GetReadFunc(weaverTypes.Import<bool>(), ref WeavingFailed)); | ||||
|  | ||||
|             Instruction labelEmptyArray = worker.Create(OpCodes.Nop); | ||||
|             worker.Emit(OpCodes.Brtrue, labelEmptyArray); | ||||
|             // return null | ||||
|             worker.Emit(OpCodes.Ldnull); | ||||
|             worker.Emit(OpCodes.Ret); | ||||
|             worker.Append(labelEmptyArray); | ||||
|         } | ||||
|  | ||||
|         // Initialize the local variable with a new instance | ||||
|         void CreateNew(TypeReference variable, ILProcessor worker, TypeDefinition td, ref bool WeavingFailed) | ||||
|         { | ||||
|             if (variable.IsValueType) | ||||
|             { | ||||
|                 // structs are created with Initobj | ||||
|                 worker.Emit(OpCodes.Ldloca, 0); | ||||
|                 worker.Emit(OpCodes.Initobj, variable); | ||||
|             } | ||||
|             else if (td.IsDerivedFrom<UnityEngine.ScriptableObject>()) | ||||
|             { | ||||
|                 GenericInstanceMethod genericInstanceMethod = new GenericInstanceMethod(weaverTypes.ScriptableObjectCreateInstanceMethod); | ||||
|                 genericInstanceMethod.GenericArguments.Add(variable); | ||||
|                 worker.Emit(OpCodes.Call, genericInstanceMethod); | ||||
|                 worker.Emit(OpCodes.Stloc_0); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // classes are created with their constructor | ||||
|                 MethodDefinition ctor = Resolvers.ResolveDefaultPublicCtor(variable); | ||||
|                 if (ctor == null) | ||||
|                 { | ||||
|                     Log.Error($"{variable.Name} can't be deserialized because it has no default constructor. Don't use {variable.Name} in [SyncVar]s, Rpcs, Cmds, etc.", variable); | ||||
|                     WeavingFailed = true; | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 MethodReference ctorRef = assembly.MainModule.ImportReference(ctor); | ||||
|  | ||||
|                 worker.Emit(OpCodes.Newobj, ctorRef); | ||||
|                 worker.Emit(OpCodes.Stloc_0); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         void ReadAllFields(TypeReference variable, ILProcessor worker, ref bool WeavingFailed) | ||||
|         { | ||||
|             foreach (FieldDefinition field in variable.FindAllPublicFields()) | ||||
|             { | ||||
|                 // mismatched ldloca/ldloc for struct/class combinations is invalid IL, which causes crash at runtime | ||||
|                 OpCode opcode = variable.IsValueType ? OpCodes.Ldloca : OpCodes.Ldloc; | ||||
|                 worker.Emit(opcode, 0); | ||||
|                 MethodReference readFunc = GetReadFunc(field.FieldType, ref WeavingFailed); | ||||
|                 if (readFunc != null) | ||||
|                 { | ||||
|                     worker.Emit(OpCodes.Ldarg_0); | ||||
|                     worker.Emit(OpCodes.Call, readFunc); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     Log.Error($"{field.Name} has an unsupported type", field); | ||||
|                     WeavingFailed = true; | ||||
|                 } | ||||
|                 FieldReference fieldRef = assembly.MainModule.ImportReference(field); | ||||
|  | ||||
|                 worker.Emit(OpCodes.Stfld, fieldRef); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Save a delegate for each one of the readers into Reader<T>.read | ||||
|         internal void InitializeReaders(ILProcessor worker) | ||||
|         { | ||||
|             ModuleDefinition module = assembly.MainModule; | ||||
|  | ||||
|             TypeReference genericReaderClassRef = module.ImportReference(typeof(Reader<>)); | ||||
|  | ||||
|             System.Reflection.FieldInfo fieldInfo = typeof(Reader<>).GetField(nameof(Reader<object>.read)); | ||||
|             FieldReference fieldRef = module.ImportReference(fieldInfo); | ||||
|             TypeReference networkReaderRef = module.ImportReference(typeof(NetworkReader)); | ||||
|             TypeReference funcRef = module.ImportReference(typeof(Func<,>)); | ||||
|             MethodReference funcConstructorRef = module.ImportReference(typeof(Func<,>).GetConstructors()[0]); | ||||
|  | ||||
|             foreach (KeyValuePair<TypeReference, MethodReference> kvp in readFuncs) | ||||
|             { | ||||
|                 TypeReference targetType = kvp.Key; | ||||
|                 MethodReference readFunc = kvp.Value; | ||||
|  | ||||
|                 // create a Func<NetworkReader, T> delegate | ||||
|                 worker.Emit(OpCodes.Ldnull); | ||||
|                 worker.Emit(OpCodes.Ldftn, readFunc); | ||||
|                 GenericInstanceType funcGenericInstance = funcRef.MakeGenericInstanceType(networkReaderRef, targetType); | ||||
|                 MethodReference funcConstructorInstance = funcConstructorRef.MakeHostInstanceGeneric(assembly.MainModule, funcGenericInstance); | ||||
|                 worker.Emit(OpCodes.Newobj, funcConstructorInstance); | ||||
|  | ||||
|                 // save it in Reader<T>.read | ||||
|                 GenericInstanceType genericInstance = genericReaderClassRef.MakeGenericInstanceType(targetType); | ||||
|                 FieldReference specializedField = fieldRef.SpecializeField(assembly.MainModule, genericInstance); | ||||
|                 worker.Emit(OpCodes.Stsfld, specializedField); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 DerTyp187
					DerTyp187