Ejemplo n.º 1
0
        internal static void SetBehaviourVersion(UdonBehaviour behaviour, UdonSharpBehaviourVersion version)
        {
            UdonSharpBehaviourVersion lastVer = GetBehaviourVersion(behaviour);

            if (lastVer == version && lastVer != UdonSharpBehaviourVersion.V0)
            {
                return;
            }

            bool setVer = behaviour.publicVariables.TrySetVariableValue <int>(UDONSHARP_BEHAVIOUR_VERSION_KEY, (int)version);

            if (!setVer)
            {
                behaviour.publicVariables.RemoveVariable(UDONSHARP_BEHAVIOUR_VERSION_KEY);
                IUdonVariable newVar = new UdonVariable <int>(UDONSHARP_BEHAVIOUR_VERSION_KEY, (int)version);
                setVer = behaviour.publicVariables.TryAddVariable(newVar);
            }

            if (setVer)
            {
                UdonSharpUtils.SetDirty(behaviour);
                return;
            }

            UdonSharpUtils.LogError("Could not set version variable");
        }
Ejemplo n.º 2
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;
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        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;
        }
Ejemplo n.º 5
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;
        }
Ejemplo n.º 6
0
        private static void RunBehaviourSetup(UdonSharpBehaviour behaviour, bool withUndo)
        {
            UdonBehaviour backingBehaviour = GetBackingUdonBehaviour(behaviour);

            // Handle components pasted across different behaviours
            if (backingBehaviour && backingBehaviour.gameObject != behaviour.gameObject)
            {
                backingBehaviour = null;
            }

            // Handle pasting components on the same behaviour, assumes pasted components are always the last in the list.
            if (backingBehaviour)
            {
                int refCount = 0;
                UdonSharpBehaviour[] behaviours = backingBehaviour.GetComponents <UdonSharpBehaviour>();
                foreach (UdonSharpBehaviour udonSharpBehaviour in behaviours)
                {
                    if (GetBackingUdonBehaviour(udonSharpBehaviour) == backingBehaviour)
                    {
                        refCount++;
                    }
                }

                if (refCount > 1 && behaviour == behaviours.Last())
                {
                    backingBehaviour = null;
                }
            }

            bool isPartOfPrefabInstance = PrefabUtility.IsPartOfPrefabInstance(behaviour) &&
                                          PrefabUtility.GetCorrespondingObjectFromSource(behaviour) != behaviour;

            if (backingBehaviour == null)
            {
                if (isPartOfPrefabInstance)
                {
                    UdonSharpUtils.LogWarning("Cannot setup behaviour on prefab instance, original prefab asset needs setup");
                    return;
                }

                SetIgnoreEvents(true);

                try
                {
                    backingBehaviour = withUndo ? Undo.AddComponent <UdonBehaviour>(behaviour.gameObject) : behaviour.gameObject.AddComponent <UdonBehaviour>();

                #pragma warning disable CS0618 // Type or member is obsolete
                    backingBehaviour.SynchronizePosition             = false;
                    backingBehaviour.AllowCollisionOwnershipTransfer = false;
                #pragma warning restore CS0618 // Type or member is obsolete

                    MoveComponentRelativeToComponent(backingBehaviour, behaviour, false);

                    SetBackingUdonBehaviour(behaviour, backingBehaviour);

                    SetBehaviourVersion(backingBehaviour, UdonSharpBehaviourVersion.CurrentVersion);
                    SetSceneBehaviourUpgraded(backingBehaviour);

                    // UdonSharpUtils.Log($"Created behaviour {backingBehaviour}", behaviour);
                }
                finally
                {
                    SetIgnoreEvents(false);
                }

                _proxyBehaviourLookup.Add(backingBehaviour, behaviour);

                UdonSharpUtils.SetDirty(behaviour);
                UdonSharpUtils.SetDirty(backingBehaviour);
            }

            // Handle U# behaviours that have been added to a prefab via Added Component > Apply To Prefab, but have not had their backing behaviour added
            // if (isPartOfPrefabInstance &&
            //     backingBehaviour != null &&
            //     !PrefabUtility.IsPartOfPrefabInstance(backingBehaviour))
            // {
            //     PropertyModification[] modifications = PrefabUtility.GetPropertyModifications(behaviour);
            //
            //     if (modifications != null)
            //     {
            //
            //     }
            // }

            UdonSharpProgramAsset programAsset = GetUdonSharpProgramAsset(behaviour);

            if (backingBehaviour.programSource == null)
            {
                backingBehaviour.programSource = programAsset;
                if (backingBehaviour.programSource == null)
                {
                    UdonSharpUtils.LogError($"Unable to find valid U# program asset associated with script '{behaviour}'", behaviour);
                }

                UdonSharpUtils.SetDirty(backingBehaviour);
            }

            if (_serializedProgramAssetField.GetValue(backingBehaviour) == null)
            {
                SerializedObject   componentAsset = new SerializedObject(backingBehaviour);
                SerializedProperty serializedProgramAssetProperty = componentAsset.FindProperty("serializedProgramAsset");

                serializedProgramAssetProperty.objectReferenceValue = programAsset.SerializedProgramAsset;

                if (withUndo)
                {
                    componentAsset.ApplyModifiedProperties();
                }
                else
                {
                    componentAsset.ApplyModifiedPropertiesWithoutUndo();
                }
            }

            if (backingBehaviour.enabled != behaviour.enabled)
            {
                if (withUndo)
                {
                    Undo.RecordObject(backingBehaviour, "Enabled change");
                }

                backingBehaviour.enabled = behaviour.enabled;

                if (!withUndo)
                {
                    UdonSharpUtils.SetDirty(backingBehaviour);
                }
            }

        #if UDONSHARP_DEBUG
            backingBehaviour.hideFlags &= ~HideFlags.HideInInspector;
        #else
            backingBehaviour.hideFlags |= HideFlags.HideInInspector;
        #endif

            ((UdonSharpProgramAsset)backingBehaviour.programSource)?.UpdateProgram();
        }
