From 31028eda22f374b47b1b590e81d746efca088396 Mon Sep 17 00:00:00 2001 From: git-upm-publisher Date: Fri, 7 Jul 2023 17:42:04 +0300 Subject: [PATCH] UPM release 3.0.0 --- .assets/generator_menu.png | Bin 10990 -> 10990 bytes .assets/settings.png | Bin 33459 -> 43081 bytes .assets/toolbox_disabled.png | Bin 0 -> 10928 bytes .assets/toolbox_nowhere.png | Bin 0 -> 11726 bytes .assets/validation_addressable_prompt.png | Bin 0 -> 8287 bytes .assets/validation_inspector.png | Bin 25163 -> 33062 bytes CHANGELOG.md | 64 ++++ Editor/Eflatun.SceneReference.Editor.asmdef | 19 +- .../AddressablesChangeListener.cs | 38 ++ .../AddressablesChangeListener.cs.meta | 3 + .../MapGeneratorTriggers/BuildPreProcessor.cs | 12 +- .../PackagesChangeListener.cs | 28 ++ .../PackagesChangeListener.cs.meta | 3 + .../MapGeneratorTriggers/PlayModeListener.cs | 6 +- .../SceneAssetPostprocessor.cs | 6 +- Editor/SceneDataMapsGenerator.cs | 102 +++++ ...cs.meta => SceneDataMapsGenerator.cs.meta} | 0 ...s.cs => SceneDataMapsGeneratorTriggers.cs} | 8 +- ...=> SceneDataMapsGeneratorTriggers.cs.meta} | 0 Editor/SceneGuidToPathMapGenerator.cs | 50 --- Editor/SceneReferencePropertyDrawer.cs | 219 +++++------ Editor/SettingsManager.cs | 115 ++++-- Editor/Toolbox.meta | 3 + Editor/Toolbox/AddToBuildTool.cs | 65 ++++ Editor/Toolbox/AddToBuildTool.cs.meta | 3 + Editor/Toolbox/EnableInBuildTool.cs | 68 ++++ Editor/Toolbox/EnableInBuildTool.cs.meta | 3 + Editor/Toolbox/ITool.cs | 10 + Editor/Toolbox/ITool.cs.meta | 3 + Editor/Toolbox/MakeAddressableTool.cs | 71 ++++ Editor/Toolbox/MakeAddressableTool.cs.meta | 3 + Editor/Toolbox/ToolboxPopupWindow.cs | 56 +++ Editor/Toolbox/ToolboxPopupWindow.cs.meta | 3 + Editor/Utility/EditorUtils.cs | 80 +++- README.md | 349 ++++++++++++------ Runtime/ColoringBehaviour.cs | 2 +- Runtime/Eflatun.SceneReference.asmdef | 15 +- Runtime/Exceptions.meta | 3 + .../Exceptions/AddressNotFoundException.cs | 39 ++ .../AddressNotFoundException.cs.meta | 3 + .../Exceptions/AddressNotUniqueException.cs | 39 ++ .../AddressNotUniqueException.cs.meta | 3 + .../AddressablesSupportDisabledException.cs | 29 ++ ...dressablesSupportDisabledException.cs.meta | 3 + .../EmptySceneReferenceException.cs | 4 +- .../EmptySceneReferenceException.cs.meta | 0 .../InvalidSceneReferenceException.cs | 6 +- .../InvalidSceneReferenceException.cs.meta | 0 .../SceneNotAddressableException.cs | 31 ++ .../SceneNotAddressableException.cs.meta | 3 + .../SceneReferenceCreationException.cs | 2 +- .../SceneReferenceCreationException.cs.meta | 0 .../SceneReferenceException.cs | 2 +- .../SceneReferenceException.cs.meta | 0 .../SceneReferenceInternalException.cs | 9 +- .../SceneReferenceInternalException.cs.meta | 0 Runtime/Paths.cs | 44 ++- Runtime/SceneGuidToAddressMapProvider.cs | 132 +++++++ Runtime/SceneGuidToAddressMapProvider.cs.meta | 3 + Runtime/SceneGuidToPathMapProvider.cs | 10 +- Runtime/SceneReference.cs | 188 ++++++---- Runtime/SceneReferenceOptionsAttribute.cs | 18 +- Runtime/SceneReferenceState.cs | 32 ++ Runtime/SceneReferenceState.cs.meta | 3 + ...tyLineBehaviour.cs => ToolboxBehaviour.cs} | 8 +- ...viour.cs.meta => ToolboxBehaviour.cs.meta} | 0 Runtime/Utility/Utils.cs | 11 + package.json | 2 +- 68 files changed, 1586 insertions(+), 448 deletions(-) create mode 100644 .assets/toolbox_disabled.png create mode 100644 .assets/toolbox_nowhere.png create mode 100644 .assets/validation_addressable_prompt.png create mode 100644 Editor/MapGeneratorTriggers/AddressablesChangeListener.cs create mode 100644 Editor/MapGeneratorTriggers/AddressablesChangeListener.cs.meta create mode 100644 Editor/MapGeneratorTriggers/PackagesChangeListener.cs create mode 100644 Editor/MapGeneratorTriggers/PackagesChangeListener.cs.meta create mode 100644 Editor/SceneDataMapsGenerator.cs rename Editor/{SceneGuidToPathMapGenerator.cs.meta => SceneDataMapsGenerator.cs.meta} (100%) rename Editor/{SceneGuidToPathMapGenerationTriggers.cs => SceneDataMapsGeneratorTriggers.cs} (59%) rename Editor/{SceneGuidToPathMapGenerationTriggers.cs.meta => SceneDataMapsGeneratorTriggers.cs.meta} (100%) delete mode 100644 Editor/SceneGuidToPathMapGenerator.cs create mode 100644 Editor/Toolbox.meta create mode 100644 Editor/Toolbox/AddToBuildTool.cs create mode 100644 Editor/Toolbox/AddToBuildTool.cs.meta create mode 100644 Editor/Toolbox/EnableInBuildTool.cs create mode 100644 Editor/Toolbox/EnableInBuildTool.cs.meta create mode 100644 Editor/Toolbox/ITool.cs create mode 100644 Editor/Toolbox/ITool.cs.meta create mode 100644 Editor/Toolbox/MakeAddressableTool.cs create mode 100644 Editor/Toolbox/MakeAddressableTool.cs.meta create mode 100644 Editor/Toolbox/ToolboxPopupWindow.cs create mode 100644 Editor/Toolbox/ToolboxPopupWindow.cs.meta create mode 100644 Runtime/Exceptions.meta create mode 100644 Runtime/Exceptions/AddressNotFoundException.cs create mode 100644 Runtime/Exceptions/AddressNotFoundException.cs.meta create mode 100644 Runtime/Exceptions/AddressNotUniqueException.cs create mode 100644 Runtime/Exceptions/AddressNotUniqueException.cs.meta create mode 100644 Runtime/Exceptions/AddressablesSupportDisabledException.cs create mode 100644 Runtime/Exceptions/AddressablesSupportDisabledException.cs.meta rename Runtime/{ => Exceptions}/EmptySceneReferenceException.cs (87%) rename Runtime/{ => Exceptions}/EmptySceneReferenceException.cs.meta (100%) rename Runtime/{ => Exceptions}/InvalidSceneReferenceException.cs (85%) rename Runtime/{ => Exceptions}/InvalidSceneReferenceException.cs.meta (100%) create mode 100644 Runtime/Exceptions/SceneNotAddressableException.cs create mode 100644 Runtime/Exceptions/SceneNotAddressableException.cs.meta rename Runtime/{ => Exceptions}/SceneReferenceCreationException.cs (95%) rename Runtime/{ => Exceptions}/SceneReferenceCreationException.cs.meta (100%) rename Runtime/{ => Exceptions}/SceneReferenceException.cs (94%) rename Runtime/{ => Exceptions}/SceneReferenceException.cs.meta (100%) rename Runtime/{ => Exceptions}/SceneReferenceInternalException.cs (62%) rename Runtime/{ => Exceptions}/SceneReferenceInternalException.cs.meta (100%) create mode 100644 Runtime/SceneGuidToAddressMapProvider.cs create mode 100644 Runtime/SceneGuidToAddressMapProvider.cs.meta create mode 100644 Runtime/SceneReferenceState.cs create mode 100644 Runtime/SceneReferenceState.cs.meta rename Runtime/{UtilityLineBehaviour.cs => ToolboxBehaviour.cs} (55%) rename Runtime/{UtilityLineBehaviour.cs.meta => ToolboxBehaviour.cs.meta} (100%) diff --git a/.assets/generator_menu.png b/.assets/generator_menu.png index 951eb8770c3433e7b22ad7019e733db94280bc7f..c1e78d41a8b0df98212e5c4139646474f2e217a2 100644 GIT binary patch delta 5376 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 diff --git a/.assets/settings.png b/.assets/settings.png index 3946ca87f197f921497afe4fa8eb63cd184949f0..a430b730178548dd382b1c73fee600b9312097f7 100644 GIT binary patch literal 43081 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 Y(B0+U-rZuU*$4l$&*>8j)GdPk3tuN>hX4Qo literal 33459 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

