コード例 #1
0
        /// <summary>
        /// If you're considering commenting any section of this out, try enabling the force compile in the U# settings first.
        /// This is here to prevent you from corrupting your project files.
        /// If scripts are left uncompiled from Unity's side when uploading, there is a chance to corrupt your assemblies which can cause all of your UdonBehaviours to lose their variables if handled wrong.
        /// </summary>
        /// <param name="requestedBuildType"></param>
        /// <returns></returns>
        public bool OnBuildRequested(VRCSDKRequestedBuildType requestedBuildType)
        {
            UdonSharpSettings settings = UdonSharpSettings.GetSettings();
            bool shouldForceCompile    = settings.shouldForceCompile;

            // Unity doesn't like this and will throw errors if it ends up compiling scripts. But it seems to work.
            // This is marked experimental for now since I don't know if it will break horribly in some case.
            if (shouldForceCompile)
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
            }
            else
            {
                AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);

                if (EditorApplication.isCompiling)
                {
                    UdonSharpUtils.LogWarning("Scripts are in the process of compiling, please retry build after scripts have compiled.");
                    UdonSharpUtils.ShowEditorNotification("Scripts are in the process of compiling, please retry build after scripts have compiled.");
                    return(false);
                }
            }

            return(true);
        }
コード例 #2
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);
        }
コード例 #3
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();
        }
コード例 #4
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);
            }
        }
コード例 #5
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");
            }
        }
コード例 #6
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;
            }
        }
コード例 #7
0
        public void OnEnable()
        {
            errorState = BehaviourInspectorErrorState.Success;
            bool needsUpgradePass = false;

            foreach (Object target in targets)
            {
                UdonBehaviour targetBehaviour = (UdonBehaviour)target;

                if (!UdonSharpEditorUtility.IsUdonSharpBehaviour(targetBehaviour))
                {
                    UdonSharpUtils.LogWarning($"UdonBehaviour '{targetBehaviour}' is not using a valid U# program asset", targetBehaviour);
                    errorState = BehaviourInspectorErrorState.NoValidUSharpProgram;
                    break;
                }

                if (UdonSharpEditorUtility.GetProxyBehaviour(targetBehaviour) != null)
                {
                    continue;
                }

                needsUpgradePass = true;

                if (PrefabUtility.IsPartOfPrefabInstance(targetBehaviour) &&
                    !PrefabUtility.IsAddedComponentOverride(targetBehaviour))
                {
                    UdonSharpUtils.LogWarning($"UdonBehaviour '{targetBehaviour}' needs upgrade on source prefab asset.", targetBehaviour);
                    errorState = BehaviourInspectorErrorState.PrefabNeedsUpgrade;
                    break;
                }
            }

            if (!needsUpgradePass || errorState != BehaviourInspectorErrorState.Success)
            {
                return;
            }

            foreach (Object target in targets)
            {
                UdonBehaviour      targetBehaviour    = (UdonBehaviour)target;
                UdonSharpBehaviour udonSharpBehaviour = UdonSharpEditorUtility.GetProxyBehaviour(targetBehaviour);

                // Needs setup
                if (udonSharpBehaviour == null)
                {
                    UdonSharpEditorUtility.SetIgnoreEvents(true);

                    try
                    {
                        udonSharpBehaviour = (UdonSharpBehaviour)Undo.AddComponent(targetBehaviour.gameObject, UdonSharpEditorUtility.GetUdonSharpBehaviourType(targetBehaviour));

                        UdonSharpEditorUtility.SetBackingUdonBehaviour(udonSharpBehaviour, targetBehaviour);
                        UdonSharpEditorUtility.MoveComponentRelativeToComponent(udonSharpBehaviour, targetBehaviour, true);
                        UdonSharpEditorUtility.SetBehaviourVersion(targetBehaviour, UdonSharpBehaviourVersion.CurrentVersion);
                        UdonSharpEditorUtility.SetSceneBehaviourUpgraded(targetBehaviour);
                    }
                    finally
                    {
                        UdonSharpEditorUtility.SetIgnoreEvents(false);
                    }
                }

                udonSharpBehaviour.enabled = targetBehaviour.enabled;

            #if !UDONSHARP_DEBUG
                targetBehaviour.hideFlags = HideFlags.HideInInspector;
            #else
                targetBehaviour.hideFlags = HideFlags.None;
            #endif
            }
        }