Ejemplo n.º 7
0
        internal static void UpgradeSceneBehaviours(IEnumerable <UdonBehaviour> behaviours)
        {
            if (EditorApplication.isPlaying)
            {
                return;
            }

            // Create proxies if they do not exist
            foreach (UdonBehaviour udonBehaviour in behaviours)
            {
                if (!IsUdonSharpBehaviour(udonBehaviour))
                {
                    continue;
                }

                if (PrefabUtility.IsPartOfPrefabInstance(udonBehaviour) &&
                    PrefabUtility.IsAddedComponentOverride(udonBehaviour))
                {
                    continue;
                }

                if (GetProxyBehaviour(udonBehaviour) == null)
                {
                    if (PrefabUtility.IsPartOfPrefabInstance(udonBehaviour) &&
                        PrefabUtility.GetCorrespondingObjectFromSource(udonBehaviour) != udonBehaviour)
                    {
                        UdonSharpUtils.LogError($"Cannot upgrade scene behaviour '{udonBehaviour}' since its prefab must be upgraded.", udonBehaviour);
                        continue;
                    }

                    Type udonSharpBehaviourType = GetUdonSharpBehaviourType(udonBehaviour);

                    if (!udonSharpBehaviourType.IsSubclassOf(typeof(UdonSharpBehaviour)))
                    {
                        UdonSharpUtils.LogError($"Class script referenced by program asset '{udonBehaviour.programSource}' is not an UdonSharpBehaviour", udonBehaviour.programSource);
                        continue;
                    }

                    UdonSharpBehaviour newProxy = (UdonSharpBehaviour)udonBehaviour.gameObject.AddComponent(udonSharpBehaviourType);
                    newProxy.enabled = udonBehaviour.enabled;

                    SetBackingUdonBehaviour(newProxy, udonBehaviour);

                    if (!PrefabUtility.IsAddedComponentOverride(udonBehaviour))
                    {
                        MoveComponentRelativeToComponent(newProxy, udonBehaviour, true);
                    }
                    else
                    {
                        UdonSharpUtils.LogWarning($"Cannot reorder internal UdonBehaviour for '{udonBehaviour}' during upgrade because it is on a prefab instance.", udonBehaviour.gameObject);
                    }

                    UdonSharpUtils.SetDirty(newProxy);
                }

                if (GetBehaviourVersion(udonBehaviour) == UdonSharpBehaviourVersion.V0)
                {
                    SetBehaviourVersion(udonBehaviour, UdonSharpBehaviourVersion.V0DataUpgradeNeeded);
                }
            }

            // Copy data over from UdonBehaviour to UdonSharpBehaviour
            foreach (UdonBehaviour udonBehaviour in behaviours)
            {
                if (!IsUdonSharpBehaviour(udonBehaviour))
                {
                    continue;
                }

                bool needsPrefabInstanceUpgrade = false;

                // Checks if the version is below V1 or if it needs the prefab instance upgrade
                UdonSharpBehaviourVersion behaviourVersion = GetBehaviourVersion(udonBehaviour);
                if (behaviourVersion >= UdonSharpBehaviourVersion.V1)
                {
                    // Check if the prefab instance has a prefab that was upgraded causing the string data to be copied, but has a delta'd UnityEngine.Object storage array
                    if (PrefabUtility.IsPartOfPrefabInstance(udonBehaviour) &&
                        !HasSceneBehaviourUpgradeFlag(udonBehaviour))
                    {
                        UdonBehaviour prefabSource = PrefabUtility.GetCorrespondingObjectFromSource(udonBehaviour);

                        if (prefabSource && BehaviourRequiresBackwardsCompatibilityPersistence(prefabSource))
                        {
                            PropertyModification[] modifications =
                                PrefabUtility.GetPropertyModifications(udonBehaviour);

                            if (modifications != null &&
                                modifications.Any(e => e.propertyPath.StartsWith("publicVariablesUnityEngineObjects", StringComparison.Ordinal)))
                            {
                                needsPrefabInstanceUpgrade = true;
                            }
                        }
                    }

                    if (!needsPrefabInstanceUpgrade)
                    {
                        continue;
                    }
                }

                UdonSharpBehaviour proxy = GetProxyBehaviour(udonBehaviour);

                if (proxy == null)
                {
                    UdonSharpUtils.LogWarning($"UdonSharpBehaviour '{udonBehaviour}' could not be upgraded since it is missing a proxy", udonBehaviour);
                    continue;
                }

                CopyUdonToProxy(proxy, ProxySerializationPolicy.RootOnly);

                // Nuke out old data now because we want only the C# side to own the data from this point on

                ClearBehaviourVariables(udonBehaviour, true);

                SetBehaviourVersion(udonBehaviour, UdonSharpBehaviourVersion.V1);
                SetSceneBehaviourUpgraded(udonBehaviour);

                if (needsPrefabInstanceUpgrade)
                {
                    UdonSharpUtils.Log($"Scene behaviour '{udonBehaviour.name}' needed UnityEngine.Object upgrade pass", udonBehaviour);
                }

                UdonSharpUtils.SetDirty(proxy);

                UdonSharpUtils.Log($"Upgraded scene behaviour '{udonBehaviour.name}'", udonBehaviour);
            }
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Runs a two pass upgrade of a set of prefabs, assumes all dependencies of the prefabs are included, otherwise the process could fail to maintain references.
        /// First creates a new UdonSharpBehaviour proxy script and hooks it to a given UdonBehaviour. Then in a second pass goes over all behaviours and serializes their data into the C# proxy and wipes their old data out.
        /// </summary>
        /// <param name="prefabRootEnumerable"></param>
        internal static void UpgradePrefabs(IEnumerable <GameObject> prefabRootEnumerable)
        {
            if (UdonSharpProgramAsset.IsAnyProgramAssetSourceDirty() ||
                UdonSharpProgramAsset.IsAnyProgramAssetOutOfDate())
            {
                UdonSharpCompilerV1.CompileSync();
            }

            GameObject[] prefabRoots = prefabRootEnumerable.ToArray();

            bool NeedsNewProxy(UdonBehaviour udonBehaviour)
            {
                if (!IsUdonSharpBehaviour(udonBehaviour))
                {
                    return(false);
                }

                // The behaviour originates from a parent prefab so we don't want to modify this copy of the prefab
                if (PrefabUtility.GetCorrespondingObjectFromOriginalSource(udonBehaviour) != udonBehaviour)
                {
                    if (!PrefabUtility.IsPartOfPrefabInstance(udonBehaviour))
                    {
                        UdonSharpUtils.LogWarning($"Nested prefab with UdonSharpBehaviours detected during prefab upgrade: {udonBehaviour.gameObject}, nested prefabs are not eligible for automatic upgrade.");
                    }

                    return(false);
                }

                if (GetProxyBehaviour(udonBehaviour))
                {
                    return(false);
                }

                return(true);
            }

            bool NeedsSerializationUpgrade(UdonBehaviour udonBehaviour)
            {
                if (!IsUdonSharpBehaviour(udonBehaviour))
                {
                    return(false);
                }

                if (NeedsNewProxy(udonBehaviour))
                {
                    return(true);
                }

                if (GetBehaviourVersion(udonBehaviour) == UdonSharpBehaviourVersion.V0DataUpgradeNeeded)
                {
                    return(true);
                }

                return(false);
            }

            HashSet <GameObject> phase1FixupPrefabRoots = new HashSet <GameObject>();

            // Phase 1 Pruning - Add missing proxy behaviours
            foreach (GameObject prefabRoot in prefabRoots)
            {
                if (!prefabRoot.GetComponentsInChildren <UdonBehaviour>(true).Any(NeedsNewProxy))
                {
                    continue;
                }

                string prefabPath = AssetDatabase.GetAssetPath(prefabRoot);

                if (!prefabPath.IsNullOrWhitespace())
                {
                    phase1FixupPrefabRoots.Add(prefabRoot);
                }
            }

            HashSet <GameObject> phase2FixupPrefabRoots = new HashSet <GameObject>(phase1FixupPrefabRoots);

            // Phase 2 Pruning - Check for behaviours that require their data ownership to be transferred Udon -> C#
            foreach (GameObject prefabRoot in prefabRoots)
            {
                foreach (UdonBehaviour udonBehaviour in prefabRoot.GetComponentsInChildren <UdonBehaviour>(true))
                {
                    if (NeedsSerializationUpgrade(udonBehaviour))
                    {
                        string prefabPath = AssetDatabase.GetAssetPath(prefabRoot);

                        if (!prefabPath.IsNullOrWhitespace())
                        {
                            phase2FixupPrefabRoots.Add(prefabRoot);
                        }

                        break;
                    }
                }
            }

            // Now we have a set of prefabs that we can actually load and run the two upgrade phases on.
            // Todo: look at merging the two passes since we don't actually need to load prefabs into scenes apparently

            // Early out and avoid the edit scope
            if (phase1FixupPrefabRoots.Count == 0 && phase2FixupPrefabRoots.Count == 0)
            {
                return;
            }

            if (phase2FixupPrefabRoots.Count > 0)
            {
                UdonSharpUtils.Log($"Running upgrade process on {phase2FixupPrefabRoots.Count} prefabs: {string.Join(", ", phase2FixupPrefabRoots.Select(e => e.name))}");
            }

            using (new UdonSharpEditorManager.AssetEditScope())
            {
                foreach (GameObject prefabRoot in phase1FixupPrefabRoots)
                {
                    try
                    {
                        foreach (UdonBehaviour udonBehaviour in prefabRoot.GetComponentsInChildren <UdonBehaviour>(true))
                        {
                            if (!NeedsNewProxy(udonBehaviour))
                            {
                                continue;
                            }

                            UdonSharpBehaviour newProxy = (UdonSharpBehaviour)udonBehaviour.gameObject.AddComponent(GetUdonSharpBehaviourType(udonBehaviour));
                            newProxy.enabled = udonBehaviour.enabled;

                            SetBackingUdonBehaviour(newProxy, udonBehaviour);

                            MoveComponentRelativeToComponent(newProxy, udonBehaviour, true);

                            SetBehaviourVersion(udonBehaviour, UdonSharpBehaviourVersion.V0DataUpgradeNeeded);
                        }

                        // Phase2 is a superset of phase 1 upgrades, and AssetEditScope prevents flushing to disk anyways so just don't save here.
                        // PrefabUtility.SavePrefabAsset(prefabRoot);

                        // UdonSharpUtils.Log($"Ran prefab upgrade phase 1 on {prefabRoot}");
                    }
                    catch (Exception e)
                    {
                        UdonSharpUtils.LogError($"Encountered exception while upgrading prefab {prefabRoot}, report exception to Merlin: {e}");
                    }
                }

                foreach (GameObject prefabRoot in phase2FixupPrefabRoots)
                {
                    try
                    {
                        foreach (UdonBehaviour udonBehaviour in prefabRoot.GetComponentsInChildren <UdonBehaviour>(true))
                        {
                            if (!NeedsSerializationUpgrade(udonBehaviour))
                            {
                                continue;
                            }

                            CopyUdonToProxy(GetProxyBehaviour(udonBehaviour), ProxySerializationPolicy.RootOnly);

                            // We can't remove this data for backwards compatibility :'(
                            // If we nuke the data, the unity object array on the underlying storage may change.
                            // Which means that if people have copies of this prefab in the scene with no object reference changes, their data will also get nuked which we do not want.
                            // Public variable data on the prefabs will never be touched again by U# after upgrading
                            // We will probably provide an optional upgrade process that strips this extra data, and takes into account all scenes in the project

                            // foreach (string publicVarSymbol in udonBehaviour.publicVariables.VariableSymbols.ToArray())
                            //     udonBehaviour.publicVariables.RemoveVariable(publicVarSymbol);

                            SetBehaviourVersion(udonBehaviour, UdonSharpBehaviourVersion.V1);
                            SetBehaviourUpgraded(udonBehaviour);
                        }

                        PrefabUtility.SavePrefabAsset(prefabRoot);

                        // UdonSharpUtils.Log($"Ran prefab upgrade phase 2 on {prefabRoot}");
                    }
                    catch (Exception e)
                    {
                        UdonSharpUtils.LogError($"Encountered exception while upgrading prefab {prefabRoot}, report exception to Merlin: {e}");
                    }
                }

                UdonSharpUtils.Log("Prefab upgrade pass finished");
            }
        }
Ejemplo n.º 9
0
        private static void TickCompile()
        {
            if (CurrentJob == null) return;
            
            if (!CurrentJob.Task.IsCompleted)
            {
                CompilationContext.CompilePhase currentPhase = CurrentJob.Context.CurrentPhase;
                float phaseProgress = CurrentJob.Context.PhaseProgress;

                float totalProgress = (phaseProgress / (int) CompilationContext.CompilePhase.Count) +
                                      ((int) currentPhase / (float)(int)CompilationContext.CompilePhase.Count);
                
                UdonSharpUtils.ShowAsyncProgressBar($"U#: {currentPhase}", totalProgress);
                return;
            }

            if (!CurrentJob.CompileOptions.DisableLogging)
            {
                foreach (var diagnostic in CurrentJob.Context.Diagnostics)
                {
                    string filePath = "";
                    if (diagnostic.Location != null)
                        filePath = CurrentJob.Context.TranslateLocationToFileName(diagnostic.Location);
                    LinePosition? linePosition = diagnostic.Location?.GetLineSpan().StartLinePosition;

                    int line = (linePosition?.Line ?? 0) + 1;
                    int character = (linePosition?.Character ?? 0) + 1;

                    string fileStr = $"{filePath ?? "Unknown File"}({line},{character})";

                    string logStr = $"{fileStr}: {diagnostic.Message}";

                    switch (diagnostic.Severity)
                    {
                        case DiagnosticSeverity.Error:
                            UdonSharpUtils.LogBuildError(diagnostic.Message, filePath, line, character);
                            break;
                        case DiagnosticSeverity.Warning:
                            UdonSharpUtils.LogWarning(logStr);
                            break;
                        case DiagnosticSeverity.Log:
                            UdonSharpUtils.Log(logStr);
                            break;
                    }
                }
            }
            
            // Translate the diagnostic types and apply them to the cache, todo: consider merging the two structures
            UdonSharpEditorCache.CompileDiagnostic[] diagnostics = new UdonSharpEditorCache.CompileDiagnostic[CurrentJob.Context.Diagnostics.Count];
            CompilationContext.CompileDiagnostic[] compileDiagnostics = CurrentJob.Context.Diagnostics.ToArray();

            for (int i = 0; i < diagnostics.Length; ++i)
            {
                LinePosition diagLine = compileDiagnostics[i]?.Location?.GetLineSpan().StartLinePosition ?? LinePosition.Zero;
                
                diagnostics[i] = new UdonSharpEditorCache.CompileDiagnostic()
                {
                    severity = compileDiagnostics[i].Severity,
                    message = compileDiagnostics[i].Message,
                    file = CurrentJob.Context.TranslateLocationToFileName(compileDiagnostics[i].Location) ?? "",
                    line = diagLine.Line,
                    character = diagLine.Character,
                };
            }

            UdonSharpEditorCache.Instance.LastCompileDiagnostics = diagnostics;
            
            if (CurrentJob.Task.IsFaulted)
            {
                UdonSharpUtils.LogError("internal compiler error, dumping exceptions. Please report to Merlin");

                if (CurrentJob.Task.Exception != null)
                {
                    foreach (Exception innerException in CurrentJob.Task.Exception.InnerExceptions)
                    {
                        UdonSharpUtils.LogError(innerException);
                    }
                }

                CleanupCompile();
                return;
            }

            if (CurrentJob.Context.ErrorCount > 0)
            {
                CleanupCompile();
                return;
            }
                
            foreach (ModuleBinding rootBinding in CurrentJob.Context.ModuleBindings)
            {
                if (rootBinding.programAsset == null) 
                    continue;
                
                rootBinding.programAsset.ApplyProgram();
                
                UdonSharpEditorCache.Instance.SetUASMStr(rootBinding.programAsset, rootBinding.assembly);
                
                rootBinding.programAsset.CompiledVersion = UdonSharpProgramVersion.CurrentVersion;
                EditorUtility.SetDirty(rootBinding.programAsset);
            }
            
            try
            {
                UdonSharpEditorCache.Instance.RehashAllScripts();
                UdonSharpEditorManager.RunPostBuildSceneFixup();
            }
            catch (Exception e)
            {
                UdonSharpUtils.LogError($"Exception while running post build fixup:\n{e}");
                CleanupCompile();
                return;
            }

            UdonSharpEditorCache.Instance.LastBuildType = CurrentJob.CompileOptions.IsEditorBuild
                ? UdonSharpEditorCache.DebugInfoType.Editor
                : UdonSharpEditorCache.DebugInfoType.Client;
            
            int scriptCount = CurrentJob.Context.ModuleBindings.Count(e => e.programAsset != null);
            UdonSharpUtils.Log($"Compile of {scriptCount} script{(scriptCount != 1 ? "s" : "")} finished in {CurrentJob.CompileTimer.Elapsed:mm\\:ss\\.fff}");
            
            CleanupCompile();

            if (_compileQueued)
            {
                Compile(_queuedOptions);
                _compileQueued = false;
                _queuedOptions = null;
            }
        }
Ejemplo n.º 10
0
        public static void Compile(UdonSharpCompileOptions options = null)
        {
            if (options == null)
                options = new UdonSharpCompileOptions();

            if (UdonSharpUtils.DoesUnityProjectHaveCompileErrors())
            {
                UdonSharpUtils.LogError("All Unity C# compiler errors must be resolved before running an UdonSharp compile.");
                return;
            }
            
            if (CurrentJob != null)
            {
                _compileQueued = true;
                _queuedOptions = options;
                return;
            }
            
            Localization.Loc.InitLocalization();

            UdonSharpProgramAsset[] allPrograms = UdonSharpProgramAsset.GetAllUdonSharpPrograms();

            bool hasError = !ValidateProgramAssetCollisions(allPrograms);
            
            Dictionary<string, ProgramAssetInfo> rootProgramLookup = new Dictionary<string, ProgramAssetInfo>();
            foreach (UdonSharpProgramAsset udonSharpProgram in allPrograms)
            {
                if (udonSharpProgram.sourceCsScript == null)
                {
                    UdonSharpUtils.LogError($"Source C# script on {udonSharpProgram} is null", udonSharpProgram);
                    hasError = true;
                    continue;
                }

                string assetPath = AssetDatabase.GetAssetPath(udonSharpProgram.sourceCsScript);
                
                if (string.IsNullOrEmpty(assetPath))
                {
                    UdonSharpUtils.LogError($"Source C# script on {udonSharpProgram} is null", udonSharpProgram);
                    hasError = true;
                    continue;
                }
                
                rootProgramLookup.Add(assetPath.Replace('\\', '/'), new ProgramAssetInfo() { programAsset = udonSharpProgram ? udonSharpProgram : null, scriptClass = udonSharpProgram != null ? udonSharpProgram.GetClass() : null });
            }

            if (hasError)
            {
                UdonSharpEditorCache.Instance.LastCompileDiagnostics = new []
                    {
                        new UdonSharpEditorCache.CompileDiagnostic()
                        {
                            file = "",
                            message = "Compile validation failed, check console output for details.",
                            severity = DiagnosticSeverity.Error,
                        } 
                    };
                
                return;
            }
            
            // var allSourcePaths = new HashSet<string>(UdonSharpProgramAsset.GetAllUdonSharpPrograms().Where(e => e.isV1Root).Select(e => AssetDatabase.GetAssetPath(e.sourceCsScript).Replace('\\', '/')));
            HashSet<string> allSourcePaths = new HashSet<string>(CompilationContext.GetAllFilteredSourcePaths(options.IsEditorBuild));

            if (!ValidateUdonSharpBehaviours(allPrograms, allSourcePaths))
                return;

            CompilationContext compilationContext = new CompilationContext(options);
            string[] defines = UdonSharpUtils.GetProjectDefines(options.IsEditorBuild);

            EditorApplication.LockReloadAssemblies();

            Task compileTask = new Task(() => Compile(compilationContext, rootProgramLookup, allSourcePaths, defines));
            CurrentJob = new CompileJob() { Context = compilationContext, Task = compileTask, CompileTimer = Stopwatch.StartNew(), CompileOptions = options };
            
            compileTask.Start();
        }
Ejemplo n.º 11
0
        private static bool UpgradeScripts(UdonSharpProgramAsset[] programAssets)
        {
            if (programAssets.Length == 0)
            {
                return(false);
            }

            if (programAssets.All(e => e.ScriptVersion >= UdonSharpProgramVersion.CurrentVersion))
            {
                return(false);
            }

            CompilationContext compilationContext = new CompilationContext(new UdonSharpCompileOptions());

            ModuleBinding[] bindings = compilationContext.LoadSyntaxTreesAndCreateModules(CompilationContext.GetAllFilteredSourcePaths(false), UdonSharpUtils.GetProjectDefines(false));

            CSharpCompilation compilation = CSharpCompilation.Create(
                $"UdonSharpRoslynUpgradeAssembly{_assemblyCounter++}",
                bindings.Select(e => e.tree),
                CompilationContext.GetMetadataReferences(),
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            bool scriptUpgraded  = false;
            bool versionUpgraded = false;

            foreach (var programAsset in programAssets)
            {
                string        assetPath = AssetDatabase.GetAssetPath(programAsset.sourceCsScript);
                ModuleBinding binding   = bindings.FirstOrDefault(e => e.filePath == assetPath);

                if (binding == null)
                {
                    continue;
                }

                if (programAsset.ScriptVersion < UdonSharpProgramVersion.V1SerializationUpdate)
                {
                    SyntaxTree    bindingTree  = binding.tree;
                    SemanticModel bindingModel = compilation.GetSemanticModel(bindingTree);

                    SerializationUpdateSyntaxRewriter rewriter = new SerializationUpdateSyntaxRewriter(bindingModel);

                    SyntaxNode newRoot = rewriter.Visit(bindingTree.GetRoot());

                    if (rewriter.Modified)
                    {
                        try
                        {
                            File.WriteAllText(binding.filePath, newRoot.ToFullString(), Encoding.UTF8);
                            scriptUpgraded = true;

                            UdonSharpUtils.Log($"Upgraded field serialization attributes on U# script '{binding.filePath}'", programAsset.sourceCsScript);
                        }
                        catch (Exception e)
                        {
                            UdonSharpUtils.LogError($"Could not upgrade U# script, exception: {e}");
                        }
                    }
                    // We expect this to come through a second time after scripts have been updated and change the version on the asset.
                    else
                    {
                        programAsset.ScriptVersion = UdonSharpProgramVersion.V1SerializationUpdate;
                        EditorUtility.SetDirty(programAsset);
                        versionUpgraded = true;
                    }
                }
            }

            if (scriptUpgraded)
            {
                AssetDatabase.Refresh();
                return(true);
            }

            if (versionUpgraded)
            {
                UdonSharpCompilerV1.CompileSync(new UdonSharpCompileOptions());
            }

            return(false);
        }