t4nx{U;(k+5#eA6Kytt>QNiD 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 W{n(9_hYK0b0rJv7sVa%jA^#5ytpQE| literal 0 HcmV?d00001 diff --git a/.assets/validation_inspector.png b/.assets/validation_inspector.png index 8ef1ca69d7c1f9c124cc2357aa57ba17647f4e4b..851102fc3afda3b1a732c97f3c9560ff4dc5265d 100644 GIT binary patch literal 33062 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 literal 25163 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 427c926e..f1e90bb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,69 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +## [3.0.0] - 2023-07-07 + +This release introduces support for addressable scenes. + +There are breaking changes to settings. Please visit the settings page and re-apply all your settings as soon as you update. + +### Breaking Changes +- `SceneGuidToPathMapGenerator` is renamed to `SceneDataMapsGenerator`. +- `SceneGuidToPathMapGenerationTriggers` is renamed to `SceneDataMapsGeneratorTriggers`. +- Settings changes: + - Scene GUID to Path Map (new name Scene Data Maps) category: + - The category is renamed from `SceneGuidToPathMap` to `SceneDataMaps`. + - Key of the `GenerationTriggers` setting is changed from `SceneGuidToPathMap.GenerationTriggers` to `SceneDataMaps.GenerationTriggers`. + - Key of the `JsonFormatting` setting is changed from `SceneGuidToPathMap.JsonFormatting` to `SceneDataMaps.JsonFormatting`. + - Key of the `FailBuildIfGenerationFails` setting is changed from `SceneGuidToPathMap.FailBuildIfGenerationFails` to `SceneDataMaps.FailBuildIfGenerationFails`. + - Property Drawer category: + - `ShowInlineSceneInBuildUtility` setting is renamed to `ShowInlineToolbox`. The key is also changed from `PropertyDrawer.ShowInlineSceneInBuildUtility` to `PropertyDrawer.ShowInlineToolbox`. +- All exceptions are moved from the `Eflatun.SceneReference` namespace to the new `Eflatun.SceneReference.Exceptions` namespace. +- Following valiation properties are removed from `SceneReference`. They are instead replaced with a more informative `State` property. See the _Added_ section. + - `IsInSceneGuidToPathMap`: removed. + - `IsInBuildAndEnabled`: removed. + - `IsSafeToUse`: removed. + - `HasValue`: no longer public. +- `SceneReferenceOptionsAttribute` changes: + - `Coloring` field is renamed to `SceneInBuildColoring`. It still controls the same cases of coloring, but they are no longer all the coloring options. See the _Changes_ section. + - `UtilityLine` field is removed. Its replacement is the `Toolbox` field. See the _Added_ section. + +### Added +- `SceneDataMapsGenerator` now also generates a scene GUID to address map. The map will be empty if addressables support is disabled. +- New map generation triggers: + - `AfterPackagesResolve`: Triggers after packages are resolved. + - `AfterAddressablesChange`: Triggers after addressable groups change. +- Property drawer can optionally color addressables scenes differently to draw attention to them. +- New inline utilties: + - `Make Addressable`: Makes the scene addressable. +- New settings: + - Addressables Support (`AddressablesSupport`) category: + - `ColorAddressableScenes` setting: If enabled, scene references that have an addressable scene will be colored differently. + - An info box that displays the current addressables support status. +- New exceptions: + - `AddressNotFoundException` + - `AddressNotUniqueException` + - `AddressablesSupportDisabledException` + - `SceneNotAddressableException` +- `SceneGuidToAddressMapProvider` class: Provides a map of scene GUIDs to their address. Very similar to `SceneGuidToPathMapProvider`, with the exception that it cannot provide an inverse map. This is because the address of an asset is not guaranteed to be unique. Instead, it provides `GetGuidFromAddress` and `TryGetGuidFromAddress` methods. +- `SceneReference.FromAddress` factory method: Creates a `SceneReference` from the given address. +- `SceneReference.Address` property. +- `SceneReference.State` property: This property replaces all previously exposed validation methods. It returns a `SceneReferenceState` enum, which describes the state of the `SceneReference` in terms of usage. +- `SceneReferenceState` enum: Describes the state of the `SceneReference` in terms of usage. + - `Unsafe`: The `SceneReference` is not safe to use. + - `Regular`: The `SceneReference` is safe to use, and it references a regular scene. + - `Addressable`: The `SceneReference` is safe to use, and it references an addressable scene. This state is only possible if addressables support is enabled. +- `SceneReferenceOptionsAttribute` new fields: + - `Toolbox`: Controls the visibility of the toolbox button. It replaces the now deleted `UtilityLine` field. + - `AddressableColoring`: Controls the coloring behaviour of the addressable scenes. +- `ToolboxBehaviour` enum. Replaces the now deleted `UtilityLineBehaviour` enum with the same semantics. + +### Changed +- The concept of Utility Line is replaced with the concept of Toolbox. In summary, instead of drawing buttons as a second line below the field, we instead draw a small button to the end of the field on the same line. When clicked, a toolbox popup appears with all the utilities. +- `SceneInBuildColoring` argument (previously named `Coloring`) of `SceneReferenceOptionsAttribute` still controls the same types of coloring cases, but since they used to be all the cases, the field was implicitly controlling the entire coloring behaviour. While its semantics are not changed, since there are now other coloring cases, it is no longer the only field that controls the entire coloring behaviour. + + + ## [2.1.0] - 2023-03-01 ### Added @@ -52,6 +115,7 @@ We renamed some of our internal serialized fields. Since we utilize `FormerlySer - Internal serialized field name changes: - `SceneReference.sceneAsset` to `SceneReference.asset` - `SceneReference.sceneAssetGuidHex` to `SceneReference.guid` +- Menu item `Tools/Eflatun/Scene Reference/Run Scene GUID to Path Map Generator` is renamed to `Tools/Eflatun/Scene Reference/Generate Scene Data Maps`. ### Fixed - Prevent empty scene GUID hex in Unity serialized `SceneReference` instances. diff --git a/Editor/Eflatun.SceneReference.Editor.asmdef b/Editor/Eflatun.SceneReference.Editor.asmdef index c6bd4dc5..7b5a5ce1 100644 --- a/Editor/Eflatun.SceneReference.Editor.asmdef +++ b/Editor/Eflatun.SceneReference.Editor.asmdef @@ -3,7 +3,9 @@ "rootNamespace": "Eflatun.SceneReference.Editor", "references": [ "GUID:caca889fed6088d40b22c2d7e2909c31", - "GUID:49818357e697641afb75d2f8acaf1861" + "GUID:49818357e697641afb75d2f8acaf1861", + "GUID:9e24947de15b9834991c9d8411ea37cf", + "GUID:69448af7b92c7f342b298e06a37122aa" ], "includePlatforms": [ "Editor" @@ -14,6 +16,17 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.addressables", + "expression": "", + "define": "ESR_ADDRESSABLES" + }, + { + "name": "com.unity.addressables", + "expression": "[1.0.0,2.0.0)", + "define": "ESR_ADDRESSABLES_1_X_X" + } + ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Editor/MapGeneratorTriggers/AddressablesChangeListener.cs b/Editor/MapGeneratorTriggers/AddressablesChangeListener.cs new file mode 100644 index 00000000..e73287be --- /dev/null +++ b/Editor/MapGeneratorTriggers/AddressablesChangeListener.cs @@ -0,0 +1,38 @@ +#if ESR_ADDRESSABLES + +using UnityEditor; +using UnityEditor.AddressableAssets; +using UnityEditor.AddressableAssets.Settings; + +namespace Eflatun.SceneReference.Editor.MapGeneratorTriggers +{ + [InitializeOnLoad] + internal class AddressablesChangeListener + { + static AddressablesChangeListener() + { + AddressableAssetSettingsDefaultObject.Settings.OnModification += OnAddressablesChange; + } + + private static void OnAddressablesChange(AddressableAssetSettings s, AddressableAssetSettings.ModificationEvent e, object o) + { + if (e == AddressableAssetSettings.ModificationEvent.EntryAdded || + e == AddressableAssetSettings.ModificationEvent.EntryRemoved || + e == AddressableAssetSettings.ModificationEvent.EntryModified || + e == AddressableAssetSettings.ModificationEvent.EntryMoved || + e == AddressableAssetSettings.ModificationEvent.EntryCreated) + { + if (SettingsManager.SceneDataMaps.IsGenerationTriggerEnabled(SceneDataMapsGeneratorTriggers.AfterAddressablesChange)) + { + SceneDataMapsGenerator.Run(); + } + else + { + Logger.Warn($"Skipping scene data maps generation after addressables changes. It is recommended to enable map generation after addressables changes, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); + } + } + } + } +} + +#endif // ESR_ADDRESSABLES diff --git a/Editor/MapGeneratorTriggers/AddressablesChangeListener.cs.meta b/Editor/MapGeneratorTriggers/AddressablesChangeListener.cs.meta new file mode 100644 index 00000000..f90e9dcb --- /dev/null +++ b/Editor/MapGeneratorTriggers/AddressablesChangeListener.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: be593da8a2c047678b992f7ccd7f5d1f +timeCreated: 1682780777 \ No newline at end of file diff --git a/Editor/MapGeneratorTriggers/BuildPreProcessor.cs b/Editor/MapGeneratorTriggers/BuildPreProcessor.cs index 099b3357..d0068169 100644 --- a/Editor/MapGeneratorTriggers/BuildPreProcessor.cs +++ b/Editor/MapGeneratorTriggers/BuildPreProcessor.cs @@ -10,28 +10,28 @@ internal class BuildPreProcessor : IPreprocessBuildWithReport public void OnPreprocessBuild(BuildReport report) { - if (!SettingsManager.SceneGuidToPathMap.IsGenerationTriggerEnabled(SceneGuidToPathMapGenerationTriggers.BeforeBuild)) + if (!SettingsManager.SceneDataMaps.IsGenerationTriggerEnabled(SceneDataMapsGeneratorTriggers.BeforeBuild)) { - Logger.Warn($"Skipping scene GUID to path map generation before build. It is recommended to enable map generation before build, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); + Logger.Warn($"Skipping scene data maps generation before build. It is recommended to enable map generation before build, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); return; } try { - SceneGuidToPathMapGenerator.Run(); + SceneDataMapsGenerator.Run(); } catch (Exception ex) { - if (SettingsManager.SceneGuidToPathMap.FailBuildIfGenerationFails.value) + if (SettingsManager.SceneDataMaps.FailBuildIfGenerationFails.value) { - Logger.Error($"Failing the build due to failure during scene GUID to path map generation. It is recommended to keep this behaviour enabled, as a failed map generation can result in broken scene references in runtime. However, if you know what you are doing, you can disable it in {SettingsManager.SettingsMenuPathForDisplay}."); + Logger.Error($"Failing the build due to failure during scene data maps generation. It is recommended to keep this behaviour enabled, as a failed map generation can result in broken scene references in runtime. However, if you know what you are doing, you can disable it in {SettingsManager.SettingsMenuPathForDisplay}."); // Only a BuildFailedException fails the build, so wrapping the original exception. throw new BuildFailedException(ex); } - Logger.Error($"Scene GUID to path map generation failed, but not failing the build. It is recommended to enable failing the build if the map generation fails, as a failed map generation can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); + Logger.Error($"Scene data maps generation failed, but not failing the build. It is recommended to enable failing the build if the map generation fails, as a failed map generation can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); // Only a BuildFailedException fails the build, therefore rethrowing does not. throw; diff --git a/Editor/MapGeneratorTriggers/PackagesChangeListener.cs b/Editor/MapGeneratorTriggers/PackagesChangeListener.cs new file mode 100644 index 00000000..1881c107 --- /dev/null +++ b/Editor/MapGeneratorTriggers/PackagesChangeListener.cs @@ -0,0 +1,28 @@ +using UnityEditor; +using UnityEditor.PackageManager; + +// TODO: run the generator only if there is a change in the packages we are interested in. + +namespace Eflatun.SceneReference.Editor.MapGeneratorTriggers +{ + [InitializeOnLoad] + internal static class PackagesChangeListener + { + static PackagesChangeListener() + { + UnityEditor.PackageManager.Events.registeredPackages += OnRegisteredPackages; + } + + private static void OnRegisteredPackages(PackageRegistrationEventArgs obj) + { + if (SettingsManager.SceneDataMaps.IsGenerationTriggerEnabled(SceneDataMapsGeneratorTriggers.AfterPackagesResolve)) + { + SceneDataMapsGenerator.Run(); + } + else + { + Logger.Warn($"Skipping scene data maps generation after packages resolve. It is recommended to enable map generation after packages resolve, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); + } + } + } +} diff --git a/Editor/MapGeneratorTriggers/PackagesChangeListener.cs.meta b/Editor/MapGeneratorTriggers/PackagesChangeListener.cs.meta new file mode 100644 index 00000000..0388b6a8 --- /dev/null +++ b/Editor/MapGeneratorTriggers/PackagesChangeListener.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f9f4f688838a40c2940d68030f697c80 +timeCreated: 1685900308 \ No newline at end of file diff --git a/Editor/MapGeneratorTriggers/PlayModeListener.cs b/Editor/MapGeneratorTriggers/PlayModeListener.cs index 6df3463d..b349e806 100644 --- a/Editor/MapGeneratorTriggers/PlayModeListener.cs +++ b/Editor/MapGeneratorTriggers/PlayModeListener.cs @@ -14,16 +14,16 @@ private static void EditorApplication_OnPlayModeStateChanged(PlayModeStateChange { if (state == PlayModeStateChange.ExitingEditMode) { - if (SettingsManager.SceneGuidToPathMap.IsGenerationTriggerEnabled(SceneGuidToPathMapGenerationTriggers.BeforeEnterPlayMode)) + if (SettingsManager.SceneDataMaps.IsGenerationTriggerEnabled(SceneDataMapsGeneratorTriggers.BeforeEnterPlayMode)) { - SceneGuidToPathMapGenerator.Run(); + SceneDataMapsGenerator.Run(); } else { // DESIGN: If 'Clear On Play' is enabled, this will get cleared before user gets a chance to see // it. It is easy to fix: just log in EnteredPlayMode. But that is overengineering and I don't want // to do it. - Logger.Warn($"Skipping scene GUID to path map generation before play mode. It is recommended to enable map generation before play mode, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); + Logger.Warn($"Skipping scene data maps generation before play mode. It is recommended to enable map generation before play mode, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); } } } diff --git a/Editor/MapGeneratorTriggers/SceneAssetPostprocessor.cs b/Editor/MapGeneratorTriggers/SceneAssetPostprocessor.cs index 2e6f6da3..a68aaa7a 100644 --- a/Editor/MapGeneratorTriggers/SceneAssetPostprocessor.cs +++ b/Editor/MapGeneratorTriggers/SceneAssetPostprocessor.cs @@ -16,13 +16,13 @@ private static void OnPostprocessAllAssets(string[] importedAssets, string[] del if (hasSceneChange) { - if (SettingsManager.SceneGuidToPathMap.IsGenerationTriggerEnabled(SceneGuidToPathMapGenerationTriggers.AfterSceneAssetChange)) + if (SettingsManager.SceneDataMaps.IsGenerationTriggerEnabled(SceneDataMapsGeneratorTriggers.AfterSceneAssetChange)) { - SceneGuidToPathMapGenerator.Run(); + SceneDataMapsGenerator.Run(); } else { - Logger.Warn($"Skipping scene GUID to path map generation after scene asset changes. It is recommended to enable map generation after scene asset changes, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); + Logger.Warn($"Skipping scene data maps generation after scene asset changes. It is recommended to enable map generation after scene asset changes, as an outdated map can result in broken scene references in runtime. You can enable it in {SettingsManager.SettingsMenuPathForDisplay}."); } } } diff --git a/Editor/SceneDataMapsGenerator.cs b/Editor/SceneDataMapsGenerator.cs new file mode 100644 index 00000000..07566e4c --- /dev/null +++ b/Editor/SceneDataMapsGenerator.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using JetBrains.Annotations; +using Newtonsoft.Json; +using UnityEditor; + +#if ESR_ADDRESSABLES +using UnityEditor.AddressableAssets; +#endif // ESR_ADDRESSABLES + +namespace Eflatun.SceneReference.Editor +{ + ///

+ /// Generates and writes the scene data maps. + /// + [PublicAPI] + public static class SceneDataMapsGenerator + { + private const string DotKeepFileContent = "Add this file to version control. See for explanation: https://stackoverflow.com/a/17929518/6301627"; + + /// + /// Runs the generator. + /// + /// + /// The menu item "Tools/Eflatun/Scene Reference/Generate Scene Data Maps" executes this method. + /// + [MenuItem("Tools/" + Constants.MenuPrefixBase + "/Generate Scene Data Maps", priority = -3130)] + public static void Run() + { + try + { + Logger.Debug("Generating scene data maps."); + + WriteScaffolding(); + + var allSceneGuids = AssetDatabase.FindAssets("t:Scene"); + + var sceneGuidToPathMap = GenerateSceneGuidToPathMap(allSceneGuids); + WriteSceneGuidToPathMap(sceneGuidToPathMap); + + var sceneGuidToAddressMap = GenerateSceneGuidToAddressMap(allSceneGuids); + WriteSceneGuidToAddressMap(sceneGuidToAddressMap); + } + finally + { + AssetDatabase.Refresh(); + } + } + + private static void WriteScaffolding() + { + Directory.CreateDirectory(Paths.Absolute.SceneDataMapsFolder.PlatformPath); + File.WriteAllText(Paths.Absolute.SceneDataMapsDotKeepFile.PlatformPath, DotKeepFileContent); + } + + private static Dictionary GenerateSceneGuidToPathMap(string[] allSceneGuids) + { + var sceneGuidToPathMap = allSceneGuids.ToDictionary( + x => x, // key generator: take guids + AssetDatabase.GUIDToAssetPath // value generator: take paths + ); + return sceneGuidToPathMap; + } + + private static void WriteSceneGuidToPathMap(Dictionary sceneGuidToPathMap) + { + var jsonRaw = JsonConvert.SerializeObject(sceneGuidToPathMap, SettingsManager.SceneDataMaps.JsonFormatting.value); + File.WriteAllText(Paths.Absolute.SceneGuidToPathMapFile.PlatformPath, jsonRaw); + + SceneGuidToPathMapProvider.DirectAssign(sceneGuidToPathMap); + } + + private static Dictionary GenerateSceneGuidToAddressMap(string[] allSceneGuids) + { +#if ESR_ADDRESSABLES + var addressableSettings = AddressableAssetSettingsDefaultObject.Settings; + + var addressableSceneAssetEntries = allSceneGuids + .Select(addressableSettings.FindAssetEntry) + .Where(x => x != null); + + var sceneGuidToAddressMap = addressableSceneAssetEntries.ToDictionary( + x => x.guid, // key generator: take guids + x => x.address // value generator: take addresses + ); + + return sceneGuidToAddressMap; +#else // ESR_ADDRESSABLES + return new Dictionary(); +#endif // ESR_ADDRESSABLES + } + + private static void WriteSceneGuidToAddressMap(Dictionary sceneGuidToAddressMap) + { + var jsonRaw = JsonConvert.SerializeObject(sceneGuidToAddressMap, SettingsManager.SceneDataMaps.JsonFormatting.value); + File.WriteAllText(Paths.Absolute.SceneGuidToAddressMapFile.PlatformPath, jsonRaw); + + SceneGuidToAddressMapProvider.DirectAssign(sceneGuidToAddressMap); + } + } +} diff --git a/Editor/SceneGuidToPathMapGenerator.cs.meta b/Editor/SceneDataMapsGenerator.cs.meta similarity index 100% rename from Editor/SceneGuidToPathMapGenerator.cs.meta rename to Editor/SceneDataMapsGenerator.cs.meta diff --git a/Editor/SceneGuidToPathMapGenerationTriggers.cs b/Editor/SceneDataMapsGeneratorTriggers.cs similarity index 59% rename from Editor/SceneGuidToPathMapGenerationTriggers.cs rename to Editor/SceneDataMapsGeneratorTriggers.cs index 0adc298f..6c513917 100644 --- a/Editor/SceneGuidToPathMapGenerationTriggers.cs +++ b/Editor/SceneDataMapsGeneratorTriggers.cs @@ -4,17 +4,19 @@ namespace Eflatun.SceneReference.Editor { /// - /// Identifiers for places that perform scene GUID to path map generation generation. + /// Identifiers for places that trigger . /// [PublicAPI] [Flags] - public enum SceneGuidToPathMapGenerationTriggers + public enum SceneDataMapsGeneratorTriggers { None = 0, All = ~0, - + AfterSceneAssetChange = 1 << 0, BeforeEnterPlayMode = 1 << 1, BeforeBuild = 1 << 2, + AfterPackagesResolve = 1 << 3, + AfterAddressablesChange = 1 << 4, } } diff --git a/Editor/SceneGuidToPathMapGenerationTriggers.cs.meta b/Editor/SceneDataMapsGeneratorTriggers.cs.meta similarity index 100% rename from Editor/SceneGuidToPathMapGenerationTriggers.cs.meta rename to Editor/SceneDataMapsGeneratorTriggers.cs.meta diff --git a/Editor/SceneGuidToPathMapGenerator.cs b/Editor/SceneGuidToPathMapGenerator.cs deleted file mode 100644 index 1f981ea2..00000000 --- a/Editor/SceneGuidToPathMapGenerator.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.IO; -using System.Linq; -using JetBrains.Annotations; -using Newtonsoft.Json; -using UnityEditor; - -namespace Eflatun.SceneReference.Editor -{ - /// - /// Generates and writes the scene GUID to path map. - /// - [PublicAPI] - public static class SceneGuidToPathMapGenerator - { - /// - /// Runs the generator. - /// - /// - /// The menu item "Tools/Eflatun/Scene Reference/Run Scene GUID to Path Map Generator" executes this method. - /// - [MenuItem("Tools/" + Constants.MenuPrefixBase + "/Run Scene GUID to Path Map Generator", priority = -3130)] - public static void Run() - { - const string dotKeepFileContent = "Add this file to version control. See for explanation: https://stackoverflow.com/a/17929518/6301627"; - - Logger.Debug("Generating scene GUID to path map."); - - try - { - var allSceneGuids = AssetDatabase.FindAssets("t:Scene"); - var sceneGuidToPath = allSceneGuids.ToDictionary( - x => x, // key generator: take guids - AssetDatabase.GUIDToAssetPath // value generator: take paths - ); - - var jsonRaw = JsonConvert.SerializeObject(sceneGuidToPath, SettingsManager.SceneGuidToPathMap.JsonFormatting.value); - - Directory.CreateDirectory(Paths.Absolute.SceneGuidToPathMapFolder.PlatformPath); - File.WriteAllText(Paths.Absolute.SceneGuidToPathMapDotKeepFile.PlatformPath, dotKeepFileContent); - File.WriteAllText(Paths.Absolute.SceneGuidToPathMapFile.PlatformPath, jsonRaw); - - SceneGuidToPathMapProvider.DirectAssign(sceneGuidToPath); - } - finally - { - AssetDatabase.Refresh(); - } - } - } -} diff --git a/Editor/SceneReferencePropertyDrawer.cs b/Editor/SceneReferencePropertyDrawer.cs index bbae5eb2..4df4b078 100644 --- a/Editor/SceneReferencePropertyDrawer.cs +++ b/Editor/SceneReferencePropertyDrawer.cs @@ -1,11 +1,17 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; -using Eflatun.SceneReference.Editor.Utility; +using Eflatun.SceneReference.Editor.Toolbox; using JetBrains.Annotations; using UnityEditor; using UnityEngine; +#if ESR_ADDRESSABLES +using UnityEditor.AddressableAssets; +using UnityEditor.AddressableAssets.Settings; +#endif // ESR_ADDRESSABLES + namespace Eflatun.SceneReference.Editor { /// @@ -17,12 +23,13 @@ public class SceneReferencePropertyDrawer : PropertyDrawer { private static readonly SceneReferenceOptionsAttribute DefaultOptionsAttribute = new SceneReferenceOptionsAttribute(); - private enum SceneBuildSettingsState + private enum SceneBundlingState { - None, - NotIncluded, - Disabled, - Enabled + NoScene = 0, + Nowhere = 1, + InBuildDisabled = 2, + InBuildEnabled = 3, + Addressable = 4, } private SerializedProperty _assetSerializedProperty; @@ -31,27 +38,36 @@ private enum SceneBuildSettingsState private UnityEngine.Object _asset; private string _guid; private string _path; - private EditorBuildSettingsScene _sceneInBuildSettings; - private SceneBuildSettingsState _sceneBuildSettingsState; + private EditorBuildSettingsScene _buildEntry; + private SceneBundlingState _bundlingState; private SceneReferenceOptionsAttribute _optionsAttribute; - private bool NeedsBuildSettingsFix => _sceneBuildSettingsState == SceneBuildSettingsState.Disabled - || _sceneBuildSettingsState == SceneBuildSettingsState.NotIncluded; +#if ESR_ADDRESSABLES + private AddressableAssetEntry _addressableEntry; +#endif // ESR_ADDRESSABLES - private bool IsColoringEnabled => _optionsAttribute.Coloring switch + private bool ShouldColorSceneInBuild => _optionsAttribute.SceneInBuildColoring switch { ColoringBehaviour.Enabled => true, ColoringBehaviour.Disabled => false, ColoringBehaviour.DoNotOverride => SettingsManager.PropertyDrawer.ColorBasedOnSceneInBuildState.value, - _ => throw new ArgumentOutOfRangeException(nameof(_optionsAttribute.Coloring), _optionsAttribute.Coloring, null) + _ => throw new ArgumentOutOfRangeException(nameof(_optionsAttribute.SceneInBuildColoring), _optionsAttribute.SceneInBuildColoring, null) }; - private bool IsUtilityLineEnabled => _optionsAttribute.UtilityLine switch + private bool ShouldColorAddressable => _optionsAttribute.AddressableColoring switch { - UtilityLineBehaviour.Enabled => true, - UtilityLineBehaviour.Disabled => false, - UtilityLineBehaviour.DoNotOverride => SettingsManager.PropertyDrawer.ShowInlineSceneInBuildUtility.value, - _ => throw new ArgumentOutOfRangeException(nameof(_optionsAttribute.UtilityLine), _optionsAttribute.UtilityLine, null) + ColoringBehaviour.Enabled => true, + ColoringBehaviour.Disabled => false, + ColoringBehaviour.DoNotOverride => SettingsManager.AddressablesSupport.ColorAddressableScenes.value, + _ => throw new ArgumentOutOfRangeException(nameof(_optionsAttribute.AddressableColoring), _optionsAttribute.AddressableColoring, null) + }; + + private bool ShouldShowToolbox => _optionsAttribute.Toolbox switch + { + ToolboxBehaviour.Enabled => true, + ToolboxBehaviour.Disabled => false, + ToolboxBehaviour.DoNotOverride => SettingsManager.PropertyDrawer.ShowInlineToolbox.value, + _ => throw new ArgumentOutOfRangeException(nameof(_optionsAttribute.Toolbox), _optionsAttribute.Toolbox, null) }; private void Init(SerializedProperty property) @@ -62,25 +78,35 @@ private void Init(SerializedProperty property) _asset = _assetSerializedProperty.objectReferenceValue; _guid = _guidSerializedProperty.stringValue; _path = AssetDatabase.GetAssetPath(_asset); - _sceneInBuildSettings = EditorBuildSettings.scenes.FirstOrDefault(x => x.guid.ToString() == _guid); + _buildEntry = EditorBuildSettings.scenes.FirstOrDefault(x => x.guid.ToString() == _guid); + +#if ESR_ADDRESSABLES + _addressableEntry = AddressableAssetSettingsDefaultObject.Settings.FindAssetEntry(_guid); +#endif // ESR_ADDRESSABLES _optionsAttribute = fieldInfo.GetCustomAttribute(false) ?? DefaultOptionsAttribute; if (_asset == null) { - _sceneBuildSettingsState = SceneBuildSettingsState.None; + _bundlingState = SceneBundlingState.NoScene; } - else if (_sceneInBuildSettings == null) +#if ESR_ADDRESSABLES + else if (_addressableEntry != null) { - _sceneBuildSettingsState = SceneBuildSettingsState.NotIncluded; + _bundlingState = SceneBundlingState.Addressable; } - else if (!_sceneInBuildSettings.enabled) +#endif // ESR_ADDRESSABLES + else if (_buildEntry == null) { - _sceneBuildSettingsState = SceneBuildSettingsState.Disabled; + _bundlingState = SceneBundlingState.Nowhere; + } + else if (!_buildEntry.enabled) + { + _bundlingState = SceneBundlingState.InBuildDisabled; } else { - _sceneBuildSettingsState = SceneBuildSettingsState.Enabled; + _bundlingState = SceneBundlingState.InBuildEnabled; } } @@ -98,68 +124,6 @@ private void SetWith(UnityEngine.Object newAsset) _guidSerializedProperty.stringValue = newGuid.ToString(); } - private void FixInBuildSettings() - { - var changed = false; - var tempScenes = EditorBuildSettings.scenes.ToList(); - - if (_sceneBuildSettingsState == SceneBuildSettingsState.NotIncluded) - { - var title = "Add Scene to Build Settings?"; - var body = $"Would you like to add the following scene to build settings?\n\n{_path}"; - - switch (EditorUtility.DisplayDialogComplex(title, body, "Add to Build", "Cancel", "Open Build Settings")) - { - case 0: - { - tempScenes.Add(new EditorBuildSettingsScene(_path, true)); - changed = true; - break; - } - case 1: - { - // 1 is cancel - break; - } - case 2: - { - PingAssetAndOpenBuildSettings(); - break; - } - } - } - else if (_sceneBuildSettingsState == SceneBuildSettingsState.Disabled) - { - var title = "Enable Scene in Build Settings?"; - var body = $"Would you like to enable the following scene in build settings?\n\n{_path}"; - - switch (EditorUtility.DisplayDialogComplex(title, body, "Enable in Build", "Cancel", "Open Build Settings")) - { - case 0: - { - tempScenes.Single(x => x.guid.ToString() == _guid).enabled = true; - changed = true; - break; - } - case 1: - { - // 1 is cancel - break; - } - case 2: - { - PingAssetAndOpenBuildSettings(); - break; - } - } - } - - if (changed) - { - EditorBuildSettings.scenes = tempScenes.ToArray(); - } - } - public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { Init(property); @@ -167,47 +131,44 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten EditorGUI.BeginProperty(position, GUIContent.none, property); var colorToRestore = GUI.color; - - if (IsColoringEnabled) + GUI.color = _bundlingState switch { - GUI.color = _sceneBuildSettingsState switch - { - SceneBuildSettingsState.NotIncluded => Color.red, - SceneBuildSettingsState.Disabled => Color.yellow, - _ => colorToRestore - }; - } + SceneBundlingState.Nowhere when ShouldColorSceneInBuild => Color.red, + SceneBundlingState.InBuildDisabled when ShouldColorSceneInBuild => Color.yellow, + SceneBundlingState.Addressable when ShouldColorAddressable => Color.cyan, + _ => colorToRestore + }; // draw scene asset selector + var toolboxButtonWidth = EditorGUIUtility.singleLineHeight; var selectorFieldRect = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); selectorFieldRect.height = EditorGUIUtility.singleLineHeight; + + if (ShouldShowToolbox) + { + selectorFieldRect.width -= toolboxButtonWidth + 2; + } + var newAsset = EditorGUI.ObjectField(selectorFieldRect, _asset, typeof(SceneAsset), false); SetWith(newAsset); - // draw utility line if needed - if (IsUtilityLineEnabled && NeedsBuildSettingsFix) + if (ShouldShowToolbox) { - var buttonRect = new Rect(position) - { - // x = selectorFieldRect.x, - y = position.y + EditorGUIUtility.singleLineHeight, - // width = selectorFieldRect.width, - height = EditorGUIUtility.singleLineHeight - }; - - var buttonText = _sceneBuildSettingsState switch + var toolboxButtonRect = new Rect { - SceneBuildSettingsState.NotIncluded => "Add to Build...", - SceneBuildSettingsState.Disabled => "Enable in Build...", - _ => throw new ArgumentOutOfRangeException(nameof(_sceneBuildSettingsState), _sceneBuildSettingsState, null) + x = selectorFieldRect.x + selectorFieldRect.width + 2, + width = toolboxButtonWidth, + y = selectorFieldRect.y + 1, + height = selectorFieldRect.height - 1 }; - if (GUI.Button(buttonRect, buttonText)) + // TODO: we should ship our own icon to prevent this breaking in the future + var settingsIcon = EditorGUIUtility.IconContent("SettingsIcon"); + var toolboxButton = GUI.Button(toolboxButtonRect, settingsIcon, EditorStyles.iconButton); + if (toolboxButton) { - FixInBuildSettings(); - - // This prevents Unity from throwing 'InvalidOperationException: Stack empty.' at 'EditorGUI.EndProperty'. - GUIUtility.ExitGUI(); + var toolboxPopupWindow = CreateToolboxPopupWindow(); + PopupWindow.Show(toolboxButtonRect, toolboxPopupWindow); } } @@ -217,17 +178,31 @@ public override void OnGUI(Rect position, SerializedProperty property, GUIConten public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { - Init(property); - - return _asset && IsUtilityLineEnabled && NeedsBuildSettingsFix - ? EditorGUIUtility.singleLineHeight * 2 - : EditorGUIUtility.singleLineHeight; + return EditorGUIUtility.singleLineHeight; } - private void PingAssetAndOpenBuildSettings() + private ToolboxPopupWindow CreateToolboxPopupWindow() { - EditorGUIUtility.PingObject(_asset); - EditorWindow.GetWindow(); + var tools = new List(); + + if (_bundlingState == SceneBundlingState.Nowhere) + { + tools.Add(new AddToBuildTool(_path, _asset)); + } + + if (_bundlingState == SceneBundlingState.InBuildDisabled) + { + tools.Add(new EnableInBuildTool(_path, _guid, _asset)); + } + +#if ESR_ADDRESSABLES + if(_bundlingState == SceneBundlingState.Nowhere || _bundlingState == SceneBundlingState.InBuildDisabled) + { + tools.Add(new MakeAddressableTool(_path, _guid, _asset)); + } +#endif // ESR_ADDRESSABLES + + return new ToolboxPopupWindow(tools); } } } diff --git a/Editor/SettingsManager.cs b/Editor/SettingsManager.cs index 29a48819..22ced7dd 100644 --- a/Editor/SettingsManager.cs +++ b/Editor/SettingsManager.cs @@ -1,4 +1,5 @@ using System.Reflection; +using Eflatun.SceneReference.Editor.Utility; using Eflatun.SceneReference.Utility; using JetBrains.Annotations; using UnityEditor; @@ -16,7 +17,7 @@ namespace Eflatun.SceneReference.Editor [PublicAPI] public static class SettingsManager { - /// + /// public class ProjectSetting : UserSetting { /// @@ -26,7 +27,7 @@ internal ProjectSetting(string key, T value) : base(Settings, key, value, Settin } private const string SettingsMenuPath = "Project/" + Constants.MenuPrefixBase; - internal const string SettingsMenuPathForDisplay = "Project Settings/" + SettingsMenuPath; + internal const string SettingsMenuPathForDisplay = "Project Settings/" + Constants.MenuPrefixBase; private static readonly Assembly ContainingAssembly = typeof(SettingsManager).Assembly; private static readonly Settings Settings = new Settings(Constants.PackageNameReverseDomain); @@ -35,51 +36,59 @@ internal ProjectSetting(string key, T value) : base(Settings, key, value, Settin private static SettingsProvider CreateUserSettingsProvider() => new UserSettingsProvider(SettingsMenuPath, Settings, new[] { ContainingAssembly }, SettingsScope.Project); /// - /// Settings regarding the scene GUID to path map. + /// Settings regarding the scene data maps and . /// /// [PublicAPI] - public static class SceneGuidToPathMap + public static class SceneDataMaps { - private const string CategoryName = "Scene GUID To Path Map"; + private const string CategoryName = "Scene Data Maps"; /// - /// Controls when the scene GUID to path map gets regenerated.
- /// • After Scene Asset Change: Regenerate the map every time a scene asset changes (delete, create, move, rename).
- /// • Before Enter Play Mode: Regenerate the map before entering play mode in the editor.
- /// • Before Build: Regenerate the map before a build.
- /// It is recommended that you leave this option at 'All' unless you are debugging something. Failure to generate the map when needed can result in broken scene references in runtime. + /// Controls when the scene data maps get regenerated.
+ /// + /// After Scene Asset Change: Regenerate the maps every time a scene asset changes (delete, create, move, rename). + /// Before Enter Play Mode: Regenerate the maps before entering play mode in the editor. + /// Before Build: Regenerate the maps before a build. + /// After Packages Resolve: Regenerate the maps after UPM packages are resolved. + /// After Addressables Change: Regenerate the maps after addressable group entries change. Only relevant if you have addressables support enabled. + /// + /// It is recommended that you leave this option at All unless you are debugging something. Failure to generate the maps when needed can result in broken scene references in runtime. ///
- /// + /// + /// All and Everything are the same thing. They both represent all triggers.

+ /// + /// /// - [field: UserSetting(CategoryName, "Generation Triggers", "Controls when the scene GUID to path map gets regenerated.\n\n• After Scene Asset Change: Regenerate the map every time a scene asset changes (delete, create, move, rename).\n\n• Before Enter Play Mode: Regenerate the map before entering play mode in the editor.\n\n• Before Build: Regenerate the map before a build.\n\nIt is recommended that you leave this option at 'All' unless you are debugging something. Failure to generate the map when needed can result in broken scene references in runtime.")] - public static ProjectSetting GenerationTriggers { get; } - = new ProjectSetting("SceneGuidToPathMap.GenerationTriggers", SceneGuidToPathMapGenerationTriggers.All); + [field: UserSetting(CategoryName, "Generation Triggers", "Controls when the scene data maps get regenerated.\n\n• AAfter Scene Asset Change: Regenerate the maps every time a scene asset changes (delete, create, move, rename).\n\n• Before Enter Play Mode: Regenerate the maps before entering play mode in the editor.\n\n• Before Build: Regenerate the maps before a build.\n\n• After Packages Resolve: Regenerate the maps after UPM packages are resolved.\n\n• After Addressables Change: Regenerate the maps after addressable group entries change. Only relevant if you have addressables support enabled.\n\nIt is recommended that you leave this option at 'All' unless you are debugging something. Failure to generate the maps when needed can result in broken scene references in runtime.\n\nNote: 'All' and 'Everything' are the same thing. They both represent all triggers.")] + public static ProjectSetting GenerationTriggers { get; } + = new ProjectSetting("SceneDataMaps.GenerationTriggers", SceneDataMapsGeneratorTriggers.All); ///

- /// Controls the scene GUID to path map generator's JSON formatting.
- /// It is recommended to leave this option at 'Indented', as it will help with version control and make the generated file human-readable. + /// Controls the Scene Data Maps Generator's JSON formatting.
+ /// It is recommended to leave this option at Indented, as it will help with version control and make the generated files human-readable. ///
/// - [field: UserSetting(CategoryName, "JSON Formatting", "Controls the scene GUID to path map generator's JSON formatting.\n\nIt is recommended to leave this option at 'Indented', as it will help with version control and make the generated file human-readable.")] + [field: UserSetting(CategoryName, "JSON Formatting", "Controls the Scene Data Maps Generator's JSON formatting.\n\nIt is recommended to leave this option at _Indented_, as it will help with version control and make the generated files human-readable.")] public static ProjectSetting JsonFormatting { get; } - = new ProjectSetting("SceneGuidToPathMap.JsonFormatting", Formatting.Indented); + = new ProjectSetting("SceneDataMaps.JsonFormatting", Formatting.Indented); /// - /// Should we fail a build if scene GUID to path map generation fails?
- /// Only relevant if 'Before Build' generation trigger is enabled.
- /// It is recommended to leave this option at 'true', as a failed map generation can result in broken scene references in runtime. + /// Should we fail a build if scene data maps generation fails?
+ /// Only relevant if Before Build generation trigger is enabled.
+ /// It is recommended to leave this option at true, as a failed map generation can result in broken scene references in runtime. ///
/// - [field: UserSetting(CategoryName, "Fail Build If Generation Fails", "Should we fail a build if scene GUID to path map generation fails?\n\nOnly relevant if 'Before Build' generation trigger is enabled.\n\nIt is recommended to leave this option at 'true', as a failed map generation can result in broken scene references in runtime.")] + [field: UserSetting(CategoryName, "Fail Build If Generation Fails", "Should we fail a build if scene data maps generation fails?\n\nOnly relevant if 'Before Build' generation trigger is enabled.\n\nIt is recommended to leave this option at _true_, as a failed map generation can result in broken scene references in runtime.")] public static ProjectSetting FailBuildIfGenerationFails { get; } - = new ProjectSetting("SceneGuidToPathMap.FailBuildIfGenerationFails", true); + = new ProjectSetting("SceneDataMaps.FailBuildIfGenerationFails", true); /// /// Returns whether the given is enabled in the settings. /// + /// This is a utility method for code access, it is not part of the settings data. /// - public static bool IsGenerationTriggerEnabled(SceneGuidToPathMapGenerationTriggers trigger) => + public static bool IsGenerationTriggerEnabled(SceneDataMapsGeneratorTriggers trigger) => GenerationTriggers.value.IncludesFlag(trigger); } @@ -93,24 +102,66 @@ public static class PropertyDrawer private const string CategoryName = "Property Drawer"; /// - /// Should we show the inline utility that allows you to quickly fix scenes that are either not in build or disabled in build?
- /// Unity only bundles scenes that are added and enabled in build settings. Therefore, you would want to make sure the scene you assign to a SceneReference is added and enabled in build settings.
- /// It is recommended to leave this option at 'true', as the inline utility saves you a lot of time. + /// Should we show the inline gear (⚙️) button that opens a toolbox?
+ /// Unity only bundles scenes that are added and enabled in build settings, and addressables only pack scenes that are in an Addressable Group. Therefore, you would want to make sure the scene you assign to a SceneReference is either added and enabled in build settings, or is in an addressable group. The toolbox provides tools for you to quickly take action in these cases.
+ /// It is recommended to leave this option enabled, as the toolbox saves you a lot of time. ///
/// - [field: UserSetting(CategoryName, "Show Inline Scene-In-Build Utility", "Should we show the inline utility that allows you to quickly fix scenes that are either not in build or disabled in build?\n\nUnity only bundles scenes that are added and enabled in build settings. Therefore, you would want to make sure the scene you assign to a SceneReference is added and enabled in build settings.\n\nIt is recommended to leave this option at 'true', as the inline utility saves you a lot of time.")] - public static ProjectSetting ShowInlineSceneInBuildUtility { get; } - = new ProjectSetting("PropertyDrawer.ShowInlineSceneInBuildUtility", true); + [field: UserSetting(CategoryName, "Show Inline Toolbox", "Should we show the inline gear button that opens a toolbox?\n\nUnity only bundles scenes that are added and enabled in build settings, and addressables only pack scenes that are in an Addressable Group. Therefore, you would want to make sure the scene you assign to a SceneReference is either added and enabled in build settings, or is in an addressable group. The toolbox provides tools for you to quickly take action in these cases.\n\nIt is recommended to leave this option enabled, as the toolbox saves you a lot of time.")] + public static ProjectSetting ShowInlineToolbox { get; } + = new ProjectSetting("PropertyDrawer.ShowInlineToolbox", true); /// /// Should we color the property to draw attention for scenes that are either not in build or disabled in build?
/// Unity only bundles scenes that are added and enabled in build settings. Therefore, you would want to validate whether the scene you assign to a SceneReference is added and enabled in build settings.
/// It is recommended to leave this option at 'true', as it will help you identify many potential runtime errors. ///
- /// - [field: UserSetting(CategoryName, "Color Based On Scene-In-Build State", "Should we color the property to draw attention for scenes that are either not in build or disabled in build?\n\nUnity only bundles scenes that are added and enabled in build settings. Therefore, you would want to validate whether the scene you assign to a SceneReference is added and enabled in build settings.\n\nIt is recommended to leave this option at 'true', as it will help you identify many potential runtime errors.")] + /// + /// This setting does not apply to addressable scenes. They have their own coloring mechanism. It is controlled by the _Color Addressable Scenes_ setting under the _Addressables Support_ category.
+ /// + ///
+ [field: UserSetting(CategoryName, "Color Based On Scene-In-Build State", "Should we color the property to draw attention for scenes that are either not in build or disabled in build?\n\nUnity only bundles scenes that are added and enabled in build settings. Therefore, you would want to validate whether the scene you assign to a SceneReference is added and enabled in build settings.\n\nIt is recommended to leave this option at 'true', as it will help you identify many potential runtime errors.\n\nNote: This setting does not apply to addressable scenes. They have their own coloring mechanism. It is controlled by the 'Color Addressable Scenes' setting under the 'Addressables Support' category.")] public static ProjectSetting ColorBasedOnSceneInBuildState { get; } = new ProjectSetting("PropertyDrawer.ColorBasedOnSceneInBuildState", true); } + + /// + /// Settings regarding addressables support. + /// + /// + /// Settings under this category are only relevant if you have addressables support enabled.
+ /// + ///
+ [PublicAPI] + public static class AddressablesSupport + { + private const string CategoryName = "Addressables Support"; + + /// + /// Should we color the property to draw attention for addressable scenes?
+ /// Addressable scenes should be handled differently than regular scenes in runtime, through the addressables API. Therefore, you would want quickly identify if a references an addressable scene or not.
+ /// It is recommended to leave this option at true, as it will help you easily distinguish addressable scenes. + ///
+ /// + /// This setting does not apply to regular scenes. They have their own coloring mechanism. It is controlled by the _Color Based On Scene-In-Build State_ setting under the _Property Drawer_ category.
+ /// + ///
+ [field: UserSetting(CategoryName, "Color Addressable Scenes", "Should we color the property to draw attention for addressable scenes?\n\nAddressable scenes should be handled differently than regular scenes in runtime, through the addressables API. Therefore, you would want quickly identify if a SceneReference references an addressable scene or not.\n\nIt is recommended to leave this option at 'true', as it will help you easily distinguish addressable scenes.\n\nNote: This setting does not apply to regular scenes. They have their own coloring mechanism. It is controlled by the 'Color Based On Scene-In-Build State' setting under the 'Property Drawer' category.")] + public static ProjectSetting ColorAddressableScenes { get; } + = new ProjectSetting("AddressablesSupport.ColorAddressableScenes", true); + + [UserSettingBlock(CategoryName)] + private static void Draw(string searchContext) + { + if (!EditorUtils.IsAddressablesPackagePresent) + { + EditorGUILayout.HelpBox("Addressables support is disabled because the addressables package is not installed in the project. Addressables support will be enabled automatically when the addressables package is installed in the project.", MessageType.Warning); + } + else + { + EditorGUILayout.HelpBox("Addressables support is enabled.", MessageType.Info); + } + } + } } } diff --git a/Editor/Toolbox.meta b/Editor/Toolbox.meta new file mode 100644 index 00000000..e6be38e8 --- /dev/null +++ b/Editor/Toolbox.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ef89616219864850a65ecc837e4875dd +timeCreated: 1688296222 \ No newline at end of file diff --git a/Editor/Toolbox/AddToBuildTool.cs b/Editor/Toolbox/AddToBuildTool.cs new file mode 100644 index 00000000..9bbdc704 --- /dev/null +++ b/Editor/Toolbox/AddToBuildTool.cs @@ -0,0 +1,65 @@ +using System; +using Eflatun.SceneReference.Editor.Utility; +using UnityEditor; +using UnityEngine; + +namespace Eflatun.SceneReference.Editor.Toolbox +{ + /// + /// Before: Not in build and not addressable.
+ /// After: In build and enabled. + ///
+ internal class AddToBuildTool : ITool + { + private readonly string _scenePath; + private readonly UnityEngine.Object _sceneAsset; + + public AddToBuildTool(string scenePath, UnityEngine.Object sceneAsset) + { + _scenePath = scenePath; + _sceneAsset = sceneAsset; + } + + public void Draw(Action closeToolbox) + { + if (GUILayout.Button("Add to build...", EditorStyles.toolbarButton)) + { + // Prevents an editor crash. https://issuetracker.unity3d.com/issues/crash-on-guiview-oninputevent-when-opening-an-editorwindow-from-a-genericmenu-in-a-popup-window + closeToolbox.Invoke(); + + Perform(); + } + } + + public float GetHeight() + { + return EditorStyles.toolbarButton.fixedHeight; + } + + private void Perform() + { + var title = "Add Scene to Build Settings?"; + var body = $"Would you like to add the following scene to build settings?\n\n{_scenePath}"; + + switch (EditorUtility.DisplayDialogComplex(title, body, "Add to Build", "Cancel", "Open Build Settings")) + { + case 0: + { + EditorUtils.AddSceneToBuild(_scenePath); + break; + } + case 1: + { + // 1 is cancel + break; + } + case 2: + { + EditorGUIUtility.PingObject(_sceneAsset); + EditorWindow.GetWindow(); + break; + } + } + } + } +} diff --git a/Editor/Toolbox/AddToBuildTool.cs.meta b/Editor/Toolbox/AddToBuildTool.cs.meta new file mode 100644 index 00000000..fbbdfe49 --- /dev/null +++ b/Editor/Toolbox/AddToBuildTool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 332a6a5944cc46a1b3ff41834a396edb +timeCreated: 1688296776 \ No newline at end of file diff --git a/Editor/Toolbox/EnableInBuildTool.cs b/Editor/Toolbox/EnableInBuildTool.cs new file mode 100644 index 00000000..9dac3e8b --- /dev/null +++ b/Editor/Toolbox/EnableInBuildTool.cs @@ -0,0 +1,68 @@ +using System; +using Eflatun.SceneReference.Editor.Utility; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace Eflatun.SceneReference.Editor.Toolbox +{ + /// + /// Before: In build and disabled.
+ /// After: In build and enabled. + ///
+ internal class EnableInBuildTool : ITool + { + private readonly string _scenePath; + private readonly string _sceneGuid; + private readonly Object _sceneAsset; + + public EnableInBuildTool(string scenePath, string sceneGuid, UnityEngine.Object sceneAsset) + { + _scenePath = scenePath; + _sceneGuid = sceneGuid; + _sceneAsset = sceneAsset; + } + + public void Draw(Action closeToolbox) + { + if (GUILayout.Button("Enable in build...", EditorStyles.toolbarButton)) + { + // Prevents an editor crash. https://issuetracker.unity3d.com/issues/crash-on-guiview-oninputevent-when-opening-an-editorwindow-from-a-genericmenu-in-a-popup-window + closeToolbox.Invoke(); + + Perform(); + } + } + + public float GetHeight() + { + return EditorStyles.toolbarButton.fixedHeight; + } + + private void Perform() + { + var title = "Enable Scene in Build Settings?"; + var body = $"Would you like to enable the following scene in build settings?\n\n{_scenePath}"; + + switch (EditorUtility.DisplayDialogComplex(title, body, "Enable in Build", "Cancel", "Open Build Settings")) + { + case 0: + { + EditorUtils.EnableSceneInBuild(_sceneGuid); + break; + } + case 1: + { + // 1 is cancel + break; + } + case 2: + { + EditorGUIUtility.PingObject(_sceneAsset); + EditorWindow.GetWindow(); + break; + } + } + } + } +} diff --git a/Editor/Toolbox/EnableInBuildTool.cs.meta b/Editor/Toolbox/EnableInBuildTool.cs.meta new file mode 100644 index 00000000..824b7dfc --- /dev/null +++ b/Editor/Toolbox/EnableInBuildTool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: e21ca1739e45483cb807456e4ccb2767 +timeCreated: 1688296861 \ No newline at end of file diff --git a/Editor/Toolbox/ITool.cs b/Editor/Toolbox/ITool.cs new file mode 100644 index 00000000..a479e496 --- /dev/null +++ b/Editor/Toolbox/ITool.cs @@ -0,0 +1,10 @@ +using System; + +namespace Eflatun.SceneReference.Editor.Toolbox +{ + internal interface ITool + { + void Draw(Action closeToolbox); + float GetHeight(); + } +} diff --git a/Editor/Toolbox/ITool.cs.meta b/Editor/Toolbox/ITool.cs.meta new file mode 100644 index 00000000..78d47b21 --- /dev/null +++ b/Editor/Toolbox/ITool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 17cba5d0fe954646aaf8569739ee6395 +timeCreated: 1688296236 \ No newline at end of file diff --git a/Editor/Toolbox/MakeAddressableTool.cs b/Editor/Toolbox/MakeAddressableTool.cs new file mode 100644 index 00000000..28242e38 --- /dev/null +++ b/Editor/Toolbox/MakeAddressableTool.cs @@ -0,0 +1,71 @@ +#if ESR_ADDRESSABLES + +using System; +using Eflatun.SceneReference.Editor.Utility; +using UnityEditor; +using UnityEngine; + +namespace Eflatun.SceneReference.Editor.Toolbox +{ + /// + /// Before: Not in build and not addressable.
+ /// After: Addressable. + ///
+ internal class MakeAddressableTool : ITool + { + private readonly string _scenePath; + private readonly string _sceneGuid; + private readonly UnityEngine.Object _sceneAsset; + + public MakeAddressableTool(string scenePath, string sceneGuid, UnityEngine.Object sceneAsset) + { + _scenePath = scenePath; + _sceneGuid = sceneGuid; + _sceneAsset = sceneAsset; + } + + public void Draw(Action closeToolbox) + { + if (GUILayout.Button("Make addressable...", EditorStyles.toolbarButton)) + { + // Prevents an editor crash. https://issuetracker.unity3d.com/issues/crash-on-guiview-oninputevent-when-opening-an-editorwindow-from-a-genericmenu-in-a-popup-window + closeToolbox.Invoke(); + + Perform(); + } + } + + public float GetHeight() + { + return EditorStyles.toolbarButton.fixedHeight; + } + + private void Perform() + { + var title = "Make the Scene Addressable?"; + var body = $"Would you like to make the following scene addressable?\n\n{_scenePath}"; + + switch (EditorUtility.DisplayDialogComplex(title, body, "Add to Default Group", "Cancel", "Open Addressable Groups Window")) + { + case 0: + { + EditorUtils.AddToDefaultAddressableGroup(_sceneGuid); + break; + } + case 1: + { + // 1 is cancel + break; + } + case 2: + { + EditorGUIUtility.PingObject(_sceneAsset); + EditorWindow.GetWindow(EditorUtils.AddressablesGroupsWindowType); + break; + } + } + } + } +} + +#endif diff --git a/Editor/Toolbox/MakeAddressableTool.cs.meta b/Editor/Toolbox/MakeAddressableTool.cs.meta new file mode 100644 index 00000000..32958d45 --- /dev/null +++ b/Editor/Toolbox/MakeAddressableTool.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c8511c5ac7c84d228f518f9ac891e1d5 +timeCreated: 1688296861 \ No newline at end of file diff --git a/Editor/Toolbox/ToolboxPopupWindow.cs b/Editor/Toolbox/ToolboxPopupWindow.cs new file mode 100644 index 00000000..af740059 --- /dev/null +++ b/Editor/Toolbox/ToolboxPopupWindow.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace Eflatun.SceneReference.Editor.Toolbox +{ + internal class ToolboxPopupWindow : PopupWindowContent + { + private readonly IReadOnlyList _tools; + + public ToolboxPopupWindow(IReadOnlyList tools) + { + _tools = tools; + } + + public override void OnGUI(Rect rect) + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label("Scene Reference Toolbox", EditorStyles.boldLabel); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + if(_tools.Count == 0) + { + GUILayout.Space(2); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label("No tools available.", new GUIStyle { normal = { textColor = Color.grey }}); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + } + + foreach (var tool in _tools) + { + tool.Draw(Close); + } + } + + public override Vector2 GetWindowSize() + { + const int width = 200; + const int bottomPadding = 5; + var contentHeight = _tools.Count == 0 + ? EditorGUIUtility.singleLineHeight + : _tools.Sum(x => x.GetHeight()); + return new Vector2(width, EditorGUIUtility.singleLineHeight + contentHeight + bottomPadding); + } + + private void Close() + { + editorWindow.Close(); + } + } +} diff --git a/Editor/Toolbox/ToolboxPopupWindow.cs.meta b/Editor/Toolbox/ToolboxPopupWindow.cs.meta new file mode 100644 index 00000000..1a9a3cf8 --- /dev/null +++ b/Editor/Toolbox/ToolboxPopupWindow.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5caea4efce234f14acff366faa90c56b +timeCreated: 1688295291 \ No newline at end of file diff --git a/Editor/Utility/EditorUtils.cs b/Editor/Utility/EditorUtils.cs index d87b5406..a79faae9 100644 --- a/Editor/Utility/EditorUtils.cs +++ b/Editor/Utility/EditorUtils.cs @@ -1,4 +1,12 @@ -using System.IO; +using System; +using System.IO; +using System.Linq; +using Eflatun.SceneReference.Exceptions; +using UnityEditor; + +#if ESR_ADDRESSABLES +using UnityEditor.AddressableAssets; +#endif // ESR_ADDRESSABLES namespace Eflatun.SceneReference.Editor.Utility { @@ -7,9 +15,79 @@ namespace Eflatun.SceneReference.Editor.Utility ///
internal static class EditorUtils { + /// + /// Returns whether the addressables package is installed in the project. + /// + public static bool IsAddressablesPackagePresent => +#if ESR_ADDRESSABLES + true +#else // ESR_ADDRESSABLES + false +#endif // ESR_ADDRESSABLES + ; + + private static Type _addressablesGroupsWindowType; + /// The of the Addressables Groups editor window. + public static Type AddressablesGroupsWindowType + { + get + { + if (_addressablesGroupsWindowType == null) + { + var assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (var assembly in assemblies) + { + var type = assembly.GetType("UnityEditor.AddressableAssets.GUI.AddressableAssetsWindow"); + if (type != null) + { + _addressablesGroupsWindowType = type; + } + } + } + + if (_addressablesGroupsWindowType == null) + { + throw SceneReferenceInternalException.EditorCode("48302749", "Could not find addressables groups window type."); + } + + return _addressablesGroupsWindowType; + } + } + /// /// Returns true if the given ends with the file extension ".unity". /// public static bool IsScenePath(this string path) => Path.GetExtension(path) == ".unity"; + + /// + /// Adds the scene with the given path to build settings as enabled. + /// + public static void AddSceneToBuild(string scenePath) + { + var tempScenes = EditorBuildSettings.scenes.ToList(); + tempScenes.Add(new EditorBuildSettingsScene(scenePath, true)); + EditorBuildSettings.scenes = tempScenes.ToArray(); + } + + /// + /// Enables the scene with the given guid in build settings. + /// + public static void EnableSceneInBuild(string sceneGuid) + { + var tempScenes = EditorBuildSettings.scenes.ToList(); + tempScenes.Single(x => x.guid.ToString() == sceneGuid).enabled = true; + EditorBuildSettings.scenes = tempScenes.ToArray(); + } + +#if ESR_ADDRESSABLES + /// + /// Adds the scene with the given GUID to the default addressable group. + /// + public static void AddToDefaultAddressableGroup(string sceneGuid) + { + var defaultGroup = AddressableAssetSettingsDefaultObject.Settings.DefaultGroup; + AddressableAssetSettingsDefaultObject.Settings.CreateOrMoveEntry(sceneGuid, defaultGroup); + } +#endif // ESR_ADDRESSABLES } } diff --git a/README.md b/README.md index 20386070..30de6fee 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

- Provides GUID, Path, Build Index, and Name. + Provides GUID, Path, Build Index, Name, and Address.


@@ -46,28 +46,37 @@ openupm add com.eflatun.scenereference ### With Git URL -Add the following line to the `dependencies` section of your project's `manifest.json` file. Replace `2.1.0` with the version you want to install. +Add the following line to the `dependencies` section of your project's `manifest.json` file. Replace `3.0.0` with the version you want to install. ```json -"com.eflatun.scenereference": "git+https://github.com/starikcetin/Eflatun.SceneReference.git#2.1.0" +"com.eflatun.scenereference": "git+https://github.com/starikcetin/Eflatun.SceneReference.git#3.0.0" ``` -_Although it is highly discouraged, you can replace `2.1.0` with `upm` to get the latest version instead of a specific one._ +_Although it is highly discouraged, you can replace `3.0.0` with `upm` to get the latest version instead of a specific one._ -## Ignore Auto-Generated Map File in Version Control +## Ignore Auto-Generated Map Files in Version Control _You can skip this section if you are not using version control in your project._ -It is generally a recommended practice to ignore auto-generated files in version control. `Eflatun.SceneReference` auto-generates a JSON file with the path `Assets/Resources/Eflatun/SceneReference/SceneGuidToPathMap.generated.json`. We recommend you ignore this file and its corresponding `.meta` file in your version control. +It is generally a recommended practice to ignore auto-generated files in version control. `Eflatun.SceneReference` auto-generates JSON files with format `Assets/Resources/Eflatun/SceneReference/.generated.json`. We recommend you ignore these files and their corresponding `.meta` files in your version control. If you are using Git, you can do so by adding the following lines to your `.gitignore` file: ```gitignore -# Eflatun.SceneReference auto-generated map file -**/[Aa]ssets/Resources/Eflatun/SceneReference/SceneGuidToPathMap.generated.json -**/[Aa]ssets/Resources/Eflatun/SceneReference/SceneGuidToPathMap.generated.json.meta +# Eflatun.SceneReference auto-generated map files +**/[Aa]ssets/Resources/Eflatun/SceneReference/*.generated.json +**/[Aa]ssets/Resources/Eflatun/SceneReference/*.generated.json.meta ``` +## Optional Dependencies + +### Addressables Support + +`Eflatun.SceneReference` has support for addressables. It will be enabled or disabled automatically depending on whether you have the addressables package installed in your project. Please refer to the [Addressables Package Documentation](https://docs.unity3d.com/Packages/com.unity.addressables@latest) for information on how to install addressables package in your project. + +> **Note**
+> In accordance with the [Principle of least astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment), the public API and settings that concern addressables will still be visible even if addressables support is disabled. This is a deliberate design decision to prevent the overwhelming amount of compiler errors you would face otherwise, if you later decide to uninstall addressables from your project. This way, you only need to perform a minimal amount of refactors to your code in that case. + # Usage 1. Define your `SceneReference` serialized field: @@ -101,48 +110,84 @@ var sceneName = mySceneReference.Name; // You can only access these when the scene is currently loaded var loadedScene = mySceneReference.LoadedScene + +// You can only access these if you have addressables support enabled +var sceneAddress = mySceneReference.Address; ``` ## Validation -You can check `IsSafeToUse` property to make sure a `SceneReference` is completely valid before using it. +You can check `State` property to make sure a `SceneReference` is completely valid before using it. It will also give you information regarding the type of the scene it references. ```cs // Import Runtime namespace using Eflatun.SceneReference; -// Validate -if(mySceneReference.IsSafeToUse) +if (mySceneReference.State == SceneReferenceState.Unsafe) { - // Safe to use! + // The scene is not safe to use. Something is wrong. } -else + +if (mySceneReference.State == SceneReferenceState.Regular) +{ + // The scene is safe to use. It references a regular scene. +} + +// If you have addressables support enabled, you can also get this state: +if (mySceneReference.State == SceneReferenceState.Addressable) { - // Something is wrong. + // The scene is safe to use. It references an addressable scene. } + ``` -## Inline Scene-In-Build Validation & Fix Utility +## Inline Inspector Utilities + +A scene will be accessible in runtime only if one of the following is true: -Unity only includes in a build the scenes that are added and enabled in build settings. `Eflatun.SceneReference` on the other hand, allows you to assign on to it any scene you wish. This behaviour may cause runtime bugs when loading scenes. To prevent these potential bugs, `Eflatun.SceneReference` provides inline validation and fix utilities. +1. The scene is added and enabled in build settings. +2. The scene is in an addressables group. + +`Eflatun.SceneReference` on the other hand, allows you to assign onto it any scene you wish. This behaviour may cause runtime bugs when loading scenes. To prevent these potential bugs, `Eflatun.SceneReference` provides inline inspector utilities. In this example: ![.assets/validation_inspector.png](.assets/validation_inspector.png) -- `Another Scene` field is assigned a scene that is disabled in build settings. -- `Yet Another Scene` field is assigned a scene that is not included in build settings. +- `Scene A` field is assigned a scene that is added and enabled in build settings. All good here. +- `Scene B` field is assigned a scene that is disabled in build settings. +- `Scene C` field is assigned a scene that is neither in build settings nor addressable. +- `Scene D` filds is assigned an addressable scene. Again, all good here. +- `Scene E` field is not assigned anything. It is empty. - Similarly for the `Scene Reference List` property. -Clicking on the `Enable in Build...` button gives us this prompt, which enables us to quickly fix the situation: +> **Note**
+> Addressable scenes are only available if addressables support is enabled. + +If we click on the little gear (⚙️) icon to the right of the field, a toolbox popup will open that contains the fix utilities. For `Scene B` field, we get the following tools: + +![.assets/toolbox_disabled.png](.assets/toolbox_disabled.png) + +And for `Scene C` field, we get the following tools: + +![.assets/toolbox_nowhere.png](.assets/toolbox_nowhere.png) + +> **Note**
+> You will only see the `Make addressable...` tool if addressables support is enabled. + +Clicking on the `Enable in build...` button gives us this prompt: ![.assets/validation_enable_prompt.png](.assets/validation_enable_prompt.png) -Similarly, `Add to Build...` button gives the following prompt: +`Add to build...` button gives the following prompt: ![.assets/validation_add_prompt.png](.assets/validation_add_prompt.png) -Using these prompts, we can quickly alleviate the situation, and prevent potential runtime bugs when loading these scenes. +And `Make addressable...` button gives the following prompt: + +![.assets/validation_addressable_prompt.png](.assets/validation_addressable_prompt.png) + +Using these prompts, we can quickly alleviate the situation, and prevent potential runtime bugs when using these scenes. # Settings @@ -154,16 +199,35 @@ Look for the `Eflatun` category in the left panel. Select the `Scene Reference` ![.assets/settings.png](.assets/settings.png) +## Addressables Support + +Settings regarding addressables support. + +> **Note**
+> Settings under this category are only relevant if addressables support is enabled. + +### Color Addressable Scenes + +Should we color the property to draw attention for addressable scenes? + +Addressable scenes should be handled differently than regular scenes in runtime, through the addressables API. Therefore, you would want quickly identify if an `Eflatun.SceneReference` references an addressable scene or not. + +It is recommended to leave this option at 'true', as it will help you easily distinguish addressable scenes. + +> **Note**
+> This setting does not apply to regular scenes. They have their own coloring mechanism. It is controlled by the _Color Based On Scene-In-Build State_ setting under the _Property Drawer_ category. + ## Property Drawer -### Show Inline Scene-In-Build Utility +Settings regarding the property drawer. -Should we show the inline utility that allows you to quickly fix scenes that are either not in build or disabled in build? +### Show Inline Toolbox -Unity only bundles scenes that are added and enabled in build settings. Therefore, you would want to make sure the scene you assign to a -SceneReference is added and enabled in build settings. +Should we show the inline gear (⚙️) button that opens a toolbox? -It is recommended to leave this option at 'true', as the inline utility saves you a lot of time. +Unity only bundles scenes that are added and enabled in build settings, and addressables only pack scenes that are in an Addressable Group. Therefore, you would want to make sure the scene you assign to a SceneReference is either added and enabled in build settings, or is in an addressable group. The toolbox provides tools for you to quickly take action in these cases. + +It is recommended to leave this option enabled, as the toolbox saves you a lot of time. ### Color Based On Scene-In-Build State @@ -173,29 +237,41 @@ Unity only bundles scenes that are added and enabled in build settings. Therefor It is recommended to leave this option at 'true', as it will help you identify many potential runtime errors. -## Scene GUID To Path Map +> **Note**
+> This setting does not apply to addressable scenes. They have their own coloring mechanism. It is controlled by the _Color Addressable Scenes_ setting under the _Addressables Support_ category. + +## Scene Data Maps + +Settings regarding the scene data maps and the generator. ### Generation Triggers -Controls when the Scene GUID to Path Map gets regenerated. +Controls when the scene data maps get regenerated. + +- After Scene Asset Change: Regenerate the maps every time a scene asset changes (delete, create, move, rename). + +- Before Enter Play Mode: Regenerate the maps before entering play mode in the editor. + +- Before Build: Regenerate the maps before a build. -- After Scene Asset Change: Regenerate the map every time a scene asset changes (delete, create, move, rename). +- After Packages Resolve: Regenerate the maps after UPM packages are resolved. -- Before Enter Play Mode: Regenerate the map before entering play mode in the editor. +- After Addressables Change: Regenerate the maps after addressable group entries change. Only relevant if you have addressables support enabled. -- Before Build: Regenerate the map before a build. +It is recommended that you leave this option at _All_ unless you are debugging something. Failure to generate the maps when needed can result in broken scene references in runtime. -It is recommended that you leave this option at _All_ unless you are debugging something. Failure to generate the map when needed can result in broken scene references in runtime. +> **Note**
+> _All_ and _Everything_ are the same thing. They both represent all triggers. ### JSON Formatting -Controls the Scene GUID to Path Map Generator's JSON formatting. +Controls the Scene Data Maps Generator's JSON formatting. -It is recommended to leave this option at _Indented_, as it will help with version control and make the generated file human-readable. +It is recommended to leave this option at _Indented_, as it will help with version control and make the generated files human-readable. ### Fail Build If Generation Fails -Should we fail a build if scene GUID to path map generation fails? +Should we fail a build if scene data maps generation fails? Only relevant if _Before Build_ generation trigger is enabled. @@ -203,11 +279,12 @@ It is recommended to leave this option at _true_, as a failed map generation can # Advanced Usage -## Generated File +## Generated Files -`Eflatun.SceneReference` uses a JSON generator in editor-time to produce a `Scene GUID -> Scene Path` map. You can find the file at this location: `Assets/Resources/Eflatun/SceneReference/SceneGuidToPathMap.generated.json`. +`Eflatun.SceneReference` uses a JSON generator in editor-time to produce map files. You can find them at this location: `Assets/Resources/Eflatun/SceneReference`. They all end with `.generated.json`. -**This file is auto-generated, do not edit it. Any edits will be lost at the next generation.** +> **Warning**
+> Map files are auto-generated, do not edit them. Any edits will be lost at the next generation. ## Running the Generator Manually @@ -217,7 +294,7 @@ Running the generator has no side-effects. ### Via Menu Item -You can trigger the generator via a menu item. Find it under `Tools/Eflatun/Scene Reference/Run Scene GUID to Path Map Generator`: +You can trigger the generator via a menu item. Find it under `Tools/Eflatun/Scene Reference/Generate Scene Data Maps`: ![.assets/generator_menu.png](.assets/generator_menu.png) @@ -230,29 +307,44 @@ You can trigger the generator from your editor code: using Eflatun.SceneReference.Editor; // Run the generator. Only do this in Editor code! -SceneGuidToPathMapGenerator.Run(); +SceneDataMapsGenerator.Run(); ``` ## Accessing Settings in Editor Code You can read and manipulate `Eflatun.SceneReference` settings from your editor code. -**Changing the settings from code may have unintended consequences. Make sure you now what you are doing.** - ```cs // Import the Editor namespace using Eflatun.SceneReference.Editor; // Access a setting. Only do this in Editor code! -var generationTriggers = SettingsManager.SceneGuidToPathMap.GenerationTriggers; +var generationTriggers = SettingsManager.SceneDataMaps.GenerationTriggers; // Change a setting. Only do this in Editor code! -SettingsManager.SceneGuidToPathMap.GenerationTriggers = GenerationTriggers.All; +SettingsManager.SceneDataMaps.GenerationTriggers = GenerationTriggers.All; ``` -## Accessing the Scene Guid to Path Map Directly +> **Warning**
+> Changing settings from code may have unintended consequences. Make sure you now what you are doing. + +## Accessing the Maps Directly + +You can access the maps directly from both runtime and editor code. There are no side-effects of accessing the maps directly. -The `SceneGuidToPathMapProvider` static class is responsible for providing the scene GUID to scene path mapping to the rest of the code. There are two maps, one maps from GUIDs to paths, and the other one maps from paths to GUIDs. Both maps are inversely equivalent. You have the option of accessing them directly both in runtime and editor code: +In runtime, there are no performance penalties. The generated file is parsed automatically either upon the first access to the maps from a provider or during `RuntimeInitializeLoadType.BeforeSceneLoad`, whichever comes first. It is guaranteed that the generated file is parsed only once. Each provider does this for itself, there is no coordination between them. + +In editor, there are also no performance penalties except for one case. The generator assigns the map directly to the providers upon every generation. This prevents unnecessarily parsing the map file. However, if the providers lose the values assigned by the generator due to Unity [reloading the domain](https://docs.unity3d.com/Manual/DomainReloading.html), and some code tries to access the map before the generator runs again, then the providers have to parse the map file themselves. This is what happens in that scenario: + +1. Generator runs and directly assigns the map to the providers. +2. Something happens which triggers Unity to [reload the domain](https://docs.unity3d.com/Manual/DomainReloading.html). +3. You access the map from a provider. +4. Provider checks to see if it still has the map values, and realizes they are lost. +5. Provider parses the map file. + +### Scene GUID to Path Map + +The `SceneGuidToPathMapProvider` static class is responsible for providing the scene GUID to scene path mapping to the rest of the code. There are two maps, one maps from GUIDs to paths, and the other one maps from paths to GUIDs. Both maps are inversely equivalent. ```cs // Import the Runtime namespace @@ -265,83 +357,61 @@ var scenePath = SceneGuidToPathMapProvider.SceneGuidToPathMap[sceneGuid]; var sceneGuid = SceneGuidToPathMapProvider.ScenePathToGuidMap[scenePath]; ``` -There are no side-effects of accessing the map directly. +### Scene GUID to Address Map -In runtime, there are no performance penalties. The generated file is parsed automatically either upon the first access to `SceneGuidToPathMapProvider.SceneGuidToPathMap` or during `RuntimeInitializeLoadType.BeforeSceneLoad`, whichever comes first. It is guaranteed that the map is parsed only once. +> **Note**
+> This map is only relevant if addressables support is enabled. -In editor, there are also no performance penalties except for one case. The generator assigns the map directly to the provider upon every generation. This prevents unnecessarily parsing the map file. However, if the provider loses the value assigned by the generator due to Unity [reloading the domain](https://docs.unity3d.com/Manual/DomainReloading.html), and some code tries to access the map before the generator runs again, then the provider has to parse the map file itself. This is what happens in that scenario: +The `SceneGuidToAddressMapProvider` static class is responsible for providing the scene GUID to scene address mapping to the rest of the code. Unlike `SceneGuidToPathMapProvider`, this class cannot provide an inverse map, because the address of an asset is not guaranteed to be unique due to the design of addressables. Instead, it provides two methods called `GetGuidFromAddress` and `TryGetGuidFromAddress` that serve the same purpose. -1. Generator runs and directly assigns the map to the provider. -2. Something happens which triggers Unity to [reload the domain](https://docs.unity3d.com/Manual/DomainReloading.html). -3. You access `SceneGuidToPathMapProvider.SceneGuidToPathMap`. -4. Provider checks to see if it still has the map values, and realizes they are lost. -5. Provider parses the map file. +Getting the GUID from address can fail in following cases: +1. No scene with the given address found in the map (`AddressNotFoundException`). +2. Multiple scenes found with the given address in the map (`AddressNotUniqueException`). +3. Addressables support is disabled (`AddressablesSupportDisabledException`). -## Overriding Inline Scene-In-Build Validation Settings Per Field +```cs +// Import the Runtime namespace +using Eflatun.SceneReference; -You can override the behaviour of the scene-in-build validation project settings on a per-field basis using the `[SceneReferenceOptions]` attribute. For example, in order to disable both the coloring and the utility line, use the attribute as such: +// Get the scene address from a scene GUID. You can do this both in runtime and in editor. +var sceneAddress = SceneGuidToAddressMapProvider.SceneGuidToAddressMap[sceneGuid]; -```cs -[SceneReferenceOptions(Coloring = ColoringBehaviour.Disabled, UtilityLine = UtilityLineBehaviour.Disabled)] -[SerializeField] private SceneReference scene; +// Get the scene GUID from a scene address. You can do this both in runtime and in editor. + +// First way. Will throw exceptions on faliure. +var sceneGuid = SceneGuidToAddressMapProvider.GetGuidFromAddress(sceneAddress); + +// Second way. Returns a bool that represents success or failure. +if(SceneGuidToAddressMapProvider.TryGetGuidFromAddress(sceneAddress, out var sceneGuid)) +{ + // Success. sceneGuid is valid. +} +else +{ + // Failure. sceneGuid is invalid. +} ``` -For both `Coloring` and `UtlityLine`, passing `Enabled` or `Disabled` will force that behaviour to be enabled or disabled respectively, disregarding the project settings. `DoNotOverride` makes the field respect the project settings. `DoNotOverride` is the default value. +## Overriding Inline Inspector Utility Settings Per Field -You don't have to supply both fields at once. Missing fields will have the default value, which is `DoNotOverride`. For example, the following code disables the utility line, but makes coloring respect project settings: +You can override the inline inspector utility project settings on a per-field basis using the `[SceneReferenceOptions]` attribute. For example, in order to disable all inline utilities, use the attribute as such: ```cs -[SceneReferenceOptions(UtilityLine = UtilityLineBehaviour.Disabled)] +[SceneReferenceOptions(SceneInBuildColoring = ColoringBehaviour.Disabled, Toolbox = ToolboxBehaviour.Disabled, AddressableColoring = ColoringBehaviour.Disabled)] [SerializeField] private SceneReference scene; ``` -## Partial Validation - -If you need to perform validation partially (step-by-step), then you can use the partial validation properties. Keep in mind that the use cases that require partial validation are rare and few. +For all arguments, passing `Enabled` or `Disabled` will force that behaviour to be enabled or disabled respectively, disregarding the project settings. `DoNotOverride` makes the argument respect the project settings. `DoNotOverride` is the default value. -1. `HasValue`: Is this `SceneReference` assigned something? -2. `IsInSceneGuidToPathMap`: Does the Scene GUID to Path Map contain the scene? -3. `IsInBuildAndEnabled`: Is the scene added and enabled in Build Settings? - -These properties can throw exceptions. So the order in which you check them is important. This is the recommended order to avoid exceptions: +You don't have to supply both fields at once. Missing fields will have the default value, which is `DoNotOverride`. For example, the following code disables the toolbox, but makes coloring respect project settings: ```cs -// Import Runtime namespace -using Eflatun.SceneReference; - -// Avoid EmptySceneReferenceException. -if(mySceneReference.HasValue) -{ - // Avoid InvalidSceneReferenceException. - if(mySceneReference.IsInSceneGuidToPathMap) - { - // Avoid SceneManagement-related problems. - if(mySceneReference.IsInBuildAndEnabled) - { - // Completely validated. Safe to use. - } - else - { - // The scene is not added or is disabled in Build Settings. - } - } - else - { - // One of these things: - // 1. The Scene GUID to Path Map is outdated. - // 2. The scene is invalid. - // 3. The assigned asset is not a scene. - } -} -else -{ - // The SceneReference is empty (not assigned anything). -} +[SceneReferenceOptions(Toolbox = ToolboxBehaviour.Disabled)] +[SerializeField] private SceneReference scene; ``` -If you only need to check if it is completely safe to use a `SceneReference` without knowing where exactly the problem is, then only check `IsSafeToUse` instead. Checking only `IsSafeToUse` is sufficient for the majority of the use cases. - -Checking `IsSafeToUse` is equivalent to checking all partial validation properties in the correct order, but it provides a slightly better performance. +> **Note**
+> `AddressableColoring` argument is only relevant if addressables support is enabled. ## Custom Serialization @@ -368,7 +438,8 @@ SceneReference deserialized = JsonConvert.DeserializeObject(json ### Binary serialization via `System.Runtime.Serialization.Formatters.Binary` -**Warning:** We strongly advise against using `BinaryFormatter` as it is inconsistent and has inherent security risks. Only use it if you absolutely have to. +> **Warning**
+> We strongly advise against using `BinaryFormatter` as it is inconsistent and has inherent security risks. Only use it if you absolutely have to. Example `SceneReference` serialization to binary and back via `System.Runtime.Serialization.Formatters.Binary`: @@ -422,7 +493,7 @@ SceneReference deserialized = xmlSerializer.Deserialize(xmlReader) as SceneRefer ## Creating Instances in Code -You can create instances of `SceneReference` in code. To facilitate this, it exposes constructors and a factory method. +You can create instances of `SceneReference` in code. To facilitate this, it exposes constructors and factory methods. ```cs // Empty (and subsequently invalid) @@ -436,15 +507,28 @@ var fromSceneGuid = new SceneReference(sceneGuid); string scenePath = /* ... */; var fromScenePath = SceneReference.FromScenePath(scenePath); -// From Scene Asset (Only do this in Editor code!) +// Fom Scene Address +// Will throw AddressablesSupportDisabledException if addressables support is disabled. +string sceneAddress = /* ... */; +var fromSceneAddress = SceneReference.FromAddress(sceneAddress); + +// From Scene Asset +// You can only do this in Editor code. UnityEngine.Object sceneAsset = /* ... */; var fromSceneAsset = new SceneReference(sceneAsset); ``` -**Warnings:** -- Constructors and factory methods validate their arguments and throw exceptions of type `SceneReferenceCreationException` if they are invalid. -- The default constructor always creates an empty instance, but it never throws. -- The constructor that accepts a scene asset of type `UnityEngine.Object` is for Editor-use only. Do NOT use it in runtime code. +> **Warning**
+> Constructors and factory methods validate their arguments and throw exceptions of type `SceneReferenceCreationException` if they are invalid. + +> **Warning**
+> The default constructor always creates an empty instance, but it never throws. + +> **Warning**
+> The constructor that accepts a scene asset of type `UnityEngine.Object` is for Editor-use only. Do NOT use it in runtime code. + +> **Warning**
+> `FromAddress` factory method throws `AddressablesSupportDisabledException` if addressables support is disabled. # Exceptions @@ -454,7 +538,7 @@ Thrown if a `SceneReference` is empty (not assigned anything). To fix it, make sure the `SceneReference` is assigned a valid scene asset. -You can avoid it by checking `IsSafeToUse` (recommended) or `HasValue`. +You can avoid it by making sure the `State` property is not `Unsafe`. ## `InvalidSceneReferenceException` @@ -464,7 +548,7 @@ Thrown if a `SceneReference` is invalid. This can happen for these reasons: 2. The Scene GUID to Path Map is outdated. To fix this, you can either manually run the map generator, or enable all generation triggers. It is highly recommended to keep all the generation triggers enabled. -You can avoid it by checking `IsSafeToUse` (recommended) or `IsInSceneGuidToPathMap`. +You can avoid it by making sure the `State` property is not `Unsafe`. ## `SceneReferenceCreationException` @@ -474,6 +558,43 @@ It can happen for many different reasons. The exception message contains the particular reason and suggestions on how to fix it. +## `AddressNotFoundException` + +Thrown if a given address is not found in the Scene GUID to Address Map. This can happen for these reasons: + +1. The asset with the given address either doesn't exist or is not a scene. To fix this, make sure you provide the address of a valid scene. + +2. The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled. + +> **Note**
+> This exception will never be thrown if addressables support is disabled. + +## `AddressNotUniqueException` + +Thrown if a given address matches multiple entries in the Scene GUID to Address Map. This can happen for these reasons: + +1. There are multiple addressable scenes with the same given address. To fix this, make sure there is only one addressable scene with the given address. + +2. The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled. + +> **Note**
+> This exception will never be thrown if addressables support is disabled. + +## `SceneNotAddressableException` + +Thrown if addressables-specific operations are attempted on a `SceneReference` that is assigned a non-addressable scene. + +You can avoid this exception by making sure the `State` property is `Addressable`. + +> **Note**
+> This exception will never be thrown if addressables support is disabled. + +## `AddressablesSupportDisabledException` + +Thrown if an operation that requires addressables support is attempted while addressables support is disabled. + +To fix it, make sure addressables support is enabled. + ## `SceneReferenceInternalException` This exception is not part of the public API. It indicates that something has gone wrong internally. It is not meant to be catched, fixed, or avoided by user code. diff --git a/Runtime/ColoringBehaviour.cs b/Runtime/ColoringBehaviour.cs index 2a14527d..dae3a8ef 100644 --- a/Runtime/ColoringBehaviour.cs +++ b/Runtime/ColoringBehaviour.cs @@ -3,7 +3,7 @@ namespace Eflatun.SceneReference { /// - /// The inspector coloring override behaviour for scene-in-build validation of fields. + /// The inspector coloring override behaviour for fields. /// [PublicAPI] public enum ColoringBehaviour diff --git a/Runtime/Eflatun.SceneReference.asmdef b/Runtime/Eflatun.SceneReference.asmdef index cebb6d58..8fcfc436 100644 --- a/Runtime/Eflatun.SceneReference.asmdef +++ b/Runtime/Eflatun.SceneReference.asmdef @@ -9,6 +9,17 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [], + "versionDefines": [ + { + "name": "com.unity.addressables", + "expression": "", + "define": "ESR_ADDRESSABLES" + }, + { + "name": "com.unity.addressables", + "expression": "[1.0.0,2.0.0)", + "define": "ESR_ADDRESSABLES_1_X_X" + } + ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Runtime/Exceptions.meta b/Runtime/Exceptions.meta new file mode 100644 index 00000000..aed7f7e7 --- /dev/null +++ b/Runtime/Exceptions.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5daedf7ef42e488b88d8bc66745083a4 +timeCreated: 1686399518 \ No newline at end of file diff --git a/Runtime/Exceptions/AddressNotFoundException.cs b/Runtime/Exceptions/AddressNotFoundException.cs new file mode 100644 index 00000000..5a283e6f --- /dev/null +++ b/Runtime/Exceptions/AddressNotFoundException.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.Serialization; +using JetBrains.Annotations; + +namespace Eflatun.SceneReference.Exceptions +{ + /// + /// Thrown if a given address is not found in the Scene GUID to Address Map. This can happen for these reasons: + /// + /// The asset with the given address either doesn't exist or is not a scene. To fix this, make sure you provide the address of a valid addressable scene. + /// The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled. + /// + /// + /// + /// This exception will never be thrown if addressables support is disabled. + /// + [PublicAPI] + [Serializable] + public class AddressNotFoundException : SceneReferenceException + { + private static string MakeExceptionMessage(string address) => + $"The address is not found in the Scene GUID to Address Map. Address: {address}." + + "\nThis can happen for these reasons:" + + "\n1. The asset with the given address either doesn't exist or is not a scene. To fix this, make sure you provide the address of a valid addressable scene." + + "\n2. The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled."; + + internal AddressNotFoundException(string address) : base(MakeExceptionMessage(address)) + { + } + + internal AddressNotFoundException(string address, Exception inner) : base(MakeExceptionMessage(address), inner) + { + } + + private protected AddressNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Runtime/Exceptions/AddressNotFoundException.cs.meta b/Runtime/Exceptions/AddressNotFoundException.cs.meta new file mode 100644 index 00000000..cfc97dcb --- /dev/null +++ b/Runtime/Exceptions/AddressNotFoundException.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 714848f93bda47fd89acb4de0b00e489 +timeCreated: 1686396017 \ No newline at end of file diff --git a/Runtime/Exceptions/AddressNotUniqueException.cs b/Runtime/Exceptions/AddressNotUniqueException.cs new file mode 100644 index 00000000..150c17b2 --- /dev/null +++ b/Runtime/Exceptions/AddressNotUniqueException.cs @@ -0,0 +1,39 @@ +using System; +using System.Runtime.Serialization; +using JetBrains.Annotations; + +namespace Eflatun.SceneReference.Exceptions +{ + /// + /// Thrown if a given address matches multiple entries in the Scene GUID to Address Map. This can happen for these reasons: + /// + /// There are multiple addressable scenes with the same given address. To fix this, make sure there is only one addressable scene with the given address. + /// The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled. + /// + /// + /// + /// This exception will never be thrown if addressables support is disabled. + /// + [PublicAPI] + [Serializable] + public class AddressNotUniqueException : SceneReferenceException + { + private static string MakeExceptionMessage(string address) => + $"The address matches multiple scenes in the Scene GUID to Address Map. Address: {address}." + + "\nThrown if a given address matches multiple entries in the Scene GUID to Address Map. This can happen for these reasons:" + + "\n1. There are multiple addressable scenes with the same given address. To fix this, make sure there is only one addressable scene with the given address." + + "\n2. The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled."; + + internal AddressNotUniqueException(string address) : base(MakeExceptionMessage(address)) + { + } + + internal AddressNotUniqueException(string address, Exception inner) : base(MakeExceptionMessage(address), inner) + { + } + + private protected AddressNotUniqueException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Runtime/Exceptions/AddressNotUniqueException.cs.meta b/Runtime/Exceptions/AddressNotUniqueException.cs.meta new file mode 100644 index 00000000..f08a47d4 --- /dev/null +++ b/Runtime/Exceptions/AddressNotUniqueException.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4a44d1dda9284c499b7ae568e6682a0f +timeCreated: 1686396112 \ No newline at end of file diff --git a/Runtime/Exceptions/AddressablesSupportDisabledException.cs b/Runtime/Exceptions/AddressablesSupportDisabledException.cs new file mode 100644 index 00000000..73723363 --- /dev/null +++ b/Runtime/Exceptions/AddressablesSupportDisabledException.cs @@ -0,0 +1,29 @@ +using System; +using System.Runtime.Serialization; +using JetBrains.Annotations; + +namespace Eflatun.SceneReference.Exceptions +{ + /// + /// Thrown if an operation that requires addressables support is attempted while addressables support is disabled. + /// To fix it, make sure addressables support is enabled. + /// + [PublicAPI] + [Serializable] + public class AddressablesSupportDisabledException : SceneReferenceException + { + private const string ExceptionMessage = "An operation that requires addressables support is attempted while addressables support is disabled. To fix it, make sure addressables support is enabled."; + + internal AddressablesSupportDisabledException() : base(ExceptionMessage) + { + } + + internal AddressablesSupportDisabledException(Exception inner) : base(ExceptionMessage, inner) + { + } + + private protected AddressablesSupportDisabledException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Runtime/Exceptions/AddressablesSupportDisabledException.cs.meta b/Runtime/Exceptions/AddressablesSupportDisabledException.cs.meta new file mode 100644 index 00000000..dcf34724 --- /dev/null +++ b/Runtime/Exceptions/AddressablesSupportDisabledException.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 01e03da31c404e01a44322489e8d6067 +timeCreated: 1686397369 \ No newline at end of file diff --git a/Runtime/EmptySceneReferenceException.cs b/Runtime/Exceptions/EmptySceneReferenceException.cs similarity index 87% rename from Runtime/EmptySceneReferenceException.cs rename to Runtime/Exceptions/EmptySceneReferenceException.cs index 998d30d9..84eca101 100644 --- a/Runtime/EmptySceneReferenceException.cs +++ b/Runtime/Exceptions/EmptySceneReferenceException.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace Eflatun.SceneReference +namespace Eflatun.SceneReference.Exceptions { /// /// Thrown if a is empty (not assigned anything). @@ -13,7 +13,7 @@ namespace Eflatun.SceneReference public class EmptySceneReferenceException : SceneReferenceException { private const string ExceptionMessage = - "The SceneReference is empty (not assigned anything). To fix this, make sure to assign a valid scene to the SceneReference."; + "The SceneReference is empty (not assigned anything). To fix this, make sure the 'SceneReference' is assigned a valid scene asset."; internal EmptySceneReferenceException() : base(ExceptionMessage) { diff --git a/Runtime/EmptySceneReferenceException.cs.meta b/Runtime/Exceptions/EmptySceneReferenceException.cs.meta similarity index 100% rename from Runtime/EmptySceneReferenceException.cs.meta rename to Runtime/Exceptions/EmptySceneReferenceException.cs.meta diff --git a/Runtime/InvalidSceneReferenceException.cs b/Runtime/Exceptions/InvalidSceneReferenceException.cs similarity index 85% rename from Runtime/InvalidSceneReferenceException.cs rename to Runtime/Exceptions/InvalidSceneReferenceException.cs index 58712213..84affb6c 100644 --- a/Runtime/InvalidSceneReferenceException.cs +++ b/Runtime/Exceptions/InvalidSceneReferenceException.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace Eflatun.SceneReference +namespace Eflatun.SceneReference.Exceptions { /// /// Thrown if a is invalid. This can happen for these reasons: @@ -17,8 +17,8 @@ public class InvalidSceneReferenceException : SceneReferenceException { private const string ExceptionMessage = "The SceneReference is invalid. This can happen for these reasons:" - + "\n1. The SceneReference is assigned an invalid scene, or the assigned asset is not a scene. To fix this, make sure to assign a valid scene to the SceneReference." - + "\n2. The scene GUID to path map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled."; + + "\n1. The SceneReference is assigned an invalid scene, or the assigned asset is not a scene. To fix this, make sure the 'SceneReference' is assigned a valid scene asset." + + "\n2. The scene GUID to path map is outdated. To fix this, you can either manually run the map generator, or enable all generation triggers. It is highly recommended to keep all the generation triggers enabled."; internal InvalidSceneReferenceException() : base(ExceptionMessage) { diff --git a/Runtime/InvalidSceneReferenceException.cs.meta b/Runtime/Exceptions/InvalidSceneReferenceException.cs.meta similarity index 100% rename from Runtime/InvalidSceneReferenceException.cs.meta rename to Runtime/Exceptions/InvalidSceneReferenceException.cs.meta diff --git a/Runtime/Exceptions/SceneNotAddressableException.cs b/Runtime/Exceptions/SceneNotAddressableException.cs new file mode 100644 index 00000000..b28d17b7 --- /dev/null +++ b/Runtime/Exceptions/SceneNotAddressableException.cs @@ -0,0 +1,31 @@ +using System; +using System.Runtime.Serialization; +using JetBrains.Annotations; + +namespace Eflatun.SceneReference.Exceptions +{ + /// + /// Thrown if addressables-specific operations are attempted on a that is assigned a non-addressable scene.

+ /// You can avoid this exception by making sure the property is . + ///

+ [PublicAPI] + [Serializable] + public class SceneNotAddressableException : SceneReferenceException + { + private const string ExceptionMessage = + "An addressables-specific operation is attempted on a SceneReference that is assigned a non-addressable scene." + + "\nYou can avoid this exception by making sure the State property is Addressable."; + + internal SceneNotAddressableException() : base(ExceptionMessage) + { + } + + internal SceneNotAddressableException(Exception inner) : base(ExceptionMessage, inner) + { + } + + private protected SceneNotAddressableException(SerializationInfo info, StreamingContext context) : base(info, context) + { + } + } +} diff --git a/Runtime/Exceptions/SceneNotAddressableException.cs.meta b/Runtime/Exceptions/SceneNotAddressableException.cs.meta new file mode 100644 index 00000000..058e2624 --- /dev/null +++ b/Runtime/Exceptions/SceneNotAddressableException.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 818f593b4e12490cb6c75b0071955cba +timeCreated: 1686395907 \ No newline at end of file diff --git a/Runtime/SceneReferenceCreationException.cs b/Runtime/Exceptions/SceneReferenceCreationException.cs similarity index 95% rename from Runtime/SceneReferenceCreationException.cs rename to Runtime/Exceptions/SceneReferenceCreationException.cs index 07a898ac..1f2b5c8f 100644 --- a/Runtime/SceneReferenceCreationException.cs +++ b/Runtime/Exceptions/SceneReferenceCreationException.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace Eflatun.SceneReference +namespace Eflatun.SceneReference.Exceptions { /// /// Thrown when something goes wrong during the creation of a . diff --git a/Runtime/SceneReferenceCreationException.cs.meta b/Runtime/Exceptions/SceneReferenceCreationException.cs.meta similarity index 100% rename from Runtime/SceneReferenceCreationException.cs.meta rename to Runtime/Exceptions/SceneReferenceCreationException.cs.meta diff --git a/Runtime/SceneReferenceException.cs b/Runtime/Exceptions/SceneReferenceException.cs similarity index 94% rename from Runtime/SceneReferenceException.cs rename to Runtime/Exceptions/SceneReferenceException.cs index 3c46c99e..8cb4f931 100644 --- a/Runtime/SceneReferenceException.cs +++ b/Runtime/Exceptions/SceneReferenceException.cs @@ -2,7 +2,7 @@ using System.Runtime.Serialization; using JetBrains.Annotations; -namespace Eflatun.SceneReference +namespace Eflatun.SceneReference.Exceptions { /// /// Root custom exception for the Eflatun.SceneReference package. diff --git a/Runtime/SceneReferenceException.cs.meta b/Runtime/Exceptions/SceneReferenceException.cs.meta similarity index 100% rename from Runtime/SceneReferenceException.cs.meta rename to Runtime/Exceptions/SceneReferenceException.cs.meta diff --git a/Runtime/SceneReferenceInternalException.cs b/Runtime/Exceptions/SceneReferenceInternalException.cs similarity index 62% rename from Runtime/SceneReferenceInternalException.cs rename to Runtime/Exceptions/SceneReferenceInternalException.cs index d1f16e27..4f4180ae 100644 --- a/Runtime/SceneReferenceInternalException.cs +++ b/Runtime/Exceptions/SceneReferenceInternalException.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.Serialization; -namespace Eflatun.SceneReference +namespace Eflatun.SceneReference.Exceptions { [Serializable] internal class SceneReferenceInternalException : SceneReferenceException @@ -9,6 +9,13 @@ internal class SceneReferenceInternalException : SceneReferenceException public static SceneReferenceInternalException InvalidGuid(string location, string guid) => new SceneReferenceInternalException(location, $"GUID is invalid. GUID: \"{guid}\""); + public static SceneReferenceInternalException ExceptionImpossible(string location, TException exception) + where TException : Exception => + new SceneReferenceInternalException(location, $"Exception impossible. Exception: \n{exception.ToString()}"); + + public static Exception EditorCode(string location, string description) => + new SceneReferenceInternalException(location, $"Editor code. {description}"); + private SceneReferenceInternalException(string location, string info) : base($"If you are seeing this, something has gone wrong internally. Please open an issue on Github (https://github.com/starikcetin/Eflatun.SceneReference/issues) and include the following information:\nLocation: {location}\nInfo: {info}") { diff --git a/Runtime/SceneReferenceInternalException.cs.meta b/Runtime/Exceptions/SceneReferenceInternalException.cs.meta similarity index 100% rename from Runtime/SceneReferenceInternalException.cs.meta rename to Runtime/Exceptions/SceneReferenceInternalException.cs.meta diff --git a/Runtime/Paths.cs b/Runtime/Paths.cs index ed5d1d94..3e7a9713 100644 --- a/Runtime/Paths.cs +++ b/Runtime/Paths.cs @@ -11,47 +11,57 @@ internal static class Paths { private static readonly ConvertedPath AssetsFolder = new ConvertedPath(Application.dataPath); private static readonly ConvertedPath ResourcesFolder = new ConvertedPath(Path.Combine(AssetsFolder.GivenPath, "Resources")); - + /// /// Relative to the 'Assets/Resources' folder. /// public static class RelativeToResources { /// - /// Path to the folder containing the generated map file. Relative to the 'Assets/Resources' folder. + /// Path to the folder containing the generated map files. Relative to the 'Assets/Resources' folder. + /// + public static readonly ConvertedPath SceneDataMapsFolder = new ConvertedPath(Path.Combine("Eflatun", "SceneReference")); + + /// + /// Path to the generated scene GUID to path map file. Relative to the 'Assets/Resources' folder. /// - public static readonly ConvertedPath SceneGuidToPathMapFolder = new ConvertedPath(Path.Combine("Eflatun", "SceneReference")); - + public static readonly ConvertedPath SceneGuidToPathMapFile = new ConvertedPath(Path.Combine(SceneDataMapsFolder.GivenPath, "SceneGuidToPathMap.generated.json")); + /// - /// Path to the generated map file. Relative to the 'Assets/Resources' folder. + /// Path to the `.keep` file in the folder of the generated map files. Relative to the 'Assets/Resources' folder. /// - public static readonly ConvertedPath SceneGuidToPathMapFile = new ConvertedPath(Path.Combine(SceneGuidToPathMapFolder.GivenPath, "SceneGuidToPathMap.generated.json")); - + public static readonly ConvertedPath SceneDataMapsDotKeepFile = new ConvertedPath(Path.Combine(SceneDataMapsFolder.GivenPath, ".keep")); + /// - /// Path to the `.keep` file in the folder of the generated map file. Relative to the 'Assets/Resources' folder. + /// Path to the generated scene GUID to address map file. Relative to the 'Assets/Resources' folder. /// - public static readonly ConvertedPath SceneGuidToPathMapDotKeepFile = new ConvertedPath(Path.Combine(SceneGuidToPathMapFolder.GivenPath, ".keep")); + public static readonly ConvertedPath SceneGuidToAddressMapFile = new ConvertedPath(Path.Combine(SceneDataMapsFolder.GivenPath, "SceneGuidToAddressMap.generated.json")); } - + /// /// Absolute paths. /// public static class Absolute { /// - /// Path to the folder containing the generated map file. Absolute. + /// Path to the folder containing the generated map files. Absolute. /// - public static readonly ConvertedPath SceneGuidToPathMapFolder = new ConvertedPath(Path.Combine(ResourcesFolder.GivenPath, RelativeToResources.SceneGuidToPathMapFolder.GivenPath)); - + public static readonly ConvertedPath SceneDataMapsFolder = new ConvertedPath(Path.Combine(ResourcesFolder.GivenPath, RelativeToResources.SceneDataMapsFolder.GivenPath)); + /// - /// Path to the generated map file. Absolute. + /// Path to the generated scene GUID to path map file. Absolute. /// public static readonly ConvertedPath SceneGuidToPathMapFile = new ConvertedPath(Path.Combine(ResourcesFolder.GivenPath, RelativeToResources.SceneGuidToPathMapFile.GivenPath)); - + + /// + /// Path to the `.keep` file in the folder of the generated map files. Absolute. + /// + public static readonly ConvertedPath SceneDataMapsDotKeepFile = new ConvertedPath(Path.Combine(ResourcesFolder.GivenPath, RelativeToResources.SceneDataMapsDotKeepFile.GivenPath)); + /// - /// Path to the `.keep` file in the folder of the generated map file. Absolute. + /// Path to the generated scene GUID to address map file. Absolute. /// - public static readonly ConvertedPath SceneGuidToPathMapDotKeepFile = new ConvertedPath(Path.Combine(ResourcesFolder.GivenPath, RelativeToResources.SceneGuidToPathMapDotKeepFile.GivenPath)); + public static readonly ConvertedPath SceneGuidToAddressMapFile = new ConvertedPath(Path.Combine(ResourcesFolder.GivenPath, RelativeToResources.SceneGuidToAddressMapFile.GivenPath)); } } } diff --git a/Runtime/SceneGuidToAddressMapProvider.cs b/Runtime/SceneGuidToAddressMapProvider.cs new file mode 100644 index 00000000..026e54fc --- /dev/null +++ b/Runtime/SceneGuidToAddressMapProvider.cs @@ -0,0 +1,132 @@ +using System.Collections.Generic; +using System.Linq; +using Eflatun.SceneReference.Exceptions; +using JetBrains.Annotations; +using UnityEngine; +using UnityEngine.Scripting; +using Eflatun.SceneReference.Utility; +using Newtonsoft.Json; + +namespace Eflatun.SceneReference +{ + /// + /// Provides the scene GUID to address map. Can be used in both editor and runtime. + /// + /// + /// This map is only relevant if addressables support is enabled. It will be empty if addressables support is disabled.

+ /// Unlike , this class can not provide an inverse map because address + /// of an asset does not have to be unique. Instead, it provides and + /// methods. + /// + [PublicAPI] + public static class SceneGuidToAddressMapProvider + { + private static Dictionary _sceneGuidToAddressMap; + + ///

+ /// The scene GUID to address map. + /// + public static IReadOnlyDictionary SceneGuidToAddressMap + { + get + { + LoadIfNotAlready(); + return _sceneGuidToAddressMap; + } + } + + /// + /// Gets the GUID of the scene with the given address. + /// + /// Address of the scene. + /// GUID of the scene. + /// Thrown if no scene with the given address is found in the map. + /// Thrown if multiple scenes found with the given address in the map. + /// Thrown if addressables support is disabled. + public static string GetGuidFromAddress(string address) + { +#if ESR_ADDRESSABLES + LoadIfNotAlready(); + + var matchingEntries = _sceneGuidToAddressMap.Where(x => x.Value == address).ToArray(); + + if (matchingEntries.Length < 1) + { + throw new AddressNotFoundException(address); + } + + if (matchingEntries.Length > 1) + { + throw new AddressNotUniqueException(address); + } + + return matchingEntries.First().Key; +#else // ESR_ADDRESSABLES + throw new AddressablesSupportDisabledException(); +#endif // ESR_ADDRESSABLES + } + + /// + /// Gets the GUID of the scene with the given address. + /// + /// Address of the scene. + /// GUID of the scene if the return is true; null otherwise. + /// true if there is exactly one scene with the given address in the map; false otherwise. + [ContractAnnotation("=> true, guid:notnull; => false, guid:null")] + public static bool TryGetGuidFromAddress(string address, out string guid) + { + try + { + guid = GetGuidFromAddress(address); + return true; + } + catch + { + guid = null; + return false; + } + } + + /// + /// IMPORTANT: This method does NOT check if addressables support is enabled or not! It will assign no matter what. + /// + internal static void DirectAssign(Dictionary sceneGuidToAddressMap) + { + FillWith(sceneGuidToAddressMap); + } + + [Preserve] + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void LoadIfNotAlready() + { + if (_sceneGuidToAddressMap == null) + { + Load(); + } + } + + private static void Load() + { +#if ESR_ADDRESSABLES + var genFilePath = Paths.RelativeToResources.SceneGuidToAddressMapFile.UnixPath.WithoutExtension(); + var genFile = Resources.Load(genFilePath); + + if (genFile == null) + { + Logger.Error("Scene GUID to address map file not found!"); + return; + } + + var deserialized = JsonConvert.DeserializeObject>(genFile.text); + FillWith(deserialized); +#else // ESR_ADDRESSABLES + FillWith(new Dictionary()); +#endif // ESR_ADDRESSABLES + } + + private static void FillWith(Dictionary sceneGuidToAddressMap) + { + _sceneGuidToAddressMap = sceneGuidToAddressMap; + } + } +} diff --git a/Runtime/SceneGuidToAddressMapProvider.cs.meta b/Runtime/SceneGuidToAddressMapProvider.cs.meta new file mode 100644 index 00000000..3f282615 --- /dev/null +++ b/Runtime/SceneGuidToAddressMapProvider.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ffad85fa192d4be79d8bcf67d29926a3 +timeCreated: 1682781782 \ No newline at end of file diff --git a/Runtime/SceneGuidToPathMapProvider.cs b/Runtime/SceneGuidToPathMapProvider.cs index 6b8d6806..4c10c01b 100644 --- a/Runtime/SceneGuidToPathMapProvider.cs +++ b/Runtime/SceneGuidToPathMapProvider.cs @@ -41,9 +41,9 @@ public static IReadOnlyDictionary ScenePathToGuidMap } } - internal static void DirectAssign(Dictionary sceneGuidToPath) + internal static void DirectAssign(Dictionary sceneGuidToPathMap) { - FillWith(sceneGuidToPath); + FillWith(sceneGuidToPathMap); } [Preserve] @@ -71,10 +71,10 @@ private static void Load() FillWith(deserialized); } - private static void FillWith(Dictionary sceneGuidToPath) + private static void FillWith(Dictionary sceneGuidToPathMap) { - _sceneGuidToPathMap = sceneGuidToPath; - _scenePathToGuidMap = sceneGuidToPath.ToDictionary(x => x.Value, x => x.Key); + _sceneGuidToPathMap = sceneGuidToPathMap; + _scenePathToGuidMap = sceneGuidToPathMap.ToDictionary(x => x.Value, x => x.Key); } } } diff --git a/Runtime/SceneReference.cs b/Runtime/SceneReference.cs index 0019ff19..a5b62c2b 100644 --- a/Runtime/SceneReference.cs +++ b/Runtime/SceneReference.cs @@ -3,6 +3,7 @@ using System.Xml; using System.Xml.Schema; using System.Xml.Serialization; +using Eflatun.SceneReference.Exceptions; using Eflatun.SceneReference.Utility; using JetBrains.Annotations; using UnityEngine; @@ -156,6 +157,77 @@ public static SceneReference FromScenePath(string path) return new SceneReference(guidFromMap); } + /// + /// Creates a new which references the scene with the given address. + /// + /// Address of the scene to reference. + /// A new . + /// Throws if the given address is null or whitespace. + /// Throws if the given address is not found in the scene GUID to address map. + /// Throws if the given address matches multiple entries from the scene GUID to address map. + /// Throws if addressables support is disabled. + public static SceneReference FromAddress(string address) + { +#if ESR_ADDRESSABLES + if (string.IsNullOrWhiteSpace(address)) + { + throw new SceneReferenceCreationException( + $"Given address is null or whitespace. Path: '{address}'" + + "\nTo fix this, make sure you provide the address of a valid addressable scene."); + } + + try + { + var guidFromMap = SceneGuidToAddressMapProvider.GetGuidFromAddress(address); + return new SceneReference(guidFromMap); + } + catch (AddressNotFoundException e) + { + throw new SceneReferenceCreationException( + $"Given address is not found in the Scene GUID to Address Map. Address: {address}." + + "\nThis can happen for these reasons:" + + "\n1. The asset with the given address either doesn't exist or is not a scene. To fix this, make sure you provide the address of a valid addressable scene." + + "\n2. The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled." + , e + ); + } + catch (AddressNotUniqueException e) + { + throw new SceneReferenceCreationException( + $"Given address matches multiple scenes in the Scene GUID to Address Map. Address: {address}." + + "\nThrown if a given address matches multiple entries in the Scene GUID to Address Map. This can happen for these reasons:" + + "\n1. There are multiple addressable scenes with the same given address. To fix this, make sure there is only one addressable scene with the given address." + + "\n2. The Scene GUID to Address Map is outdated. To fix this, you can either manually run the generator, or enable generation triggers. It is highly recommended to keep all the generation triggers enabled." + , e + ); + } + catch (AddressablesSupportDisabledException e) + { + // internal exceptions should not be documented as part of the public API + throw SceneReferenceInternalException.ExceptionImpossible("48302749", e); + } +#else // ESR_ADDRESSABLES + throw new AddressablesSupportDisabledException(); +#endif // ESR_ADDRESSABLES + } + + /// + /// Is this assigned something? + /// + private bool HasValue + { + get + { + if (!Guid.IsValidGuid()) + { + // internal exceptions should not be documented as part of the public API + throw SceneReferenceInternalException.InvalidGuid("54783205", Guid); + } + + return Guid != Utils.AllZeroGuid; + } + } + /// /// GUID of the scene asset. /// @@ -164,8 +236,8 @@ public static SceneReference FromScenePath(string path) /// /// Path to the scene asset. /// - /// Throws if is false. - /// Throws if is false. + /// Throws if nothing is assigned to this SceneReference. + /// Throws if the scene is not in the scene GUID to path map. public string Path { get @@ -187,108 +259,88 @@ public string Path /// /// Build index of the scene. /// - /// Throws if is false. - /// Throws if is false. + /// Throws if nothing is assigned to this SceneReference. + /// Throws if the scene is not in the scene GUID to path map. /// - /// This property will return -1 if is false. + /// This property will return -1 if the scene is not added and enabled in the build settings. /// public int BuildIndex => SceneUtility.GetBuildIndexByScenePath(Path); /// /// Name of the scene asset. Without '.unity' extension. /// - /// Throws if is false. - /// Throws if is false. + /// Throws if nothing is assigned to this SceneReference. + /// Throws if the scene is not in the scene GUID to path map. public string Name => System.IO.Path.GetFileNameWithoutExtension(Path); /// /// The struct for this scene. Only valid if the scene is currently loaded. /// - /// Throws if is false. - /// Throws if is false. + /// Throws if nothing is assigned to this SceneReference. + /// Throws if the scene is not in the scene GUID to path map. /// - /// You can check to see if the value of this property is valid.

- /// If is false, the scene can never be loaded, and therefore this property can never have a valid value. + /// You can check on the return value to see if it is valid. /// public Scene LoadedScene => SceneManager.GetSceneByPath(Path); ///

- /// Is this assigned something? + /// Address of the scene. /// - /// - /// Only check this property if you need partial validations, as this property alone does not communicate whether this is absolutely safe to use.

- /// If you only need to check if it is completely safe to use a without knowing where exactly the problem is, then only check instead. Checking only is sufficient for the majority of the use cases. - /// - /// - /// - /// - public bool HasValue + /// Throws if nothing is assigned to this SceneReference. + /// Throws if the scene is not in the scene GUID to path map. + /// Throws if the scene is not in the scene GUID to address map. + /// Throws if addressables support is disabled. + public string Address { get { - if (!Guid.IsValidGuid()) +#if ESR_ADDRESSABLES + if (!HasValue) { - // internal exceptions should not be documented as part of the public API - throw SceneReferenceInternalException.InvalidGuid("54783205", Guid); + throw new EmptySceneReferenceException(); } - return Guid != Utils.AllZeroGuid; + if (!SceneGuidToPathMapProvider.SceneGuidToPathMap.ContainsKey(Guid)) + { + throw new InvalidSceneReferenceException(); + } + + if (!SceneGuidToAddressMapProvider.SceneGuidToAddressMap.TryGetValue(Guid, out var addressFromMap)) + { + throw new SceneNotAddressableException(); + } + + return addressFromMap; +#else // ESR_ADDRESSABLES + throw new AddressablesSupportDisabledException(); +#endif // ESR_ADDRESSABLES } } - ///

- /// Does the scene GUID to path map contain the scene? - /// - /// Throws if is false. - /// - /// Only check this property if you need partial validations, as this property alone does not communicate whether this is absolutely safe to use.

- /// If you only need to check if it is completely safe to use a without knowing where exactly the problem is, then only check instead. Checking only is sufficient for the majority of the use cases. - /// - /// - /// - /// - public bool IsInSceneGuidToPathMap + /// + public SceneReferenceState State { get { - if (!HasValue) + if (HasValue && SceneGuidToPathMapProvider.SceneGuidToPathMap.TryGetValue(Guid, out var path)) { - throw new EmptySceneReferenceException(); + if (SceneUtility.GetBuildIndexByScenePath(path) != -1) + { + return SceneReferenceState.Regular; + } + +#if ESR_ADDRESSABLES + if (SceneGuidToAddressMapProvider.SceneGuidToAddressMap.ContainsKey(Guid)) + { + return SceneReferenceState.Addressable; + } +#endif // ESR_ADDRESSABLES } - return SceneGuidToPathMapProvider.SceneGuidToPathMap.ContainsKey(Guid); + return SceneReferenceState.Unsafe; } } - ///

- /// Is the scene added and enabled in Build Settings? - /// - /// Throws if is false. - /// Throws if is false. - /// - /// Only check this property if you need partial validations, as this property alone does not communicate whether this is absolutely safe to use.

- /// If you only need to check if it is completely safe to use a without knowing where exactly the problem is, then only check instead. Checking only is sufficient for the majority of the use cases. - /// - /// - /// - /// - public bool IsInBuildAndEnabled => BuildIndex != -1; - - ///

- /// Is this safe to use? - /// - /// - /// Checking this property alone is sufficient for the majority of the validation use cases, as this property absolutely communicates whether this is safe to use.

- /// Checking this property is equivalent to checking all partial validation properties (namely: , , and ) in the correct order, but it provides a slightly better performance. If you need those validations partially, you can check them instead of this property. Keep in mind that the use cases that require partial validation are rare and few. - /// - /// - /// - /// - public bool IsSafeToUse => - HasValue - && SceneGuidToPathMapProvider.SceneGuidToPathMap.TryGetValue(Guid, out var path) - && SceneUtility.GetBuildIndexByScenePath(path) != -1; - /// void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { diff --git a/Runtime/SceneReferenceOptionsAttribute.cs b/Runtime/SceneReferenceOptionsAttribute.cs index ab3be074..f341a4c4 100644 --- a/Runtime/SceneReferenceOptionsAttribute.cs +++ b/Runtime/SceneReferenceOptionsAttribute.cs @@ -11,15 +11,23 @@ namespace Eflatun.SceneReference public class SceneReferenceOptionsAttribute : Attribute { ///

- /// + /// For scene-in-build state.
+ ///
/// Defaults to . ///
- public ColoringBehaviour Coloring = ColoringBehaviour.DoNotOverride; + public ColoringBehaviour SceneInBuildColoring = ColoringBehaviour.DoNotOverride; /// - /// - /// Defaults to . + ///
+ /// Defaults to . ///
- public UtilityLineBehaviour UtilityLine = UtilityLineBehaviour.DoNotOverride; + public ToolboxBehaviour Toolbox = ToolboxBehaviour.DoNotOverride; + + /// + /// For addressable scenes.
+ ///
+ /// Defaults to . + ///
+ public ColoringBehaviour AddressableColoring = ColoringBehaviour.DoNotOverride; } } diff --git a/Runtime/SceneReferenceState.cs b/Runtime/SceneReferenceState.cs new file mode 100644 index 00000000..275587b8 --- /dev/null +++ b/Runtime/SceneReferenceState.cs @@ -0,0 +1,32 @@ +using JetBrains.Annotations; + +namespace Eflatun.SceneReference +{ + /// + /// State of a . + /// + /// : + /// : + /// : + /// + /// + [PublicAPI] + public enum SceneReferenceState + { + /// + /// The is not safe to use. Something is wrong. + /// + Unsafe = 0, + + /// + /// The is safe to use, and it references a regular scene. + /// + Regular = 1, + + /// + /// The is safe to use, and it references an addressable scene. + /// This state is only possible if the addressables support is enabled. + /// + Addressable = 2, + } +} diff --git a/Runtime/SceneReferenceState.cs.meta b/Runtime/SceneReferenceState.cs.meta new file mode 100644 index 00000000..9477eb69 --- /dev/null +++ b/Runtime/SceneReferenceState.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 10fc56d7bdce412fba330a3c6d19f43e +timeCreated: 1686400008 \ No newline at end of file diff --git a/Runtime/UtilityLineBehaviour.cs b/Runtime/ToolboxBehaviour.cs similarity index 55% rename from Runtime/UtilityLineBehaviour.cs rename to Runtime/ToolboxBehaviour.cs index fd94e2ea..9235e307 100644 --- a/Runtime/UtilityLineBehaviour.cs +++ b/Runtime/ToolboxBehaviour.cs @@ -3,10 +3,10 @@ namespace Eflatun.SceneReference { /// - /// The inspector utility line override behaviour for scene-in-build validation of fields. + /// The inspector toolbox button override behaviour for fields. /// [PublicAPI] - public enum UtilityLineBehaviour + public enum ToolboxBehaviour { /// /// Use the project settings. @@ -14,12 +14,12 @@ public enum UtilityLineBehaviour DoNotOverride = 0, /// - /// Force enable the utility line, disregard project settings. + /// Force enable the toolbox button, disregard project settings. /// Enabled = 1, /// - /// Force disable the utility line, disregard project settings. + /// Force disable the toolbox button, disregard project settings. /// Disabled = 2 } diff --git a/Runtime/UtilityLineBehaviour.cs.meta b/Runtime/ToolboxBehaviour.cs.meta similarity index 100% rename from Runtime/UtilityLineBehaviour.cs.meta rename to Runtime/ToolboxBehaviour.cs.meta diff --git a/Runtime/Utility/Utils.cs b/Runtime/Utility/Utils.cs index 61ce1d4b..0b8a6240 100644 --- a/Runtime/Utility/Utils.cs +++ b/Runtime/Utility/Utils.cs @@ -13,6 +13,17 @@ internal static class Utils /// public const string AllZeroGuid = "00000000000000000000000000000000"; + /// + /// Returns whether the addressables package is installed in the project. + /// + public static bool IsAddressablesPackagePresent => +#if ESR_ADDRESSABLES + true +#else // ESR_ADDRESSABLES + false +#endif // ESR_ADDRESSABLES + ; + /// /// Returns the given without file extension. /// diff --git a/package.json b/package.json index a29d403c..ddaad63d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.eflatun.scenereference", - "version": "2.1.0", + "version": "3.0.0", "displayName": "Eflatun.SceneReference", "description": "Scene References for Runtime and Editor. Strongly typed, robust, and reliable. Provides Asset GUID, Scene Path, Build Index, and Scene Name.\nhttps://github.com/starikcetin/Eflatun.SceneReference", "documentationUrl": "https://github.com/starikcetin/Eflatun.SceneReference/blob/main/README.md",