Example #1
0
        private static bool ValidateProgramAssetCollisions(UdonSharpProgramAsset[] allProgramAssets)
        {
            Dictionary<MonoScript, List<UdonSharpProgramAsset>> scriptToAssetMap = new Dictionary<MonoScript, List<UdonSharpProgramAsset>>();

            foreach (UdonSharpProgramAsset programAsset in allProgramAssets)
            {
                if (programAsset == null || programAsset.sourceCsScript == null)
                    continue;

                // Add program asset to map to check if there are any duplicate program assets that point to the same script
                if (!scriptToAssetMap.TryGetValue(programAsset.sourceCsScript, out var programAssetList))
                {
                    programAssetList = new List<UdonSharpProgramAsset>();
                    scriptToAssetMap.Add(programAsset.sourceCsScript, programAssetList);
                }

                programAssetList.Add(programAsset);
            }

            int errorCount = 0;
            
            foreach (var scriptAssetMapping in scriptToAssetMap)
            {
                if (scriptAssetMapping.Value.Count > 1)
                {
                    UdonSharpUtils.LogError($"Script {Path.GetFileName(AssetDatabase.GetAssetPath(scriptAssetMapping.Key))} is referenced by {scriptAssetMapping.Value.Count} UdonSharpProgramAssets, scripts can only be referenced by 1 program asset.\n" +
                                            "Referenced program assets:\n" +
                                            string.Join(",\n", scriptAssetMapping.Value.Select(AssetDatabase.GetAssetPath)));

                    errorCount++;
                }
            }

            return errorCount == 0;
        }
        public void AddExternCall(string externCall, string comment = "")
        {
            externStringSet.Add(externCall);

            AppendCommentedLine($"EXTERN, \"{externCall}\"", comment);
            programCounter += UdonSharpUtils.GetUdonInstructionSize("EXTERN");
        }
        public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
        {
            if (requestedBuildType == VRCSDKRequestedBuildType.Avatar)
            {
                return(true);
            }

            if (UdonSharpSettings.GetSettings().disableUploadCompile)
            {
                return(true);
            }

            UdonSharpCompilerV1.CompileSync(new UdonSharpCompileOptions()
            {
                IsEditorBuild = false
            });
            UdonSharpEditorCache.SaveAllCache();

            if (UdonSharpProgramAsset.AnyUdonSharpScriptHasError())
            {
                UdonSharpUtils.LogError("Failed to compile UdonSharp scripts for build, check error log for details.");
                UdonSharpUtils.ShowEditorNotification("Failed to compile UdonSharp scripts for build, check error log for details.");
                return(false);
            }

            if (UdonSharpEditorManager.RunAllUpgrades())
            {
                UdonSharpUtils.LogWarning(UdonSharpEditorManager.UPGRADE_MESSAGE);
                return(false);
            }

            return(true);
        }
        public override Value EmitValue(EmitContext context)
        {
            Type operandType = ValueType.UdonType.SystemType;

            object allBits;

            if (UdonSharpUtils.IsSignedType(operandType))
            {
                allBits = Convert.ChangeType(-1, operandType);
            }
            else
            {
                allBits = operandType.GetField("MaxValue").GetValue(null);
            }

            BoundAccessExpression allBitsExpr = BoundAccessExpression.BindAccess(context.GetConstantValue(ValueType, allBits));

            Value returnValue = context.GetReturnValue(ValueType);

            using (context.InterruptAssignmentScope())
            {
                BoundAccessExpression operandVal = BoundAccessExpression.BindAccess(context.EmitValue(SourceExpression));

                context.EmitValueAssignment(returnValue,
                                            CreateBoundInvocation(context, null,
                                                                  new ExternSynthesizedOperatorSymbol(BuiltinOperatorType.LogicalXor, ValueType, context), null,
                                                                  new BoundExpression[] { operandVal, allBitsExpr }));
            }

            return(returnValue);
        }
