diff --git a/Editor/Editors/YarnProjectImporterEditor.cs b/Editor/Editors/YarnProjectImporterEditor.cs index b6e9e3b1..07f983bf 100644 --- a/Editor/Editors/YarnProjectImporterEditor.cs +++ b/Editor/Editors/YarnProjectImporterEditor.cs @@ -234,7 +234,7 @@ protected override void Apply() localisationFieldsContainer?.Add(newBaseLanguageField); } } - + #if UNITY_2022_2_OR_NEWER public override void DiscardChanges() { localizationEntryFields.Clear(); @@ -250,7 +250,23 @@ public override void DiscardChanges() inspectorRoot?.Add(CreateInspectorGUI()); } + #else + protected override void ResetValues() + { + localizationEntryFields.Clear(); + sourceEntryFields.Clear(); + LocalisationsAddedOrRemoved = false; + BaseLanguageNameModified = false; + SourceFilesAddedOrRemoved = false; + base.ResetValues(); + + var inspectorRoot = uiRoot?.parent; + uiRoot?.RemoveFromHierarchy(); + + inspectorRoot?.Add(CreateInspectorGUI()); + } + #endif public override VisualElement CreateInspectorGUI() { if (!(target is YarnProjectImporter yarnProjectImporter)) diff --git a/Editor/Utility/AssetStoreSamplesInstaller.cs.meta b/Editor/Utility/AssetStoreSamplesInstaller.cs.meta index 33a0ba9a..be61f308 100644 --- a/Editor/Utility/AssetStoreSamplesInstaller.cs.meta +++ b/Editor/Utility/AssetStoreSamplesInstaller.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: cd9cd1021d3794a8bb88cc658221e712 \ No newline at end of file +guid: cd9cd1021d3794a8bb88cc658221e712 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Utility/DependencyInstallerUtilities.cs.meta b/Editor/Utility/DependencyInstallerUtilities.cs.meta index 2f430e9d..cd688693 100644 --- a/Editor/Utility/DependencyInstallerUtilities.cs.meta +++ b/Editor/Utility/DependencyInstallerUtilities.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 91c165b2af7cd4bdcbf269890f2a49c0 \ No newline at end of file +guid: 91c165b2af7cd4bdcbf269890f2a49c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Utility/EnsureInputModuleAvailableEditor.cs b/Editor/Utility/EnsureInputModuleAvailableEditor.cs new file mode 100644 index 00000000..bc984508 --- /dev/null +++ b/Editor/Utility/EnsureInputModuleAvailableEditor.cs @@ -0,0 +1,39 @@ +#nullable enable +using UnityEditor; +using UnityEngine; + +namespace Yarn.Unity.Editor +{ + [CustomEditor(typeof(EnsureInputModuleAvailable))] + public class EnsureInputModuleAvailableEditor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + const string message = "This component checks to see if a UI Input Module exists in the scene. If one isn't available, it creates an input module on this object that's compatible with your current input system."; + + EditorGUILayout.HelpBox(message, MessageType.Info); + + if (target is not EnsureInputModuleAvailable module) + { + return; + } + + if (module.TryGetComponent(out var existingInputModule)) + { + if ((existingInputModule.hideFlags & HideFlags.DontSaveInEditor) != 0) + { + EditorGUILayout.Space(); + + EditorGUILayout.HelpBox("The " + existingInputModule.GetType().Name + " on this object is marked as temporary, and won't be saved in the scene. Click the button below if you'd like to include it in the saved scene.", MessageType.Info); + if (GUILayout.Button("Save in Scene")) + { + existingInputModule.hideFlags &= ~HideFlags.DontSaveInEditor; + UnityEditor.EditorUtility.SetDirty(existingInputModule.gameObject); + var scene = existingInputModule.gameObject.scene; + UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene); + } + } + } + } + } +} diff --git a/Editor/Utility/EnsureInputModuleAvailableEditor.cs.meta b/Editor/Utility/EnsureInputModuleAvailableEditor.cs.meta new file mode 100644 index 00000000..e850fe33 --- /dev/null +++ b/Editor/Utility/EnsureInputModuleAvailableEditor.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d45c3d210e534d89b3395f69e4c91d27 +timeCreated: 1756024244 \ No newline at end of file diff --git a/Editor/Utility/ItchSamplesInstaller.cs.meta b/Editor/Utility/ItchSamplesInstaller.cs.meta index fe7ba6f6..dc2f5d90 100644 --- a/Editor/Utility/ItchSamplesInstaller.cs.meta +++ b/Editor/Utility/ItchSamplesInstaller.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 0c21c80267e2f4204b7d5e261e616faa \ No newline at end of file +guid: 0c21c80267e2f4204b7d5e261e616faa +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Utility/ManualSamplesInstaller.cs.meta b/Editor/Utility/ManualSamplesInstaller.cs.meta index 0aaf789b..70772f6c 100644 --- a/Editor/Utility/ManualSamplesInstaller.cs.meta +++ b/Editor/Utility/ManualSamplesInstaller.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: 23020a8d4675148c9ad05af13ab1202b \ No newline at end of file +guid: 23020a8d4675148c9ad05af13ab1202b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Utility/UPMSamplesInstaller.cs.meta b/Editor/Utility/UPMSamplesInstaller.cs.meta index a86496d7..8093084c 100644 --- a/Editor/Utility/UPMSamplesInstaller.cs.meta +++ b/Editor/Utility/UPMSamplesInstaller.cs.meta @@ -1,2 +1,11 @@ fileFormatVersion: 2 -guid: fc1506e515b6046b1939407847cb96e5 \ No newline at end of file +guid: fc1506e515b6046b1939407847cb96e5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/DialogueRunner/DialogueRunner.cs b/Runtime/DialogueRunner/DialogueRunner.cs index 9316fbd4..8b2c38cf 100644 --- a/Runtime/DialogueRunner/DialogueRunner.cs +++ b/Runtime/DialogueRunner/DialogueRunner.cs @@ -957,7 +957,8 @@ public void StartDialogue(string nodeName) dialogueCompletionSource = new YarnTaskCompletionSource(); - dialogueCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(this.destroyCancellationToken); + dialogueCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(this.GetDestroyCancellationToken()); + LineProvider.YarnProject = yarnProject; EnsureCommandDispatcherReady(); diff --git a/Runtime/Utility/ApplicationExitCompat.cs b/Runtime/Utility/ApplicationExitCompat.cs new file mode 100644 index 00000000..276bbbed --- /dev/null +++ b/Runtime/Utility/ApplicationExitCompat.cs @@ -0,0 +1,40 @@ +using System.Threading; +using UnityEditor; +using UnityEngine; + +namespace Yarn.Unity +{ + public static class ApplicationExitCompat + { + private static CancellationTokenSource _exitCts; + + public static CancellationToken ExitCancellationToken + { + get + { + _exitCts ??= new CancellationTokenSource(); + + return _exitCts.Token; + } + } + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void Init() + { + _exitCts ??= new CancellationTokenSource(); + + #if UNITY_EDITOR + EditorApplication.playModeStateChanged += state => + { + if (state == PlayModeStateChange.ExitingPlayMode) + { + _exitCts.Cancel(); + } + }; + #endif + + Application.quitting += () => { _exitCts.Cancel(); }; + } + } + +} diff --git a/Runtime/Utility/ApplicationExitCompat.cs.meta b/Runtime/Utility/ApplicationExitCompat.cs.meta new file mode 100644 index 00000000..152b1ce8 --- /dev/null +++ b/Runtime/Utility/ApplicationExitCompat.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8b8882563ca441ff801e38f3927d42fd +timeCreated: 1755969744 \ No newline at end of file diff --git a/Runtime/Utility/EnsureInputModuleAvailable.cs b/Runtime/Utility/EnsureInputModuleAvailable.cs index 7a78e87b..5974bd46 100644 --- a/Runtime/Utility/EnsureInputModuleAvailable.cs +++ b/Runtime/Utility/EnsureInputModuleAvailable.cs @@ -95,44 +95,4 @@ private bool IsInputModuleInScene } } } - -#if UNITY_EDITOR - namespace Editor - { - using UnityEditor; - [CustomEditor(typeof(EnsureInputModuleAvailable))] - public class EnsureInputModuleAvailableEditor : UnityEditor.Editor - { - public override void OnInspectorGUI() - { - const string message = "This component checks to see if a UI Input Module exists in the scene. If one isn't available, it creates an input module on this object that's compatible with your current input system."; - - EditorGUILayout.HelpBox(message, MessageType.Info); - - if (target is not EnsureInputModuleAvailable module) - { - return; - } - - if (module.TryGetComponent(out var existingInputModule)) - { - if ((existingInputModule.hideFlags & HideFlags.DontSaveInEditor) != 0) - { - EditorGUILayout.Space(); - - EditorGUILayout.HelpBox("The " + existingInputModule.GetType().Name + " on this object is marked as temporary, and won't be saved in the scene. Click the button below if you'd like to include it in the saved scene.", MessageType.Info); - if (GUILayout.Button("Save in Scene")) - { - existingInputModule.hideFlags &= ~HideFlags.DontSaveInEditor; - UnityEditor.EditorUtility.SetDirty(existingInputModule.gameObject); - var scene = existingInputModule.gameObject.scene; - UnityEditor.SceneManagement.EditorSceneManager.SaveScene(scene); - } - } - } - } - } - } -#endif - } diff --git a/Runtime/Utility/MonoBehaviourExtensions.cs b/Runtime/Utility/MonoBehaviourExtensions.cs new file mode 100644 index 00000000..d7f3e982 --- /dev/null +++ b/Runtime/Utility/MonoBehaviourExtensions.cs @@ -0,0 +1,35 @@ +#nullable enable +using System.Threading; +using UnityEngine; + +namespace Yarn.Unity +{ + public static class MonoBehaviourExtensions + { + /// + /// Gets a CancellationToken that is cancelled when the MonoBehaviour's GameObject is destroyed. + /// + public static CancellationToken GetDestroyCancellationToken(this MonoBehaviour mb) + { + #if UNITY_2022_2_OR_NEWER + return mb.destroyCancellationToken; + #endif + + DestroyTokenNotifier notifier = mb.gameObject.GetComponent() + ?? mb.gameObject.AddComponent(); + + return notifier.Cts.Token; + } + } + + internal class DestroyTokenNotifier : MonoBehaviour + { + public CancellationTokenSource Cts { get; } = new(); + + private void OnDestroy() + { + Cts.Cancel(); + Cts.Dispose(); + } + } +} diff --git a/Runtime/Utility/MonoBehaviourExtensions.cs.meta b/Runtime/Utility/MonoBehaviourExtensions.cs.meta new file mode 100644 index 00000000..9950a26f --- /dev/null +++ b/Runtime/Utility/MonoBehaviourExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5e0016d1a91f4ad2964f957811c5f9f8 +timeCreated: 1755961208 \ No newline at end of file diff --git a/Runtime/Views/Legacy/DialogueViewBase.cs b/Runtime/Views/Legacy/DialogueViewBase.cs index e41c02e5..0bcd4372 100644 --- a/Runtime/Views/Legacy/DialogueViewBase.cs +++ b/Runtime/Views/Legacy/DialogueViewBase.cs @@ -383,7 +383,7 @@ public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellation // 2. The line is cancelled. while (phaseComplete == false && token.IsNextLineRequested == false - && Application.exitCancellationToken.IsCancellationRequested == false) + && GetExitCancellationToken().IsCancellationRequested == false) { await YarnTask.Yield(); } @@ -396,7 +396,7 @@ public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellation phaseComplete = false; this.InterruptLine(line, PhaseComplete); while (phaseComplete == false - && Application.exitCancellationToken.IsCancellationRequested == false) + && GetExitCancellationToken().IsCancellationRequested == false) { await YarnTask.Yield(); } @@ -408,7 +408,7 @@ public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellation this.DismissLine(PhaseComplete); while (phaseComplete == false - && Application.exitCancellationToken.IsCancellationRequested == false) + && GetExitCancellationToken().IsCancellationRequested == false) { await YarnTask.Yield(); } @@ -428,12 +428,12 @@ public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellation selectedOptionID = selectedID; }); - while (selectedOptionID == -1 && cancellationToken.IsCancellationRequested == false && Application.exitCancellationToken.IsCancellationRequested == false) + while (selectedOptionID == -1 && cancellationToken.IsCancellationRequested == false && GetExitCancellationToken().IsCancellationRequested == false) { await YarnTask.Yield(); } - if (cancellationToken.IsCancellationRequested || Application.exitCancellationToken.IsCancellationRequested) + if (cancellationToken.IsCancellationRequested || GetExitCancellationToken().IsCancellationRequested) { // We were cancelled or are exiting the game. Return null. return null; @@ -453,5 +453,13 @@ public override async YarnTask RunLineAsync(LocalizedLine line, LineCancellation // an option that was valid. Throw an error. throw new InvalidOperationException($"Option view selected an invalid option ID ({selectedOptionID})"); } + + private static CancellationToken GetExitCancellationToken() + { + #if UNITY_2022_2_OR_NEWER + return Application.exitCancellationToken; + #endif + return ApplicationExitCompat.ExitCancellationToken; + } } }