protected void VerifySyncValidForType(System.Type typeToSync, UdonSyncMode syncMode) { if (syncMode == UdonSyncMode.NotSynced) { return; } if (visitorContext.behaviourSyncMode == BehaviourSyncMode.NoVariableSync) { throw new System.Exception($"Cannot sync variable because behaviour is set to NoVariableSync, change the behaviour sync mode to sync variables"); } if (!VRC.Udon.UdonNetworkTypes.CanSync(typeToSync) && typeToSync != typeof(uint) && typeToSync != typeof(uint[])) // Workaround for the uint types missing from the syncable type list >_> { throw new System.NotSupportedException($"Udon does not currently support syncing of the type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } else if (syncMode == UdonSyncMode.Linear && !VRC.Udon.UdonNetworkTypes.CanSyncLinear(typeToSync)) { throw new System.NotSupportedException($"Udon does not support linear interpolation of the synced type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } else if (syncMode == UdonSyncMode.Smooth && !VRC.Udon.UdonNetworkTypes.CanSyncSmooth(typeToSync)) { throw new System.NotSupportedException($"Udon does not support smooth interpolation of the synced type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } if (visitorContext.behaviourSyncMode == BehaviourSyncMode.Manual && syncMode != UdonSyncMode.None) { throw new System.NotSupportedException($"Udon does not support variable tweening when the behaviour is in Manual sync mode"); } else if (visitorContext.behaviourSyncMode == BehaviourSyncMode.Continuous && typeToSync.IsArray) { throw new System.NotSupportedException($"Syncing of array type {UdonSharpUtils.PrettifyTypeName(typeToSync.GetElementType())}[] is only supported in manual sync mode"); } }
public override void VisitFieldDeclaration(FieldDeclarationSyntax node) { UpdateSyntaxNode(node); var variables = node.Declaration.Variables; for (int i = 0; i < variables.Count; ++i) { VariableDeclaratorSyntax variable = variables[i]; if (variable.Initializer != null) { fieldsWithInitializers.Add(node); } } if (node.Modifiers.HasModifier("static")) { throw new System.NotSupportedException("Static fields are not yet supported by UdonSharp"); } UdonSyncMode fieldSyncMode = GetSyncAttributeValue(node); List <System.Attribute> fieldAttributes = GetFieldAttributes(node); bool isPublic = (node.Modifiers.Any(SyntaxKind.PublicKeyword) || fieldAttributes.Find(e => e is SerializeField) != null) && fieldAttributes.Find(e => e is System.NonSerializedAttribute) == null; bool isConst = (node.Modifiers.Any(SyntaxKind.ConstKeyword) || node.Modifiers.Any(SyntaxKind.ReadOnlyKeyword)); SymbolDeclTypeFlags flags = (isPublic ? SymbolDeclTypeFlags.Public : SymbolDeclTypeFlags.Private) | (isConst ? SymbolDeclTypeFlags.Readonly : 0); List <SymbolDefinition> fieldSymbols = HandleVariableDeclaration(node.Declaration, flags, fieldSyncMode); foreach (SymbolDefinition fieldSymbol in fieldSymbols) { FieldDefinition fieldDefinition = new FieldDefinition(fieldSymbol); fieldDefinition.fieldAttributes = fieldAttributes; if (fieldSymbol.IsUserDefinedType()) { System.Type fieldType = fieldSymbol.userCsType; while (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } foreach (ClassDefinition classDefinition in visitorContext.externClassDefinitions) { if (classDefinition.userClassType == fieldType) { fieldDefinition.userBehaviourSource = classDefinition.classScript; break; } } } visitorContext.localFieldDefinitions.Add(fieldSymbol.symbolUniqueName, fieldDefinition); } }
private UdonSyncMode GetSyncAttributeValue(FieldDeclarationSyntax node) { UdonSyncMode syncMode = UdonSyncMode.NotSynced; if (node.AttributeLists != null) { foreach (AttributeListSyntax attributeList in node.AttributeLists) { foreach (AttributeSyntax attribute in attributeList.Attributes) { using (ExpressionCaptureScope attributeTypeCapture = new ExpressionCaptureScope(visitorContext, null)) { attributeTypeCapture.isAttributeCaptureScope = true; Visit(attribute.Name); if (attributeTypeCapture.captureType != typeof(UdonSyncedAttribute)) { continue; } if (attribute.ArgumentList == null || attribute.ArgumentList.Arguments == null || attribute.ArgumentList.Arguments.Count == 0) { syncMode = UdonSyncMode.None; } else { using (ExpressionCaptureScope attributeCaptureScope = new ExpressionCaptureScope(visitorContext, null)) { Visit(attribute.ArgumentList.Arguments[0].Expression); if (!attributeCaptureScope.IsEnum()) { throw new System.Exception("Invalid attribute argument provided for sync"); } syncMode = (UdonSyncMode)attributeCaptureScope.GetEnumValue(); } } break; } } if (syncMode != UdonSyncMode.NotSynced) { break; } } } return(syncMode); }
private void VerifySyncValidForType(System.Type typeToSync, UdonSyncMode syncMode) { if (syncMode == UdonSyncMode.NotSynced) { return; } #if UDON_BETA_SDK if (!VRC.Udon.UdonNetworkTypes.CanSync(typeToSync)) { throw new System.NotSupportedException($"Udon does not currently support syncing of the type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } else if (syncMode == UdonSyncMode.Linear && !VRC.Udon.UdonNetworkTypes.CanSyncLinear(typeToSync)) { throw new System.NotSupportedException($"Udon does not support linear interpolation of the synced type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } else if (syncMode == UdonSyncMode.Smooth && !VRC.Udon.UdonNetworkTypes.CanSyncSmooth(typeToSync)) { throw new System.NotSupportedException($"Udon does not support smooth interpolation of the synced type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } if (visitorContext.behaviourSyncMode == BehaviourSyncMode.Manual && syncMode != UdonSyncMode.None) { throw new System.NotSupportedException($"Udon does not support variable tweening when the behaviour is in Manual sync mode"); } #else if (!UdonSharpUtils.IsUdonSyncedType(typeToSync)) { throw new System.NotSupportedException($"Udon does not currently support syncing of the type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } if (syncMode != UdonSyncMode.None && (typeToSync == typeof(string) || typeToSync == typeof(char))) { throw new System.NotSupportedException($"Udon does not support tweening the synced type '{UdonSharpUtils.PrettifyTypeName(typeToSync)}'"); } #endif }
public UdonSyncedAttribute(UdonSyncMode networkSyncTypeIn = UdonSyncMode.None) { NetworkSyncType = networkSyncTypeIn; }
protected List <SymbolDefinition> HandleVariableDeclaration(VariableDeclarationSyntax node, SymbolDeclTypeFlags symbolType, UdonSyncMode syncMode) { UpdateSyntaxNode(node); bool isVar = node.Type.IsVar; System.Type variableType = null; if (!isVar) { using (ExpressionCaptureScope typeCapture = new ExpressionCaptureScope(visitorContext, null)) { Visit(node.Type); if (!typeCapture.IsType()) { throw new System.Exception($"The type or namespace name '{typeCapture.unresolvedAccessChain}' could not be found (are you missing a using directive?)"); } variableType = typeCapture.captureType; } } List <SymbolDefinition> newSymbols = new List <SymbolDefinition>(); foreach (VariableDeclaratorSyntax variableDeclarator in node.Variables) { SymbolDefinition newSymbol = null; string variableName = variableDeclarator.Identifier.ValueText; using (ExpressionCaptureScope symbolCreationScope = new ExpressionCaptureScope(visitorContext, null)) { if (!isVar) { newSymbol = visitorContext.topTable.CreateNamedSymbol(variableDeclarator.Identifier.ValueText, variableType, symbolType); } // Run the initializer if it exists // Todo: Run the set on the new symbol scope from within the initializer scope for direct setting if (variableDeclarator.Initializer != null && symbolType.HasFlag(SymbolDeclTypeFlags.Local)) { using (ExpressionCaptureScope initializerCapture = new ExpressionCaptureScope(visitorContext, null, newSymbol)) { Visit(variableDeclarator.Initializer); if (newSymbol == null) { // TODO: Find a way to determine the return type before generating initializer code, to avoid a copy on 'var' local initializers variableType = initializerCapture.GetReturnType(true); newSymbol = visitorContext.topTable.CreateNamedSymbol(variableDeclarator.Identifier.ValueText, variableType, symbolType); } symbolCreationScope.SetToLocalSymbol(newSymbol); symbolCreationScope.ExecuteSet(initializerCapture.ExecuteGet()); } } newSymbol.syncMode = syncMode; } VerifySyncValidForType(newSymbol.symbolCsType, syncMode); newSymbols.Add(newSymbol); } if (!visitorContext.resolverContext.IsValidUdonType(variableType)) { throw new System.NotSupportedException($"Udon does not support variables of type '{variableType.Name}' yet"); } return(newSymbols); }
private List <System.Attribute> GetFieldAttributes(FieldDeclarationSyntax node) { List <System.Attribute> attributes = new List <System.Attribute>(); if (node.AttributeLists != null) { foreach (AttributeListSyntax attributeList in node.AttributeLists) { UpdateSyntaxNode(attributeList); foreach (AttributeSyntax attribute in attributeList.Attributes) { using (ExpressionCaptureScope attributeTypeCapture = new ExpressionCaptureScope(visitorContext, null)) { attributeTypeCapture.isAttributeCaptureScope = true; Visit(attribute.Name); System.Type captureType = attributeTypeCapture.captureType; if (captureType == typeof(UdonSyncedAttribute)) { UdonSyncMode syncMode = UdonSyncMode.NotSynced; if (attribute.ArgumentList == null || attribute.ArgumentList.Arguments == null || attribute.ArgumentList.Arguments.Count == 0) { syncMode = UdonSyncMode.None; } else { using (ExpressionCaptureScope attributeCaptureScope = new ExpressionCaptureScope(visitorContext, null)) { Visit(attribute.ArgumentList.Arguments[0].Expression); if (!attributeCaptureScope.IsEnum()) { throw new System.Exception("Invalid attribute argument provided for sync"); } syncMode = (UdonSyncMode)attributeCaptureScope.GetEnumValue(); } } attributes.Add(new UdonSyncedAttribute(syncMode)); } else if (captureType != null) { try { object attributeObject = null; if (attribute.ArgumentList == null || attribute.ArgumentList.Arguments == null || attribute.ArgumentList.Arguments.Count == 0) { attributeObject = System.Activator.CreateInstance(captureType); } else { // todo: requires constant folding to support decently object[] attributeArgs = new object[attribute.ArgumentList.Arguments.Count]; for (int i = 0; i < attributeArgs.Length; ++i) { AttributeArgumentSyntax attributeArg = attribute.ArgumentList.Arguments[i]; using (ExpressionCaptureScope attributeCapture = new ExpressionCaptureScope(visitorContext, null)) { Visit(attributeArg); SymbolDefinition attrSymbol = attributeCapture.ExecuteGet(); if (!attrSymbol.declarationType.HasFlag(SymbolDeclTypeFlags.Constant)) { throw new System.ArgumentException("Attributes do not support non-constant expressions"); } attributeArgs[i] = attrSymbol.symbolDefaultValue; } } attributeObject = System.Activator.CreateInstance(captureType, attributeArgs); } if (attributeObject != null) { attributes.Add((System.Attribute)attributeObject); } } catch (System.Reflection.TargetInvocationException constructionException) { throw constructionException.InnerException; } } } } } } return(attributes); }
public void AddSyncTag(Value syncValue, UdonSyncMode syncMode) { AddInstruction(new SyncTag(syncValue, syncMode)); }
public SyncTag(Value syncedValue, UdonSyncMode syncMode) { SyncedValue = syncedValue; SyncMode = syncMode; }
public override void VisitFieldDeclaration(FieldDeclarationSyntax node) { UpdateSyntaxNode(node); var variables = node.Declaration.Variables; for (int i = 0; i < variables.Count; ++i) { VariableDeclaratorSyntax variable = variables[i]; if (variable.Initializer != null) { fieldsWithInitializers.Add(node); } } if (node.Modifiers.HasModifier("static")) { throw new System.NotSupportedException("Static fields are not yet supported by UdonSharp"); } UdonSyncMode fieldSyncMode = GetSyncAttributeValue(node); List <System.Attribute> fieldAttributes = GetFieldAttributes(node); bool isPublic = (node.Modifiers.Any(SyntaxKind.PublicKeyword) || fieldAttributes.Find(e => e is SerializeField) != null) && fieldAttributes.Find(e => e is System.NonSerializedAttribute) == null; bool isConst = (node.Modifiers.Any(SyntaxKind.ConstKeyword) || node.Modifiers.Any(SyntaxKind.ReadOnlyKeyword)); SymbolDeclTypeFlags flags = (isPublic ? SymbolDeclTypeFlags.Public : SymbolDeclTypeFlags.Private) | (isConst ? SymbolDeclTypeFlags.Readonly : 0); FieldChangeCallbackAttribute varChange = fieldAttributes.OfType <FieldChangeCallbackAttribute>().FirstOrDefault(); List <SymbolDefinition> fieldSymbols = HandleVariableDeclaration(node.Declaration, flags, fieldSyncMode); foreach (SymbolDefinition fieldSymbol in fieldSymbols) { FieldDefinition fieldDefinition = new FieldDefinition(fieldSymbol); fieldDefinition.fieldAttributes = fieldAttributes; if (fieldSymbol.IsUserDefinedType()) { System.Type fieldType = fieldSymbol.userCsType; while (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } foreach (ClassDefinition classDefinition in visitorContext.externClassDefinitions) { if (classDefinition.userClassType == fieldType) { fieldDefinition.userBehaviourSource = classDefinition.classScript; break; } } } visitorContext.localFieldDefinitions.Add(fieldSymbol.symbolUniqueName, fieldDefinition); if (varChange != null) { string targetProperty = varChange.CallbackPropertyName; if (variables.Count > 1 || visitorContext.onModifyCallbackFields.ContainsKey(targetProperty)) { throw new System.Exception($"Only one field may target property '{targetProperty}'"); } PropertyDefinition foundProperty = visitorContext.definedProperties.FirstOrDefault(e => e.originalPropertyName == targetProperty); if (foundProperty == null) { throw new System.ArgumentException($"Invalid target property for {nameof(FieldChangeCallbackAttribute)} on {node.Declaration}"); } PropertyDefinition property = visitorContext.definedProperties.FirstOrDefault(e => e.originalPropertyName == targetProperty); if (property == null) { throw new System.ArgumentException($"Property not found for '{targetProperty}'"); } if (property.type != fieldDefinition.fieldSymbol.userCsType) { throw new System.Exception($"Types must match between property and variable change field"); } visitorContext.onModifyCallbackFields.Add(targetProperty, fieldDefinition); } } }