コード例 #8
0
            public override SyntaxNode VisitFieldDeclaration(FieldDeclarationSyntax node)
            {
                FieldDeclarationSyntax fieldDeclaration = (FieldDeclarationSyntax)base.VisitFieldDeclaration(node);

                var typeInfo = model.GetTypeInfo(node.Declaration.Type);

                if (typeInfo.Type == null)
                {
                    UdonSharpUtils.LogWarning($"Could not find symbol for {node}");
                    return(fieldDeclaration);
                }

                ITypeSymbol rootType = typeInfo.Type;

                while (rootType.TypeKind == TypeKind.Array)
                {
                    rootType = ((IArrayTypeSymbol)rootType).ElementType;
                }

                if (rootType.TypeKind == TypeKind.Error ||
                    rootType.TypeKind == TypeKind.Unknown)
                {
                    UdonSharpUtils.LogWarning($"Type {typeInfo.Type} for field '{fieldDeclaration.Declaration}' is invalid");
                    return(fieldDeclaration);
                }

                IFieldSymbol firstFieldSymbol = (IFieldSymbol)model.GetDeclaredSymbol(node.Declaration.Variables.First());

                rootType = firstFieldSymbol.Type;

                // If the field is not serialized or is using Odin already, we don't need to do anything.
                if (!IsFieldSerializedWithoutOdin(firstFieldSymbol))
                {
                    return(fieldDeclaration);
                }

                // Getting the type may fail if it's a user type that hasn't compiled on the C# side yet. For now we skip it, but we should do a simplified check for jagged arrays
                if (!TypeSymbol.TryGetSystemType(rootType, out Type systemType))
                {
                    return(fieldDeclaration);
                }

                // If Unity can serialize the type, we're good
                if (UnitySerializationUtility.GuessIfUnityWillSerialize(systemType))
                {
                    return(fieldDeclaration);
                }

                // Common type that gets picked up as serialized but shouldn't be
                // todo: Add actual checking for if a type is serializable, which isn't consistent. Unity/System library types in large part are serializable but don't have the System.Serializable tag, but types outside those assemblies need the tag to be serialized.
                if (systemType == typeof(VRCPlayerApi) || systemType == typeof(VRCPlayerApi[]))
                {
                    return(fieldDeclaration);
                }

                Modified = true;

                NameSyntax odinSerializeName = IdentifierName("VRC");

                odinSerializeName = QualifiedName(odinSerializeName, IdentifierName("Udon"));
                odinSerializeName = QualifiedName(odinSerializeName, IdentifierName("Serialization"));
                odinSerializeName = QualifiedName(odinSerializeName, IdentifierName("OdinSerializer"));
                odinSerializeName = QualifiedName(odinSerializeName, IdentifierName("OdinSerialize"));

                // Somehow it seems like there's literally no decent way to maintain the indent on inserted code so we'll just inline the comment because Roslyn is dumb
                SyntaxTrivia        commentTrivia = Comment(" /* UdonSharp auto-upgrade: serialization */ ");
                AttributeListSyntax newAttribList = AttributeList(SeparatedList(new [] { Attribute(odinSerializeName) })).WithTrailingTrivia(commentTrivia);

                SyntaxList <AttributeListSyntax> attributeList = fieldDeclaration.AttributeLists;

                if (attributeList.Count > 0)
                {
                    SyntaxTriviaList trailingTrivia = attributeList.Last().GetTrailingTrivia();
                    trailingTrivia = trailingTrivia.Insert(0, commentTrivia);
                    attributeList.Replace(attributeList[attributeList.Count - 1], attributeList[attributeList.Count - 1].WithoutTrailingTrivia());

                    newAttribList = newAttribList.WithTrailingTrivia(trailingTrivia);
                }
                else
                {
                    newAttribList    = newAttribList.WithLeadingTrivia(fieldDeclaration.GetLeadingTrivia());
                    fieldDeclaration = fieldDeclaration.WithoutLeadingTrivia();
                }

                attributeList = attributeList.Add(newAttribList);

                return(fieldDeclaration.WithAttributeLists(attributeList));
            }