diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4c4f7849..e166da2d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - unity: [2022.3.12f1, 2022.3.62f3, 6000.0.58f2, 6000.0.63f1, 6000.1.17f1, 6000.2.6f2, 6000.2.15f1, 6000.3.0f1, 6000.3.12f1, 6000.4.0f1, 6000.4.1f1] + unity: [2022.3.12f1, 2022.3.62f3, 6000.0.58f2, 6000.0.63f1, 6000.1.17f1, 6000.2.6f2, 6000.2.15f1, 6000.3.0f1, 6000.3.12f1, 6000.4.0f1, 6000.4.12f1, 6000.5.0f1, 6000.5.5f1] steps: - uses: actions/checkout@v4 with: diff --git a/CHANGELOG.md b/CHANGELOG.md index 087e1c88..6c12762e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## Version 1.2.2 + +### Unity 6000.4 and 6000.5 support + +- Added version-aware compatibility extensions for the APIs Unity changed in 6000.4: `Object.GetInstanceID()` (obsolete in 6000.4, removed in 6000.5), `Scene.handle`, and `SceneHierarchyWindow.SetExpanded` (now takes an `EntityId`). Context identity and hierarchy expansion now work across all supported Unity versions. +- Widened context identity keys from `int` to `long` so the new entity and scene handle values are stored without truncation. +- Extended the CI test matrix and tested-versions docs to cover Unity 6000.4 and 6000.5. + +### Fixes + +- Fixed a `NullReferenceException` in the custom inspector when a component had a field Unity cannot serialize (e.g. a multidimensional array). Such fields are now omitted from the inspector, matching Unity's default behavior. + ## Version 1.2.1 ### Fixes diff --git a/Docs/docs/reference/tested-unity-versions.md b/Docs/docs/reference/tested-unity-versions.md index eae91214..ac6a6223 100644 --- a/Docs/docs/reference/tested-unity-versions.md +++ b/Docs/docs/reference/tested-unity-versions.md @@ -23,7 +23,9 @@ The test matrix is defined using minimum and maximum tested editor versions per | 6000.3.0f1 | LTS | Minimum tested Unity 6.3 | | 6000.3.12f1 | LTS | Maximum tested Unity 6.3 | | 6000.4.0f1 | Supported | Minimum tested Unity 6.4 | -| 6000.4.1f1 | Supported | Maximum tested Unity 6.4 | +| 6000.4.12f1 | Supported | Maximum tested Unity 6.4 | +| 6000.5.0f1 | Supported | Minimum tested Unity 6.5 | +| 6000.5.5f1 | Supported | Maximum tested Unity 6.5 | In-between versions will likely work, but only the above are verified in automated tests. Unity 2023 releases are skipped since they are tech stream builds (not long-term supported), and Unity does not recommend them for production. diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Data/Context/ContextIdentity.cs b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Data/Context/ContextIdentity.cs index b48dae52..92b7ddfd 100644 --- a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Data/Context/ContextIdentity.cs +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Data/Context/ContextIdentity.cs @@ -1,6 +1,7 @@ using System; using System.ComponentModel; using System.Text; +using Plugins.Saneject.Editor.Extensions; using Plugins.Saneject.Runtime.Settings; using UnityEditor; using UnityEditor.SceneManagement; @@ -24,9 +25,9 @@ public ContextIdentity(Object obj) } public ContextType Type { get; } - public int Id { get; } + public long Id { get; } public ContextType ContainerType { get; } - public int ContainerId { get; } + public long ContainerId { get; } public bool IsPrefab { get; } public override string ToString() @@ -76,32 +77,32 @@ private static ContextData GetContextData(Object obj) ? new ContextData ( type: ContextType.PrefabInstance, - key: prefabInstanceRoot.GetInstanceID(), + key: prefabInstanceRoot.GetInstanceIDCompat(), containerType: ContextType.PrefabAsset, - containerKey: prefabAssetRoot.GetInstanceID() + containerKey: prefabAssetRoot.GetInstanceIDCompat() ) // Prefab instance inside prefab asset : new ContextData ( type: ContextType.PrefabInstance, - key: prefabInstanceRoot.GetInstanceID(), + key: prefabInstanceRoot.GetInstanceIDCompat(), containerType: ContextType.SceneObject, - containerKey: gameObject.scene.handle + containerKey: gameObject.scene.GetHandleCompat() ); // Prefab instance inside scene if (isPrefabAssetObject) return new ContextData ( type: ContextType.PrefabAsset, - key: prefabAssetRoot.GetInstanceID(), + key: prefabAssetRoot.GetInstanceIDCompat(), containerType: ContextType.PrefabAsset, - containerKey: prefabAssetRoot.GetInstanceID() + containerKey: prefabAssetRoot.GetInstanceIDCompat() ); // Prefab asset return new ContextData( type: ContextType.SceneObject, - key: gameObject.scene.handle, + key: gameObject.scene.GetHandleCompat(), containerType: ContextType.SceneObject, - containerKey: gameObject.scene.handle + containerKey: gameObject.scene.GetHandleCompat() ); // Scene object } @@ -109,9 +110,9 @@ private class ContextData { public ContextData( ContextType type, - int key, + long key, ContextType containerType, - int containerKey) + long containerKey) { Type = type; Key = key; @@ -120,9 +121,9 @@ public ContextData( } public ContextType Type { get; } - public int Key { get; } + public long Key { get; } public ContextType ContainerType { get; } - public int ContainerKey { get; } + public long ContainerKey { get; } } #region Equality logic diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/ObjectExtensions.cs b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/ObjectExtensions.cs index 71e713f8..57e62b94 100644 --- a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/ObjectExtensions.cs +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/ObjectExtensions.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.ComponentModel; +using UnityEngine; namespace Plugins.Saneject.Editor.Extensions { @@ -15,5 +16,26 @@ public static T[] AsArray(this T obj) { return new[] { obj }; } + + public static long GetInstanceIDCompat(this Object obj) + { +#if UNITY_6000_4_OR_NEWER + return (long)EntityId.ToULong(obj.GetEntityId()); +#else + return (int)obj.GetInstanceID(); +#endif + } + + // Returns the object id boxed as the exact type expected by reflection calls + // against APIs whose signature changed from int to EntityId in Unity 6000.4 + // (e.g. SceneHierarchyWindow.SetExpanded). Do not use for identity keys. + public static object GetInstanceIDBoxedCompat(this Object obj) + { +#if UNITY_6000_4_OR_NEWER + return obj.GetEntityId(); +#else + return obj.GetInstanceID(); +#endif + } } } \ No newline at end of file diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/SceneExtensions.cs b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/SceneExtensions.cs new file mode 100644 index 00000000..3834c479 --- /dev/null +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/SceneExtensions.cs @@ -0,0 +1,16 @@ +using UnityEngine.SceneManagement; + +namespace Plugins.Saneject.Editor.Extensions +{ + public static class SceneExtensions + { + public static long GetHandleCompat(this Scene scene) + { +#if UNITY_6000_4_OR_NEWER + return (long)scene.handle.GetRawData(); +#else + return (int)scene.handle; +#endif + } + } +} \ No newline at end of file diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/SceneExtensions.cs.meta b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/SceneExtensions.cs.meta new file mode 100644 index 00000000..efd6b681 --- /dev/null +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Extensions/SceneExtensions.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7de48b817adf494daecbb844483ab64b +timeCreated: 1785065359 \ No newline at end of file diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Inspectors/Models/PropertyModel.cs b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Inspectors/Models/PropertyModel.cs index 8fb25660..b560c164 100644 --- a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Inspectors/Models/PropertyModel.cs +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Inspectors/Models/PropertyModel.cs @@ -52,7 +52,7 @@ public PropertyModel( ExpectedType = elementType; HasInjectAttribute = field.HasInjectAttribute(); IsReadOnly = HasInjectAttribute || field.HasReadOnlyAttribute(); - IsCollection = SerializedProperty.isArray && SerializedProperty.propertyType != SerializedPropertyType.String; + IsCollection = SerializedProperty is { isArray: true } && SerializedProperty.propertyType != SerializedPropertyType.String; Children = field.FieldType.IsNestedSerializable() && SerializedProperty != null ? field.FieldType diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Utilities/SceneHierarchyUtility.cs b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Utilities/SceneHierarchyUtility.cs index 69e84b69..31d5892d 100644 --- a/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Utilities/SceneHierarchyUtility.cs +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/Editor/Utilities/SceneHierarchyUtility.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Plugins.Saneject.Editor.Data.Context; +using Plugins.Saneject.Editor.Extensions; using Plugins.Saneject.Runtime.Settings; using UnityEditor; using UnityEngine; @@ -94,7 +95,7 @@ public static void Expand(Object[] objects) { setExpanded!.Invoke(window, new object[] { - current.gameObject.GetInstanceID(), + current.gameObject.GetInstanceIDBoxedCompat(), true }); @@ -104,7 +105,7 @@ public static void Expand(Object[] objects) // Finally expand the object itself setExpanded!.Invoke(window, new object[] { - gameObject.GetInstanceID(), + gameObject.GetInstanceIDBoxedCompat(), true }); } diff --git a/UnityProject/Saneject/Assets/Plugins/Saneject/package.json b/UnityProject/Saneject/Assets/Plugins/Saneject/package.json index 6b72807c..771651c5 100644 --- a/UnityProject/Saneject/Assets/Plugins/Saneject/package.json +++ b/UnityProject/Saneject/Assets/Plugins/Saneject/package.json @@ -2,7 +2,7 @@ "name": "com.alexanderlarsen.saneject", "author": "Alexander Larsen", "displayName": "Saneject", - "version": "1.2.1", + "version": "1.2.2", "description": "Inject dependencies in the Unity Editor, not Play Mode, by writing them directly into serialized fields at edit-time using familiar DI APIs, so everything stays visible in the Inspector, including interfaces.\n\nNo runtime container. No startup cost. No hidden wiring. No weird lifecycles. Just simple, deterministic edit-time DI that works with Unity, not around it.", "documentationUrl": "https://github.com/alexanderlarsen/Saneject/blob/main/README.md", "changelogUrl": "https://github.com/alexanderlarsen/Saneject/blob/main/CHANGELOG.md", diff --git a/UnityProject/Saneject/Assets/Tests/Saneject/Editor/Context/ContextIdentityTests.cs b/UnityProject/Saneject/Assets/Tests/Saneject/Editor/Context/ContextIdentityTests.cs index 107ab1ba..c4967e06 100644 --- a/UnityProject/Saneject/Assets/Tests/Saneject/Editor/Context/ContextIdentityTests.cs +++ b/UnityProject/Saneject/Assets/Tests/Saneject/Editor/Context/ContextIdentityTests.cs @@ -1,6 +1,6 @@ -using System; using NUnit.Framework; using Plugins.Saneject.Editor.Data.Context; +using Plugins.Saneject.Editor.Extensions; using Tests.Saneject.Fixtures.Scripts; using UnityEditor; using UnityEngine; @@ -20,9 +20,9 @@ public void SceneObject_InScene_SetsExpectedContextIdentity() // Assert Assert.That(sceneObject, Is.Not.Null); Assert.That(identity.Type, Is.EqualTo(ContextType.SceneObject)); - Assert.That(identity.Id, Is.EqualTo(Convert.ToInt32(sceneObject.gameObject.scene.handle))); + Assert.That(identity.Id, Is.EqualTo(sceneObject.gameObject.scene.GetHandleCompat())); Assert.That(identity.ContainerType, Is.EqualTo(ContextType.SceneObject)); - Assert.That(identity.ContainerId, Is.EqualTo(Convert.ToInt32(sceneObject.gameObject.scene.handle))); + Assert.That(identity.ContainerId, Is.EqualTo(sceneObject.gameObject.scene.GetHandleCompat())); Assert.That(identity.IsPrefab, Is.False); } @@ -43,9 +43,9 @@ public void PrefabInstance_InScene_SetsExpectedContextIdentity() // Assert Assert.That(prefabObject, Is.Not.Null); Assert.That(identity.Type, Is.EqualTo(ContextType.PrefabInstance)); - Assert.That(identity.Id, Is.EqualTo(prefabInstance.Root.GetInstanceID())); + Assert.That(identity.Id, Is.EqualTo(prefabInstance.Root.GetInstanceIDCompat())); Assert.That(identity.ContainerType, Is.EqualTo(ContextType.SceneObject)); - Assert.That(identity.ContainerId, Is.EqualTo(Convert.ToInt32(prefabObject.gameObject.scene.handle))); + Assert.That(identity.ContainerId, Is.EqualTo(prefabObject.gameObject.scene.GetHandleCompat())); Assert.That(identity.IsPrefab, Is.True); } finally @@ -72,9 +72,9 @@ public void PrefabAssetObject_InPrefabAsset_SetsExpectedContextIdentity() // Assert Assert.That(prefabObject, Is.Not.Null); Assert.That(identity.Type, Is.EqualTo(ContextType.PrefabAsset)); - Assert.That(identity.Id, Is.EqualTo(prefabStage.Root.GetInstanceID())); + Assert.That(identity.Id, Is.EqualTo(prefabStage.Root.GetInstanceIDCompat())); Assert.That(identity.ContainerType, Is.EqualTo(ContextType.PrefabAsset)); - Assert.That(identity.ContainerId, Is.EqualTo(prefabStage.Root.GetInstanceID())); + Assert.That(identity.ContainerId, Is.EqualTo(prefabStage.Root.GetInstanceIDCompat())); Assert.That(identity.IsPrefab, Is.True); } finally @@ -103,9 +103,9 @@ public void PrefabAssetObject_LoadedFromAssetDatabase_SetsExpectedContextIdentit Assert.That(prefabAsset, Is.Not.Null); Assert.That(prefabObject, Is.Not.Null); Assert.That(identity.Type, Is.EqualTo(ContextType.PrefabAsset)); - Assert.That(identity.Id, Is.EqualTo(prefabAsset.GetInstanceID())); + Assert.That(identity.Id, Is.EqualTo(prefabAsset.GetInstanceIDCompat())); Assert.That(identity.ContainerType, Is.EqualTo(ContextType.PrefabAsset)); - Assert.That(identity.ContainerId, Is.EqualTo(prefabAsset.GetInstanceID())); + Assert.That(identity.ContainerId, Is.EqualTo(prefabAsset.GetInstanceIDCompat())); Assert.That(identity.IsPrefab, Is.True); } finally @@ -133,9 +133,9 @@ public void PrefabInstance_InPrefabAsset_SetsExpectedContextIdentity() // Assert Assert.That(prefabObject, Is.Not.Null); Assert.That(identity.Type, Is.EqualTo(ContextType.PrefabInstance)); - Assert.That(identity.Id, Is.EqualTo(nestedInstance.Root.GetInstanceID())); + Assert.That(identity.Id, Is.EqualTo(nestedInstance.Root.GetInstanceIDCompat())); Assert.That(identity.ContainerType, Is.EqualTo(ContextType.PrefabAsset)); - Assert.That(identity.ContainerId, Is.EqualTo(hostStage.Root.GetInstanceID())); + Assert.That(identity.ContainerId, Is.EqualTo(hostStage.Root.GetInstanceIDCompat())); Assert.That(identity.IsPrefab, Is.True); } finally