static void CheckValidity(AuthoringComponent authoringComponent) { switch (authoringComponent.Interface) { case AuthoringComponentInterface.IComponentData: if (authoringComponent.FromValueType && authoringComponent.FieldDescriptions.Any(d => d.FieldType == FieldType.EntityArray)) { throw new ArgumentException( "Invalid use of Entity[] in a struct: IComponentData structs cannot contain managed types." + "Either use an array that works in IComponentData structs (DynamicBuffer) or a IComponentData class."); } break; case AuthoringComponentInterface.IBufferElementData: if (!authoringComponent.FromValueType) { throw new ArgumentException("IBufferElementData types must be structs."); } if (authoringComponent.FieldDescriptions.Any(d => d.FieldType == FieldType.NonEntityReferenceType || d.FieldType == FieldType.EntityArray)) { throw new ArgumentException("IBufferElementData types may only contain blittable or primitive fields."); } break; default: throw new ArgumentOutOfRangeException( "The [GenerateAuthoringComponent] attribute may only be used with types " + "that implement either IBufferElementData or IComponentData."); } }
private static string CreateDeclareReferencedPrefabsMethodBody(AuthoringComponent authoringComponent) { var methodBody = new StringBuilder(); foreach (AuthoringComponentFieldDescription description in authoringComponent.FieldDescriptions) { switch (description.FieldType) { case FieldType.SingleEntity: methodBody.AppendLine( $@"Unity.Entities.Hybrid.Internal.GeneratedAuthoringComponentImplementation.AddReferencedPrefab( __referencedPrefabs, {description.FieldSymbol.Name});"); break; case FieldType.EntityArray: methodBody.AppendLine( $@"Unity.Entities.Hybrid.Internal.GeneratedAuthoringComponentImplementation.AddReferencedPrefabs( __referencedPrefabs, {description.FieldSymbol.Name});"); break; } } return(methodBody.ToString()); }
static string GenerateBufferElementDataFieldAssignments(AuthoringComponent authoringComponent) { if (authoringComponent.FieldDescriptions.Count() > 1) { return(authoringComponent.FieldDescriptions.Select(description => { string fieldName = description.FieldSymbol.Name; return $"{fieldName} = Values[i].{fieldName}"; }).SeparateByCommaAndNewLine()); } return($"{authoringComponent.FieldDescriptions.Single().FieldSymbol.Name} = Values[i]"); }
internal static ClassDeclarationSyntax GenerateBufferingElementDataAuthoring(AuthoringComponent authoringComponent) { bool exactlyOneField = authoringComponent.FieldDescriptions.Count() == 1; if (exactlyOneField) { return(GenerateBufferingElementDataAuthoringClass(authoringComponent, authoringComponent.FieldDescriptions.Single().FieldSymbol.Type.ToString())); } string generatedStructName = $"___{authoringComponent.DeclaringType.Name}GeneratedStruct___"; return(GenerateBufferingElementDataAuthoringClass(authoringComponent, generatedStructName, hasNestedGeneratedClass: true)); }
static ClassDeclarationSyntax GenerateBufferingElementDataAuthoringClass( AuthoringComponent authoringComponent, string storedType, bool hasNestedGeneratedClass = false) { const string placeholder = "___HasNestedGeneratedClassPlaceholder___"; string generatedClass = $@"[UnityEngine.DisallowMultipleComponent] [System.Runtime.CompilerServices.CompilerGenerated] {authoringComponent.AttributesToPreserve.Select(a => $"[{a}]").SeparateByNewLine()} public class {authoringComponent.DeclaringType.Name}Authoring : UnityEngine.MonoBehaviour, Unity.Entities.IConvertGameObjectToEntity {{ {placeholder} public {storedType}[] Values; public void Convert(Unity.Entities.Entity __entity, Unity.Entities.EntityManager __dstManager, GameObjectConversionSystem _) {{ Unity.Entities.DynamicBuffer<{authoringComponent.FullyQualifiedTypeName}> dynamicBuffer = __dstManager.AddBuffer<{authoringComponent.FullyQualifiedTypeName}>(__entity); dynamicBuffer.ResizeUninitialized(Values.Length); for (int i = 0; i < dynamicBuffer.Length; i++) {{ dynamicBuffer[i] = new {authoringComponent.FullyQualifiedTypeName} {{ {GenerateBufferElementDataFieldAssignments(authoringComponent)} }}; }} }} }}"; if (!hasNestedGeneratedClass) { return((ClassDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(generatedClass.Replace(placeholder, String.Empty))); } string withNestedType = generatedClass.Replace( placeholder, $@"[System.Serializable] [System.Runtime.CompilerServices.CompilerGenerated] public struct {storedType} {{ {CreateBufferElementDataClassFields(authoringComponent)} }}"); return((ClassDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(withNestedType)); }
static MemberDeclarationSyntax GenerateAuthoringTypeFrom(AuthoringComponent authoringComponent) { switch (authoringComponent.Interface) { case AuthoringComponentInterface.IComponentData: return(AuthoringComponentFactory.GenerateComponentDataAuthoring(authoringComponent) .AddNamespaces(authoringComponent.NamespacesFromMostToLeastNested)); case AuthoringComponentInterface.IBufferElementData: return(AuthoringComponentFactory.GenerateBufferingElementDataAuthoring(authoringComponent) .AddNamespaces(authoringComponent.NamespacesFromMostToLeastNested)); default: return(default); } }
private static string CreateConversionMethodBody(AuthoringComponent authoringComponent) { StringBuilder methodBody = new StringBuilder(); if (authoringComponent.DeclaringType.IsValueType) { methodBody.AppendLine( $"{authoringComponent.FullyQualifiedTypeName} component = default({authoringComponent.FullyQualifiedTypeName});"); } else { methodBody.AppendLine( $"{authoringComponent.FullyQualifiedTypeName} component = new {authoringComponent.FullyQualifiedTypeName}();"); } foreach (var fieldDescription in authoringComponent.FieldDescriptions) { switch (fieldDescription.FieldType) { case FieldType.SingleEntity: methodBody.AppendLine( $"component.{fieldDescription.FieldSymbol.Name} = __conversionSystem.GetPrimaryEntity({fieldDescription.FieldSymbol.Name});"); break; case FieldType.EntityArray: methodBody.AppendLine( $@"Unity.Entities.GameObjectConversionUtility.ConvertGameObjectsToEntitiesField( __conversionSystem, {fieldDescription.FieldSymbol.Name}, out component.{fieldDescription.FieldSymbol.Name});"); break; default: methodBody.AppendLine($"component.{fieldDescription.FieldSymbol.Name} = {fieldDescription.FieldSymbol.Name};"); break; } } methodBody.AppendLine(authoringComponent.FromValueType ? "__dstManager.AddComponentData(__entity, component);" : "Unity.Entities.EntityManagerManagedComponentExtensions.AddComponentData(__dstManager, __entity, component);"); return(methodBody.ToString()); }
static string CreateComponentDataAuthoringClass(AuthoringComponent authoringComponent) { string declareReferencedPrefabsInterface = authoringComponent.ImplementIDeclareReferencedPrefabs ? ", Unity.Entities.IDeclareReferencedPrefabs" : string.Empty; const string placeholder = "$$$InsertDeclareReferencedPrefabsMethodHere$$$"; string generatedClass = $@"[UnityEngine.DisallowMultipleComponent] [System.Runtime.CompilerServices.CompilerGenerated] {authoringComponent.AttributesToPreserve.Select(a => $"[{a}]").SeparateByNewLine()} public class {authoringComponent.DeclaringType.Name}Authoring : UnityEngine.MonoBehaviour, Unity.Entities.IConvertGameObjectToEntity{declareReferencedPrefabsInterface} {{ {CreateAuthoringClassFieldDeclarations(authoringComponent)} public void Convert(Unity.Entities.Entity __entity, Unity.Entities.EntityManager __dstManager, GameObjectConversionSystem __conversionSystem) {{ {CreateConversionMethodBody(authoringComponent)} }} {placeholder} }}"; if (!authoringComponent.ImplementIDeclareReferencedPrefabs) { return(generatedClass.Replace(oldValue: placeholder, newValue: string.Empty)); } string declareReferencedPrefabsMethod = $@"public void DeclareReferencedPrefabs(System.Collections.Generic.List<UnityEngine.GameObject> __referencedPrefabs) {{ {CreateDeclareReferencedPrefabsMethodBody(authoringComponent)} }}"; return(generatedClass.Replace(oldValue: placeholder, newValue: declareReferencedPrefabsMethod)); }
private static string CreateAuthoringClassFieldDeclarations(AuthoringComponent authoringComponent) { var fieldDeclarations = new StringBuilder(); foreach (var fieldDescription in authoringComponent.FieldDescriptions) { switch (fieldDescription.FieldType) { case FieldType.SingleEntity: fieldDeclarations.AppendLine($"public UnityEngine.GameObject {fieldDescription.FieldSymbol.Name};"); break; case FieldType.EntityArray: fieldDeclarations.AppendLine($"public UnityEngine.GameObject[] {fieldDescription.FieldSymbol.Name};"); break; default: fieldDeclarations.AppendLine($"public {fieldDescription.FieldSymbol.Type} {fieldDescription.FieldSymbol.Name};"); break; } } return(fieldDeclarations.ToString()); }
public void Execute(GeneratorExecutionContext context) { bool IsGeneratedAuthoringComponentAttribute(AttributeData data) { return(data.AttributeClass.ContainingAssembly.Name == "Unity.Entities" && data.AttributeClass.Name == "GenerateAuthoringComponentAttribute"); } try { if (context.Compilation.Assembly.Name.Contains("CodeGen.Tests")) { return; } var authoringComponentReceiver = (AuthoringComponentReceiver)context.SyntaxReceiver; if (!authoringComponentReceiver.CandidateSyntaxes.Any() || !context.Compilation.ReferencedAssemblyNames.Any(n => n.Name == "Unity.Entities.Hybrid")) { // TODO: I believe it is valid to have GenerateAuthoringComponent but not reference entities (DocCodeSamples.Tests currently does this). // We should probably throw a warning here though. //throw new ArgumentException("Using the [GenerateAuthoringComponent] attribute requires a reference to the Unity.Entities.Hybrid assembly."); return; } SourceGenHelpers.LogInfo($"Source generating assembly {context.Compilation.Assembly.Name} for authoring components..."); var stopwatch = Stopwatch.StartNew();; foreach (var candidateSyntax in authoringComponentReceiver.CandidateSyntaxes) { var semanticModel = context.Compilation.GetSemanticModel(candidateSyntax.SyntaxTree); var candidateSymbol = semanticModel.GetDeclaredSymbol(candidateSyntax); LogInfo($"Parsing authoring component {candidateSymbol.Name}"); if (!candidateSymbol.GetAttributes().Any(IsGeneratedAuthoringComponentAttribute)) { continue; } var authoringComponent = new AuthoringComponent(candidateSyntax, candidateSymbol, context); CheckValidity(authoringComponent); var compilationUnit = CompilationUnit().AddMembers(GenerateAuthoringTypeFrom(authoringComponent)) .NormalizeWhitespace(); var generatedSourceHint = candidateSyntax.SyntaxTree.GetGeneratedSourceFileName(context.Compilation.Assembly); var generatedSourceFullPath = candidateSyntax.SyntaxTree.GetGeneratedSourceFilePath(context.Compilation.Assembly); var sourceTextForNewClass = compilationUnit.GetText(Encoding.UTF8); sourceTextForNewClass = sourceTextForNewClass.WithInitialLineDirectiveToGeneratedSource(generatedSourceFullPath); context.AddSource(generatedSourceHint, sourceTextForNewClass); File.WriteAllLines( generatedSourceFullPath, sourceTextForNewClass.ToString().Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)); } stopwatch.Stop(); SourceGenHelpers.LogInfo($"TIME : AuthoringComponent : {context.Compilation.Assembly.Name} : {stopwatch.ElapsedMilliseconds}ms"); } catch (Exception exception) { //context.WaitForDebuggerInAssembly(); context.LogError("SGICE002", "Authoring Component", exception.ToString(), context.Compilation.SyntaxTrees.First().GetRoot().GetLocation()); } }
static string CreateBufferElementDataClassFields(AuthoringComponent authoringComponent) { return(authoringComponent.FieldDescriptions .Select(description => $"public {description.FieldSymbol.Type} {description.FieldSymbol.Name};") .SeparateByNewLine()); }
internal static ClassDeclarationSyntax GenerateComponentDataAuthoring(AuthoringComponent authoringComponent) { string generatedClass = CreateComponentDataAuthoringClass(authoringComponent); return((ClassDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(generatedClass)); }