Example #5
0
        private static void CleanupCompile()
        {
            UdonSharpUtils.ClearAsyncProgressBar();
            
            EditorApplication.UnlockReloadAssemblies();

            CurrentJob = null;
        }
        public void Compile()
        {
            Profiler.BeginSample("UdonSharp Compile");

            System.Diagnostics.Stopwatch compileTimer = new System.Diagnostics.Stopwatch();
            compileTimer.Start();

            int totalErrorCount = 0;

            try
            {
                EditorUtility.DisplayProgressBar("UdonSharp Compile", "Parsing Syntax Trees...", 0f);

                UdonSharpProgramAsset[] allPrograms = UdonSharpProgramAsset.GetAllUdonSharpPrograms();
                List <(UdonSharpProgramAsset, string)> programAssetsAndPaths = new List <(UdonSharpProgramAsset, string)>();

                foreach (UdonSharpProgramAsset programAsset in allPrograms)
                {
                    if (programAsset == null || programAsset.sourceCsScript == null)
                    {
                        continue;
                    }

                    programAssetsAndPaths.Add((programAsset, AssetDatabase.GetAssetPath(programAsset.sourceCsScript)));
                }

                UdonSharpProgramAsset[] programAssetsToCompile = modules.Select(e => e.programAsset).Where(e => e != null && e.sourceCsScript != null).ToArray();

                try
                {
                    beforeCompile?.Invoke(programAssetsToCompile);
                }
                catch (System.Exception e)
                {
                    Debug.LogError($"Exception thrown by pre compile listener\n{e}");
                }

                object syntaxTreeLock = new object();
                List <(UdonSharpProgramAsset, Microsoft.CodeAnalysis.SyntaxTree)> programsAndSyntaxTrees = new List <(UdonSharpProgramAsset, Microsoft.CodeAnalysis.SyntaxTree)>();
                Dictionary <UdonSharpProgramAsset, (string, Microsoft.CodeAnalysis.SyntaxTree)> syntaxTreeSourceLookup = new Dictionary <UdonSharpProgramAsset, (string, Microsoft.CodeAnalysis.SyntaxTree)>();

                string[] defines = UdonSharpUtils.GetProjectDefines(isEditorBuild);

                Parallel.ForEach(programAssetsAndPaths, (currentProgram) =>
                {
                    string programSource = UdonSharpUtils.ReadFileTextSync(currentProgram.Item2);

                    Microsoft.CodeAnalysis.SyntaxTree programSyntaxTree = CSharpSyntaxTree.ParseText(programSource, CSharpParseOptions.Default.WithDocumentationMode(DocumentationMode.None).WithPreprocessorSymbols(defines));

                    lock (syntaxTreeLock)
                    {
                        programsAndSyntaxTrees.Add((currentProgram.Item1, programSyntaxTree));
                        syntaxTreeSourceLookup.Add(currentProgram.Item1, (programSource, programSyntaxTree));
                    }
                });
Example #7
0
        public TypeSymbol GetUdonTypeSymbol(ITypeSymbol type, AbstractPhaseContext context)
        {
            if (!TypeSymbol.TryGetSystemType(type, out var systemType))
            {
                throw new InvalidOperationException("foundType should not be null");
            }

            systemType = UdonSharpUtils.UserTypeToUdonType(systemType);

            return(GetTypeSymbol(systemType, context));
        }
Example #8
0
        public ExternTypeSymbol(INamedTypeSymbol sourceSymbol, AbstractPhaseContext context)
            : base(sourceSymbol, context)
        {
            TryGetSystemType(sourceSymbol, out Type systemType);
            SystemType = systemType;

            Type udonType = UdonSharpUtils.UserTypeToUdonType(SystemType);

            UdonType = (ExternTypeSymbol)(udonType == SystemType ? this : context.GetUdonTypeSymbol(sourceSymbol));

            ExternSignature = CompilerUdonInterface.GetUdonTypeName(this);
        }
            public UdonHeapValueStorage(IUdonHeap heap, IUdonSymbolTable symbolTable, string symbolKey)
            {
                this.heap = heap;

                bool isValid = symbolTable.TryGetAddressFromSymbol(UdonSharpUtils.UnmanglePropertyFieldName(symbolKey), out symbolAddress) &&
                               heap.GetHeapVariableType(symbolAddress) == typeof(T) &&
                               heap.TryGetHeapVariable <T>(symbolAddress, out _);

                if (!isValid)
                {
                    symbolAddress = 0xFFFFFFFF;
                }
            }
        public override void WriteWeak(IValueStorage targetObject, object sourceObject)
        {
            VerifySerializationSanity();

            if (sourceObject != null && !UdonSharpUtils.IsUserJaggedArray(sourceObject.GetType()))
            {
                throw new SerializationException($"Cannot convert {targetObject.GetType()} to {typeMetadata.cSharpType}");
            }

            object tarArray = targetObject.Value;

            ConvertToUdonArrayElement(ref tarArray, sourceObject, typeMetadata.cSharpType);
            targetObject.Value = tarArray;
        }
        public void SetToType(System.Type type)
        {
            cSharpType = type;
            if (cSharpType != null && cSharpType.IsArray)
            {
                System.Type elementType = cSharpType;
                while (elementType.IsArray)
                {
                    elementType = elementType.GetElementType();
                }

                arrayElementMetadata = new TypeSerializationMetadata(elementType);
            }

            udonStorageType = UdonSharpUtils.UserTypeToUdonType(cSharpType);
        }
        public override void Read(ref T targetObject, IValueStorage sourceObject)
        {
            VerifySerializationSanity();

            if (sourceObject == null)
            {
                UdonSharpUtils.LogError($"Field for {typeof(T)} does not exist");
                return;
            }

            if (UsbSerializationContext.CollectDependencies)
            {
                if (sourceObject.Value is UnityEngine.Object unityObject && unityObject != null)
                {
                    UsbSerializationContext.Dependencies.Add(unityObject);
                }

                return;
            }

            IValueStorage storage = sourceObject as ValueStorage <T>;

            if (storage == null)
            {
                Type storageType = sourceObject.GetType().GetGenericArguments()[0];

                if (typeof(T).IsSubclassOf(storageType))
                {
                    storage = sourceObject;
                }
                else if (targetObject != null && targetObject.GetType().IsAssignableFrom(storageType))
                {
                    storage = sourceObject;
                }
                else if (targetObject == null && storageType.IsSubclassOf(typeof(T)))
                {
                    storage = sourceObject;
                }
                else
                {
                    UdonSharpUtils.LogError($"Type {typeof(T)} not compatible with serializer {sourceObject}");
                    return;
                }
            }

            targetObject = (T)storage.Value;
        }
Example #13
0
        internal static void SetSceneBehaviourUpgraded(UdonBehaviour behaviour)
        {
            if (!PrefabUtility.IsPartOfPrefabInstance(behaviour) && !PrefabUtility.IsPartOfPrefabAsset(behaviour))
            {
                return;
            }

            if (!behaviour.publicVariables.TrySetVariableValue <bool>(UDONSHARP_SCENE_BEHAVIOUR_UPGRADE_MARKER, true))
            {
                behaviour.publicVariables.RemoveVariable(UDONSHARP_SCENE_BEHAVIOUR_UPGRADE_MARKER);

                IUdonVariable newVar = new UdonVariable <bool>(UDONSHARP_SCENE_BEHAVIOUR_UPGRADE_MARKER, true);
                behaviour.publicVariables.TryAddVariable(newVar);
            }

            UdonSharpUtils.SetDirty(behaviour);
        }
        public void AddJumpIfFalse(JumpLabel jumpTarget, string comment = "")
        {
#if USE_UDON_LABELS
            AppendCommentedLine($"JUMP_IF_FALSE, {jumpTarget.uniqueName}", comment);
#else
            if (jumpTarget.IsResolved)
            {
                AppendCommentedLine($"JUMP_IF_FALSE, {jumpTarget.AddresStr()}", comment);
            }
            else
            {
                AppendCommentedLine($"JUMP_IF_FALSE_LABEL, [{jumpTarget.uniqueName}]", comment);
            }
#endif

            programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_IF_FALSE");
        }
        private void ConvertToCSharpArrayElement(ref object targetElement, object elementValue, Type cSharpType)
        {
            if (elementValue == null)
            {
                targetElement = null;
                return;
            }

            if (UdonSharpUtils.IsUserJaggedArray(cSharpType))
            {
                Array targetArray = (Array)targetElement;
                Array sourceArray = (Array)elementValue;

                if (!UsbSerializationContext.CollectDependencies)
                {
                    if (targetArray == null || targetArray.Length != sourceArray.Length)
                    {
                        targetElement = targetArray = (Array)Activator.CreateInstance(cSharpType, sourceArray.Length);
                    }
                }

                for (int i = 0; i < sourceArray.Length; ++i)
                {
                    object elementVal = targetArray.GetValue(i);
                    ConvertToCSharpArrayElement(ref elementVal, sourceArray.GetValue(i), cSharpType.GetElementType());

                    if (!UsbSerializationContext.CollectDependencies)
                    {
                        targetArray.SetValue(elementVal, i);
                    }
                }
            }
            else if (cSharpType.IsArray)
            {
                IValueStorage innerArrayValueStorage = GetInnerValueStorage();
                innerArrayValueStorage.Value = elementValue;
                rootArraySerializer.ReadWeak(ref targetElement, innerArrayValueStorage);

                innerValueStorages.Push(innerArrayValueStorage);
            }
            else
            {
                throw new Exception("Jagged array serializer requires a root array serializer");
            }
        }
Example #16
0
        private static bool TryImplicitConstantConversion(ref BoundExpression boundExpression, TypeSymbol targetType)
        {
            if (!boundExpression.IsConstant)
            {
                return(false);
            }
            if (boundExpression.ValueType == targetType)
            {
                return(false);
            }

            var targetSystemType = targetType.UdonType.SystemType;

            object constantValue = boundExpression.ConstantValue.Value;

            // if (targetSystemType == typeof(string))
            // {
            //     IConstantValue constant = new ConstantValue<string>(constantValue?.ToString() ?? "");
            //
            //     boundExpression = new BoundConstantExpression(constant, targetType, boundExpression.SyntaxNode);
            // }

            var sourceSystemType = boundExpression.ValueType.UdonType.SystemType;

            if (ConstantExpressionOptimizer.CanDoConstantConversion(sourceSystemType) &&
                ConstantExpressionOptimizer.CanDoConstantConversion(targetSystemType))
            {
                IConstantValue constant = (IConstantValue)Activator.CreateInstance(typeof(ConstantValue <>).MakeGenericType(targetSystemType), ConstantExpressionOptimizer.FoldConstantConversion(targetSystemType, constantValue));
                boundExpression = new BoundConstantExpression(constant, targetType, boundExpression.SyntaxNode);

                return(true);
            }

            if (boundExpression.ValueType.IsEnum &&
                boundExpression.ValueType.IsExtern &&
                UdonSharpUtils.IsIntegerType(targetSystemType))
            {
                boundExpression = new BoundConstantExpression(ConstantExpressionOptimizer.FoldConstantConversion(targetSystemType, constantValue), targetType);

                return(true);
            }

            return(false);
        }
        static void OnChangePlayMode(PlayModeStateChange state)
        {
            // Prevent people from entering play mode when there are compile errors, like normal Unity C#
            // READ ME
            // --------
            // If you think you know better and are about to edit this out, be aware that you gain nothing by doing so.
            // If a script hits a compile error, it will not update until the compile errors are resolved.
            // You will just be left wondering "why aren't my scripts changing when I edit them?" since the old copy of the script will be used until the compile errors are resolved.
            // --------
            if (state == PlayModeStateChange.EnteredPlayMode || state == PlayModeStateChange.ExitingEditMode)
            {
                if (UdonSharpProgramAsset.AnyUdonSharpScriptHasError())
                {
                    EditorApplication.isPlaying = false;

                    UdonSharpUtils.ShowEditorNotification("All U# compile errors have to be fixed before you can enter playmode!");
                }
                else if (state == PlayModeStateChange.EnteredPlayMode)
                {
                    CreateProxyBehaviours(GetAllUdonBehaviours());
                }
            }

            if (state == PlayModeStateChange.EnteredEditMode)
            {
                UdonSharpEditorCache.ResetInstance();
                if (UdonSharpEditorCache.Instance.LastBuildType == UdonSharpEditorCache.DebugInfoType.Client)
                {
                    UdonSharpProgramAsset.CompileAllCsPrograms(true);
                }

                RunAllUpdates();
            }
            else if (state == PlayModeStateChange.ExitingEditMode)
            {
                if (UdonSharpEditorCache.Instance.LastBuildType == UdonSharpEditorCache.DebugInfoType.Client)
                {
                    UdonSharpProgramAsset.CompileAllCsPrograms(true);
                }
            }

            UdonSharpEditorCache.SaveOnPlayExit(state);
        }
        private string BuildExternSignature(TypeSymbol containingType, string methodName)
        {
            Type methodSourceType = containingType.UdonType.SystemType;

            methodSourceType = UdonSharpUtils.RemapBaseType(methodSourceType);

            string functionNamespace = CompilerUdonInterface.SanitizeTypeName(methodSourceType.FullName ?? methodSourceType.Namespace + methodSourceType.Name).Replace("VRCUdonUdonBehaviour", "VRCUdonCommonInterfacesIUdonEventReceiver");

            methodName = $"__{methodName}";
            var parameters = Parameters;

            string paramStr = "";

            if (parameters.Length > 0)
            {
                paramStr = "_"; // Arg separator

                foreach (ParameterSymbol parameter in parameters)
                {
                    paramStr += $"_{CompilerUdonInterface.GetUdonTypeName(parameter.Type)}";
                }
            }
            else if (IsConstructor)
            {
                paramStr = "__";
            }

            string returnStr;

            if (!IsConstructor)
            {
                returnStr = ReturnType != null ? $"__{CompilerUdonInterface.GetUdonTypeName(ReturnType)}" : "__SystemVoid";
            }
            else
            {
                returnStr = $"__{CompilerUdonInterface.GetUdonTypeName(containingType)}";
            }

            string finalFunctionSig = $"{functionNamespace}.{methodName}{paramStr}{returnStr}";

            return(finalFunctionSig);
        }
Example #19
0
        public ModuleBinding[] LoadSyntaxTreesAndCreateModules(IEnumerable <string> sourcePaths, string[] scriptingDefines)
        {
            ConcurrentBag <ModuleBinding> syntaxTrees = new ConcurrentBag <ModuleBinding>();

            Parallel.ForEach(sourcePaths, (currentSource) =>
            {
                string programSource = UdonSharpUtils.ReadFileTextSync(currentSource);

                var programSyntaxTree = CSharpSyntaxTree.ParseText(programSource, CSharpParseOptions.Default.WithDocumentationMode(DocumentationMode.None).WithPreprocessorSymbols(scriptingDefines).WithLanguageVersion(LanguageVersion.CSharp7_3));

                syntaxTrees.Add(new ModuleBinding()
                {
                    tree = programSyntaxTree, filePath = currentSource, sourceText = programSource
                });
            });

            ModuleBindings = syntaxTrees.ToArray();

            return(ModuleBindings);
        }
Example #20
0
        private string GetSignature(AbstractPhaseContext context)
        {
            System.Type methodSourceType = ContainingType.UdonType.SystemType;

            methodSourceType = UdonSharpUtils.RemapBaseType(methodSourceType);

            if (methodSourceType == typeof(string) &&
                (Parameters[0].Type.UdonType.SystemType == typeof(object) ||
                 Parameters[1].Type.UdonType.SystemType == typeof(object)))
            {
                return("SystemString.__Concat__SystemObject_SystemObject__SystemString");
            }

            string functionNamespace = CompilerUdonInterface.SanitizeTypeName(methodSourceType.FullName ??
                                                                              methodSourceType.Namespace + methodSourceType.Name);

            string methodName = $"__{GetOperatorUdonName().Trim('_').TrimStart('.')}";
            var    parameters = RoslynSymbol.Parameters;

            string paramStr = "_";                                                                                  // Arg separator

            if (parameters.Length > 1 || methodName.Contains("UnaryMinus") || methodName.Contains("UnaryNegation")) // Binary operators
            {
                foreach (IParameterSymbol parameter in parameters)
                {
                    paramStr +=
                        $"_{CompilerUdonInterface.GetUdonTypeName(context.GetTypeSymbol(parameter.Type))}";
                }
            }
            else // Unary operators, we just use the regular binary operator internally and handle it in the bound operator
            {
                paramStr += $"_{CompilerUdonInterface.GetUdonTypeName(context.GetTypeSymbol(parameters[0].Type))}";
                paramStr += $"_{CompilerUdonInterface.GetUdonTypeName(context.GetTypeSymbol(parameters[0].Type))}";
            }

            string returnStr =
                $"__{CompilerUdonInterface.GetUdonTypeName(ReturnType)}";

            return($"{functionNamespace}.{methodName}{paramStr}{returnStr}");
        }
        void ConvertToUdonArrayElement(ref object targetElement, object elementValue, System.Type cSharpType)
        {
            if (elementValue == null)
            {
                targetElement = null;
                return;
            }

            if (UdonSharpUtils.IsUserJaggedArray(cSharpType))
            {
                Array targetArray = (Array)targetElement;
                Array sourceArray = (Array)elementValue;

                if (targetArray == null || targetArray.Length != sourceArray.Length)
                {
                    targetElement = targetArray = (Array)Activator.CreateInstance(UdonSharpUtils.UserTypeToUdonType(cSharpType), new object[] { sourceArray.Length });
                }

                for (int i = 0; i < sourceArray.Length; ++i)
                {
                    object elementVal = targetArray.GetValue(i);
                    ConvertToUdonArrayElement(ref elementVal, sourceArray.GetValue(i), cSharpType.GetElementType());
                    targetArray.SetValue(elementVal, i);
                }
            }
            else if (cSharpType.IsArray)
            {
                IValueStorage innerArrayValueStorage = GetInnerValueStorage();

                innerArrayValueStorage.Value = targetElement;
                rootArraySerializer.WriteWeak(innerArrayValueStorage, elementValue);
                targetElement = innerArrayValueStorage.Value;

                innerValueStorages.Push(innerArrayValueStorage);
            }
            else
            {
                throw new Exception("Jagged array serializer requires a root array serializer");
            }
        }
Example #22
0
        private List <System.Type> GetTypeArgumentList(TypeArgumentListSyntax typeArgumentList)
        {
            UpdateSyntaxNode(typeArgumentList);

            List <System.Type> argumentTypes = new List <System.Type>();

            foreach (TypeSyntax typeSyntax in typeArgumentList.Arguments)
            {
                using (ExpressionCaptureScope typeCaptureScope = new ExpressionCaptureScope(visitorContext, null))
                {
                    Visit(typeSyntax);

                    if (!typeCaptureScope.IsType())
                    {
                        throw new System.ArgumentException("Generic argument must be a valid type");
                    }

                    argumentTypes.Add(UdonSharpUtils.RemapBaseType(typeCaptureScope.captureType));
                }
            }

            return(argumentTypes);
        }
Example #23
0
        private static bool ValidateUdonSharpBehaviours(UdonSharpProgramAsset[] allProgramAssets, HashSet<string> allSourcePaths)
        {
            bool succeeded = true;
            
            foreach (var programAsset in allProgramAssets)
            {
                if (programAsset.sourceCsScript == null)
                    continue;

                string sourcePath = AssetDatabase.GetAssetPath(programAsset.sourceCsScript);
                
                if (string.IsNullOrEmpty(sourcePath))
                    continue;

                if (!allSourcePaths.Contains(sourcePath))
                {
                    succeeded = false;
                    UdonSharpUtils.LogError($"Script '{sourcePath}' does not belong to a U# assembly, have you made a U# assembly definition for the assembly the script is a part of?", programAsset.sourceCsScript);
                }
            }

            return succeeded;
        }
Example #24
0
        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
        }
Example #25
0
        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");
            }
        }
        static void InjectUnityEventInterceptors()
        {
            List <System.Type> udonSharpBehaviourTypes = new List <Type>();

            foreach (Assembly assembly in UdonSharpUtils.GetLoadedEditorAssemblies())
            {
                foreach (System.Type type in assembly.GetTypes())
                {
                    if (type != typeof(UdonSharpBehaviour) && type.IsSubclassOf(typeof(UdonSharpBehaviour)))
                    {
                        udonSharpBehaviourTypes.Add(type);
                    }
                }
            }

            const string harmonyID = "UdonSharp.Editor.EventPatch";
            Harmony      harmony   = new Harmony(harmonyID);

            harmony.UnpatchAll(harmonyID);

            MethodInfo    injectedEvent  = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.EventInterceptor), BindingFlags.Static | BindingFlags.Public);
            HarmonyMethod injectedMethod = new HarmonyMethod(injectedEvent);

            void InjectEvent(System.Type behaviourType, string eventName)
            {
                const BindingFlags eventBindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;

                MethodInfo eventInfo = behaviourType.GetMethods(eventBindingFlags).FirstOrDefault(e => e.Name == eventName && e.ReturnType == typeof(void));

                try
                {
                    if (eventInfo != null)
                    {
                        harmony.Patch(eventInfo, injectedMethod);
                    }
                }
                catch (System.Exception)
                {
                    Debug.LogWarning($"Failed to patch event {eventInfo} on {behaviourType}");
                }
            }

            foreach (System.Type udonSharpBehaviourType in udonSharpBehaviourTypes)
            {
                // Trigger events
                InjectEvent(udonSharpBehaviourType, "OnTriggerEnter");
                InjectEvent(udonSharpBehaviourType, "OnTriggerExit");
                InjectEvent(udonSharpBehaviourType, "OnTriggerStay");
                InjectEvent(udonSharpBehaviourType, "OnTriggerEnter2D");
                InjectEvent(udonSharpBehaviourType, "OnTriggerExit2D");
                InjectEvent(udonSharpBehaviourType, "OnTriggerStay2D");

                // Collision events
                InjectEvent(udonSharpBehaviourType, "OnCollisionEnter");
                InjectEvent(udonSharpBehaviourType, "OnCollisionExit");
                InjectEvent(udonSharpBehaviourType, "OnCollisionStay");
                InjectEvent(udonSharpBehaviourType, "OnCollisionEnter2D");
                InjectEvent(udonSharpBehaviourType, "OnCollisionExit2D");
                InjectEvent(udonSharpBehaviourType, "OnCollisionStay2D");

                // Controller
                InjectEvent(udonSharpBehaviourType, "OnControllerColliderHit");

                // Animator events
                InjectEvent(udonSharpBehaviourType, "OnAnimatorIK");
                InjectEvent(udonSharpBehaviourType, "OnAnimatorMove");

                // Mouse events
                InjectEvent(udonSharpBehaviourType, "OnMouseDown");
                InjectEvent(udonSharpBehaviourType, "OnMouseDrag");
                InjectEvent(udonSharpBehaviourType, "OnMouseEnter");
                InjectEvent(udonSharpBehaviourType, "OnMouseExit");
                InjectEvent(udonSharpBehaviourType, "OnMouseOver");
                InjectEvent(udonSharpBehaviourType, "OnMouseUp");
                InjectEvent(udonSharpBehaviourType, "OnMouseUpAsButton");

                // Particle events
                InjectEvent(udonSharpBehaviourType, "OnParticleCollision");
                InjectEvent(udonSharpBehaviourType, "OnParticleSystemStopped");
                InjectEvent(udonSharpBehaviourType, "OnParticleTrigger");
                InjectEvent(udonSharpBehaviourType, "OnParticleUpdateJobScheduled");

                // Rendering events
                InjectEvent(udonSharpBehaviourType, "OnPostRender");
                InjectEvent(udonSharpBehaviourType, "OnPreCull");
                InjectEvent(udonSharpBehaviourType, "OnPreRender");
                InjectEvent(udonSharpBehaviourType, "OnRenderImage");
                InjectEvent(udonSharpBehaviourType, "OnRenderObject");
                InjectEvent(udonSharpBehaviourType, "OnWillRenderObject");

                // Joint events
                InjectEvent(udonSharpBehaviourType, "OnJointBreak");
                InjectEvent(udonSharpBehaviourType, "OnJointBreak2D");

                // Audio
                InjectEvent(udonSharpBehaviourType, "OnAudioFilterRead");

                // Transforms
                InjectEvent(udonSharpBehaviourType, "OnTransformChildrenChanged");
                InjectEvent(udonSharpBehaviourType, "OnTransformParentChanged");

                // Object state, OnDisable and OnDestroy will get called regardless of the enabled state of the component, include OnEnable for consistency
                InjectEvent(udonSharpBehaviourType, "OnEnable");
                InjectEvent(udonSharpBehaviourType, "OnDisable");
                InjectEvent(udonSharpBehaviourType, "OnDestroy");
            }

            // Add method for checking if events need to be skipped
            InjectedMethods.shouldSkipEventsMethod = (Func <bool>)Delegate.CreateDelegate(typeof(Func <bool>), typeof(UdonSharpBehaviour).GetMethod("ShouldSkipEvents", BindingFlags.Static | BindingFlags.NonPublic));

            // Patch GUI object field drawer
            MethodInfo doObjectFieldMethod = typeof(EditorGUI).GetMethods(BindingFlags.Static | BindingFlags.NonPublic).FirstOrDefault(e => e.Name == "DoObjectField" && e.GetParameters().Length == 9);

            HarmonyMethod objectFieldProxy = new HarmonyMethod(typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.DoObjectFieldProxy)));

            harmony.Patch(doObjectFieldMethod, objectFieldProxy);

            System.Type validatorDelegateType = typeof(EditorGUI).GetNestedType("ObjectFieldValidator", BindingFlags.Static | BindingFlags.NonPublic);
            InjectedMethods.validationDelegate = Delegate.CreateDelegate(validatorDelegateType, typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.ValidateObjectReference)));

            InjectedMethods.objectValidatorMethod = typeof(EditorGUI).GetMethod("ValidateObjectReferenceValue", BindingFlags.NonPublic | BindingFlags.Static);

            MethodInfo crossSceneRefCheckMethod = typeof(EditorGUI).GetMethod("CheckForCrossSceneReferencing", BindingFlags.NonPublic | BindingFlags.Static);

            InjectedMethods.crossSceneRefCheckMethod = (Func <UnityEngine.Object, UnityEngine.Object, bool>)Delegate.CreateDelegate(typeof(Func <UnityEngine.Object, UnityEngine.Object, bool>), crossSceneRefCheckMethod);

            // Patch post BuildAssetBundles fixup function
            MethodInfo buildAssetbundlesMethod = typeof(BuildPipeline).GetMethods(BindingFlags.NonPublic | BindingFlags.Static).First(e => e.Name == "BuildAssetBundles" && e.GetParameters().Length == 5);

            MethodInfo    postBuildMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.PostBuildAssetBundles), BindingFlags.Public | BindingFlags.Static);
            HarmonyMethod postBuildHarmonyMethod = new HarmonyMethod(postBuildMethod);

            MethodInfo    preBuildMethod        = typeof(InjectedMethods).GetMethod(nameof(InjectedMethods.PreBuildAssetBundles), BindingFlags.Public | BindingFlags.Static);
            HarmonyMethod preBuildHarmonyMethod = new HarmonyMethod(preBuildMethod);

            harmony.Patch(buildAssetbundlesMethod, preBuildHarmonyMethod, postBuildHarmonyMethod);
        }
 public void AddCopy(string comment = "")
 {
     AppendCommentedLine("COPY", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("COPY");
 }
 public void AddJumpIndirect(SymbolDefinition addressSymbol, string comment = "")
 {
     AppendCommentedLine($"JUMP_INDIRECT, {addressSymbol.symbolUniqueName}", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP_INDIRECT");
 }
 private void AddPush(string heapAddress, string comment)
 {
     AppendCommentedLine($"PUSH, {heapAddress}", comment);
     programCounter += UdonSharpUtils.GetUdonInstructionSize("PUSH");
 }
 public void AddJumpToExit()
 {
     AppendCommentedLine($"JUMP, 0xFFFFFFFF", "");
     programCounter += UdonSharpUtils.GetUdonInstructionSize("JUMP");
 }