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"); }
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 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 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; }
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; }
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(); }
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); } }
/// <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"); } }
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; } }
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(); }
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); }