com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md +92 -0
- package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
- package/Docs/INDEX.md +11 -1
- package/Docs/Images/random_generators.svg +7 -7
- package/Docs/RANDOM_PERFORMANCE.md +17 -14
- package/Docs/REFLECTION_HELPERS.md +84 -1
- package/Docs/REFLECTION_PERFORMANCE.md +169 -0
- package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
- package/Docs/RELATIONAL_COMPONENTS.md +6 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Editor/Core/Helper/AnimationEventHelpers.cs +1 -1
- package/Editor/Sprites/AnimationCopier.cs +1 -1
- package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
- package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
- package/Editor/Sprites/TextureResizerWizard.cs +4 -3
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
- package/README.md +33 -18
- package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
- package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
- package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
- package/Runtime/Core/CodeGen.meta +8 -0
- package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
- package/Runtime/Core/Extension/IListExtensions.cs +720 -12
- package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
- package/Runtime/Core/Helper/Objects.cs +1 -1
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
- package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +2 -3
- package/Runtime/Core/Math/Line2D.cs +2 -4
- package/Runtime/Core/Math/Line3D.cs +2 -4
- package/Runtime/Core/Random/AbstractRandom.cs +52 -5
- package/Runtime/Core/Random/DotNetRandom.cs +3 -3
- package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
- package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
- package/Runtime/Core/Random/IllusionFlow.cs +3 -3
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
- package/Runtime/Core/Random/PcgRandom.cs +6 -6
- package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
- package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
- package/Runtime/Core/Random/RomuDuo.cs +3 -3
- package/Runtime/Core/Random/SplitMix64.cs +3 -3
- package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
- package/Runtime/Core/Random/StormDropRandom.cs +271 -0
- package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
- package/Runtime/Core/Random/UnityRandom.cs +3 -3
- package/Runtime/Core/Random/WyRandom.cs +6 -4
- package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
- package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
- package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
- package/Runtime/Tags/CosmeticEffectData.cs +1 -1
- package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
- package/Tests/Editor/Helper/HelpersTests.cs +2 -2
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
- package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
- package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
- package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
- package/Tests/Editor/Tags.meta +8 -0
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
- package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
- package/Tests/Runtime/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
- package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
- package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
- package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
- package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
- package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
- package/package.json +1 -1
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
|
@@ -598,8 +598,8 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
598
598
|
// Prefer Assets-relative paths for UI components that expect them
|
|
599
599
|
if (!candidate.StartsWith("Assets", StringComparison.OrdinalIgnoreCase))
|
|
600
600
|
{
|
|
601
|
-
string assetsRoot = Application.dataPath.
|
|
602
|
-
string full = candidate.
|
|
601
|
+
string assetsRoot = Application.dataPath.SanitizePath();
|
|
602
|
+
string full = candidate.SanitizePath();
|
|
603
603
|
if (full.StartsWith(assetsRoot, StringComparison.OrdinalIgnoreCase))
|
|
604
604
|
{
|
|
605
605
|
candidate = "Assets" + full.Substring(assetsRoot.Length);
|
|
@@ -622,10 +622,10 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
// Ensure Assets-relative if possible
|
|
625
|
-
string path = assetsRelativeDir.
|
|
625
|
+
string path = assetsRelativeDir.SanitizePath();
|
|
626
626
|
if (!path.StartsWith("Assets", StringComparison.OrdinalIgnoreCase))
|
|
627
627
|
{
|
|
628
|
-
string assetsRoot = Application.dataPath.
|
|
628
|
+
string assetsRoot = Application.dataPath.SanitizePath();
|
|
629
629
|
string full = path;
|
|
630
630
|
if (full.StartsWith(assetsRoot, StringComparison.OrdinalIgnoreCase))
|
|
631
631
|
{
|
|
@@ -7,6 +7,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
7
7
|
using System.Text.RegularExpressions;
|
|
8
8
|
using UnityEditor;
|
|
9
9
|
using UnityEngine;
|
|
10
|
+
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
10
11
|
|
|
11
12
|
/// <summary>
|
|
12
13
|
/// Public API to apply SpriteSettings profiles to assets. Mirrors the window logic
|
|
@@ -83,7 +84,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
83
84
|
|
|
84
85
|
private static string SanitizePath(string p)
|
|
85
86
|
{
|
|
86
|
-
return string.IsNullOrEmpty(p) ? p : p.
|
|
87
|
+
return string.IsNullOrEmpty(p) ? p : p.SanitizePath();
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
public static SpriteSettings FindMatchingSettings(
|
|
@@ -10,6 +10,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
10
10
|
using UnityEngine;
|
|
11
11
|
using UnityEngine.Serialization;
|
|
12
12
|
using WallstopStudios.UnityHelpers.Core.Extension;
|
|
13
|
+
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
13
14
|
using WallstopStudios.UnityHelpers.Utils;
|
|
14
15
|
using Object = UnityEngine.Object;
|
|
15
16
|
|
|
@@ -299,7 +300,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
299
300
|
{
|
|
300
301
|
string fileName = Path.GetFileName(assetPath);
|
|
301
302
|
finalAssetPath = Path.Combine(outputDirAssetPath, fileName)
|
|
302
|
-
.
|
|
303
|
+
.SanitizePath();
|
|
303
304
|
EnsureDirectory(finalAssetPath);
|
|
304
305
|
}
|
|
305
306
|
|
|
@@ -420,12 +421,12 @@ namespace WallstopStudios.UnityHelpers.Editor.Sprites
|
|
|
420
421
|
0,
|
|
421
422
|
Application.dataPath.Length - "Assets".Length
|
|
422
423
|
);
|
|
423
|
-
return Path.Combine(projectRoot, assetPath).
|
|
424
|
+
return Path.Combine(projectRoot, assetPath).SanitizePath();
|
|
424
425
|
}
|
|
425
426
|
|
|
426
427
|
private static void EnsureDirectory(string assetPath)
|
|
427
428
|
{
|
|
428
|
-
string dirAsset = Path.GetDirectoryName(assetPath)?.
|
|
429
|
+
string dirAsset = Path.GetDirectoryName(assetPath)?.SanitizePath();
|
|
429
430
|
if (string.IsNullOrEmpty(dirAsset))
|
|
430
431
|
{
|
|
431
432
|
return;
|
|
@@ -214,7 +214,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
214
214
|
string assetName = Path.GetFileName(targetAssetPath);
|
|
215
215
|
|
|
216
216
|
// Ensure the target parent folder exists and use its exact-cased path
|
|
217
|
-
string targetParent = Path.GetDirectoryName(normalizedTarget)?.
|
|
217
|
+
string targetParent = Path.GetDirectoryName(normalizedTarget)?.SanitizePath();
|
|
218
218
|
if (!string.IsNullOrWhiteSpace(targetParent))
|
|
219
219
|
{
|
|
220
220
|
string resolvedParent = EnsureAndResolveFolderPath(targetParent);
|
|
@@ -303,7 +303,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
// Final guard: ensure parent exists just before moving
|
|
306
|
-
string parent = Path.GetDirectoryName(normalizedTarget)?.
|
|
306
|
+
string parent = Path.GetDirectoryName(normalizedTarget)?.SanitizePath();
|
|
307
307
|
if (!string.IsNullOrWhiteSpace(parent) && !AssetDatabase.IsValidFolder(parent))
|
|
308
308
|
{
|
|
309
309
|
string ensured = EnsureAndResolveFolderPath(parent);
|
|
@@ -324,7 +324,7 @@ namespace WallstopStudios.UnityHelpers.Editor.Utils
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
// Retry after ensuring parent and performing save/refresh if parent folder may not yet be registered
|
|
327
|
-
string parentDir = Path.GetDirectoryName(normalizedTarget)?.
|
|
327
|
+
string parentDir = Path.GetDirectoryName(normalizedTarget)?.SanitizePath();
|
|
328
328
|
bool retried = false;
|
|
329
329
|
if (!string.IsNullOrWhiteSpace(parentDir))
|
|
330
330
|
{
|
package/README.md
CHANGED
|
@@ -454,24 +454,27 @@ Already read the [Top 5 Time-Savers](#-top-5-time-savers)? Jump directly to the
|
|
|
454
454
|
|
|
455
455
|
### Random Number Generators
|
|
456
456
|
|
|
457
|
-
Unity Helpers includes **
|
|
457
|
+
Unity Helpers includes **15 high-quality random number generators**, all implementing a rich `IRandom` interface:
|
|
458
458
|
|
|
459
459
|
#### Available Generators
|
|
460
460
|
|
|
461
|
-
| Generator | Speed | Quality | Use Case
|
|
462
|
-
| ------------------------------- | --------- | --------- |
|
|
463
|
-
| **IllusionFlow** ⭐ | Fast
|
|
464
|
-
| **PcgRandom**
|
|
465
|
-
| **
|
|
466
|
-
| **
|
|
467
|
-
| **
|
|
468
|
-
| **
|
|
469
|
-
| **
|
|
470
|
-
| **
|
|
471
|
-
| **
|
|
472
|
-
| **
|
|
473
|
-
| **
|
|
474
|
-
| **
|
|
461
|
+
| Generator | Speed | Quality | Use Case |
|
|
462
|
+
| ------------------------------- | --------- | --------- | ------------------------------------------ |
|
|
463
|
+
| **IllusionFlow** ⭐ | Very Fast | Excellent | Default choice (via PRNG.Instance) |
|
|
464
|
+
| **PcgRandom** ⭐ | Very Fast | Excellent | Deterministic gameplay; explicit seeding |
|
|
465
|
+
| **FlurryBurstRandom** | Very Fast | Excellent | High-quality PCG/Xoshiro alternative |
|
|
466
|
+
| **RomuDuo** | Very Fast | Good | Maximum performance needed |
|
|
467
|
+
| **LinearCongruentialGenerator** | Fastest | Fair | Simple, fast generation |
|
|
468
|
+
| **XorShiftRandom** | Very Fast | Good | General purpose |
|
|
469
|
+
| **XoroShiroRandom** | Very Fast | Good | General purpose |
|
|
470
|
+
| **SplitMix64** | Very Fast | Good | Initialization, hashing |
|
|
471
|
+
| **StormDropRandom** | Fast | Excellent | Large-buffer streams for heavy simulations |
|
|
472
|
+
| **PhotonSpinRandom** | Moderate | Excellent | Bulk generation; long non-overlapping runs |
|
|
473
|
+
| **SquirrelRandom** | Moderate | Good | Hash-based generation |
|
|
474
|
+
| **WyRandom** | Moderate | Good | Hashing applications |
|
|
475
|
+
| **DotNetRandom** | Moderate | Good | .NET compatibility |
|
|
476
|
+
| **SystemRandom** | Slow | Good | Backward compatibility |
|
|
477
|
+
| **UnityRandom** | Very Slow | Good | Unity compatibility |
|
|
475
478
|
|
|
476
479
|
⭐ **Recommended**: Use `PRNG.Instance` (currently IllusionFlow)
|
|
477
480
|
|
|
@@ -657,6 +660,7 @@ public class Enemy : MonoBehaviour
|
|
|
657
660
|
```
|
|
658
661
|
|
|
659
662
|
See the in-depth guide: [Relational Components](Docs/RELATIONAL_COMPONENTS.md).
|
|
663
|
+
Performance snapshots: [Relational Component Performance Benchmarks](Docs/RELATIONAL_COMPONENT_PERFORMANCE.md).
|
|
660
664
|
|
|
661
665
|
---
|
|
662
666
|
|
|
@@ -1120,9 +1124,16 @@ Unity Helpers is built with performance as a top priority:
|
|
|
1120
1124
|
|
|
1121
1125
|
**Reflection:**
|
|
1122
1126
|
|
|
1123
|
-
-
|
|
1124
|
-
- Safe for IL2CPP and AOT platforms
|
|
1125
|
-
-
|
|
1127
|
+
- Cached delegates are 10-100x faster than raw `System.Reflection` (boxed scenarios improve the most)
|
|
1128
|
+
- Safe for IL2CPP and AOT platforms; capability overrides (`ReflectionHelpers.OverrideReflectionCapabilities`) let tests force expression/IL fallbacks
|
|
1129
|
+
- Run the benchmarks via **ReflectionPerformanceTests.Benchmark** (EditMode Test Runner) and commit the updated markdown section
|
|
1130
|
+
- [📘 Reflection Performance Guide](Docs/ReflectionPerformance.md) and [📊 Benchmarks](Docs/REFLECTION_PERFORMANCE.md)
|
|
1131
|
+
|
|
1132
|
+
**List Sorting:**
|
|
1133
|
+
|
|
1134
|
+
- Multiple adaptive algorithms (`Ghost`, `Meteor`, `Power`, `Grail`, `Pattern-Defeating QuickSort`, `Insertion`) tuned for `IList<T>`
|
|
1135
|
+
- Deterministic datasets (sorted, nearly sorted, shuffled) across sizes from 100 to 1,000,000
|
|
1136
|
+
- [📊 IList Sorting Performance Benchmarks](Docs/ILIST_SORTING_PERFORMANCE.md)
|
|
1126
1137
|
|
|
1127
1138
|
---
|
|
1128
1139
|
|
|
@@ -1155,8 +1166,12 @@ Unity Helpers is built with performance as a top priority:
|
|
|
1155
1166
|
|
|
1156
1167
|
**Performance & Reference:**
|
|
1157
1168
|
|
|
1169
|
+
- Reflection Performance Guide — [Reflection Performance](Docs/ReflectionPerformance.md)
|
|
1170
|
+
- Reflection AOT/Burst Validation — [IL2CPP & Burst Validation](Docs/ReflectionAotBurstValidation.md)
|
|
1171
|
+
- Reflection Benchmark CI Proposal — [Benchmark CI Proposal](Docs/ReflectionBenchmarkCIProposal.md)
|
|
1158
1172
|
- Random Performance — [Random Performance](Docs/RANDOM_PERFORMANCE.md)
|
|
1159
1173
|
- Reflection Helpers — [Reflection Helpers](Docs/REFLECTION_HELPERS.md)
|
|
1174
|
+
- IList Sorting Performance — [IList Sorting Performance](Docs/ILIST_SORTING_PERFORMANCE.md)
|
|
1160
1175
|
|
|
1161
1176
|
**Project Info:**
|
|
1162
1177
|
|
|
@@ -84,6 +84,12 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
84
84
|
/// </summary>
|
|
85
85
|
internal static class RelationalComponentProcessor
|
|
86
86
|
{
|
|
87
|
+
private static readonly MethodInfo CreateFieldAccessorGenericMethod =
|
|
88
|
+
typeof(RelationalComponentProcessor).GetMethod(
|
|
89
|
+
nameof(CreateFieldAccessorGeneric),
|
|
90
|
+
BindingFlags.NonPublic | BindingFlags.Static
|
|
91
|
+
);
|
|
92
|
+
|
|
87
93
|
internal enum FieldKind : byte
|
|
88
94
|
{
|
|
89
95
|
Single = 0,
|
|
@@ -168,42 +174,135 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
168
174
|
{
|
|
169
175
|
public readonly FieldInfo field;
|
|
170
176
|
public readonly TAttribute attribute;
|
|
171
|
-
|
|
172
|
-
|
|
177
|
+
private readonly FieldAccessor accessor;
|
|
178
|
+
private readonly FilterParameters filters;
|
|
173
179
|
public readonly FieldKind kind;
|
|
174
180
|
public readonly Type elementType;
|
|
175
181
|
public readonly Func<int, Array> arrayCreator;
|
|
176
182
|
public readonly Func<int, IList> listCreator;
|
|
177
183
|
public readonly Func<int, object> hashSetCreator;
|
|
178
184
|
public readonly Action<object, object> hashSetAdder;
|
|
185
|
+
public readonly Action<object> hashSetClearer;
|
|
179
186
|
public readonly bool isInterface;
|
|
180
187
|
|
|
181
188
|
public FieldMetadata(
|
|
182
189
|
FieldInfo field,
|
|
183
190
|
TAttribute attribute,
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
FilterParameters filters,
|
|
192
|
+
FieldAccessor accessor,
|
|
186
193
|
FieldKind kind,
|
|
187
194
|
Type elementType,
|
|
188
195
|
Func<int, Array> arrayCreator,
|
|
189
196
|
Func<int, IList> listCreator,
|
|
190
197
|
Func<int, object> hashSetCreator,
|
|
191
198
|
Action<object, object> hashSetAdder,
|
|
199
|
+
Action<object> hashSetClearer,
|
|
192
200
|
bool isInterface
|
|
193
201
|
)
|
|
194
202
|
{
|
|
195
203
|
this.field = field;
|
|
196
204
|
this.attribute = attribute;
|
|
197
|
-
this.
|
|
198
|
-
this.
|
|
205
|
+
this.accessor = accessor ?? FieldAccessor.Null;
|
|
206
|
+
this.filters = filters;
|
|
199
207
|
this.kind = kind;
|
|
200
208
|
this.elementType = elementType;
|
|
201
209
|
this.arrayCreator = arrayCreator;
|
|
202
210
|
this.listCreator = listCreator;
|
|
203
211
|
this.hashSetCreator = hashSetCreator;
|
|
204
212
|
this.hashSetAdder = hashSetAdder;
|
|
213
|
+
this.hashSetClearer = hashSetClearer;
|
|
205
214
|
this.isInterface = isInterface;
|
|
206
215
|
}
|
|
216
|
+
|
|
217
|
+
public bool HasFilters => this.filters.RequiresPostProcessing;
|
|
218
|
+
|
|
219
|
+
public FilterParameters Filters => this.filters;
|
|
220
|
+
|
|
221
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
222
|
+
public object GetValue(Component component)
|
|
223
|
+
{
|
|
224
|
+
return this.accessor.Get(component);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
228
|
+
public void SetValue(Component component, object value)
|
|
229
|
+
{
|
|
230
|
+
this.accessor.Set(component, value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
internal abstract class FieldAccessor
|
|
235
|
+
{
|
|
236
|
+
public static readonly FieldAccessor Null = new NullFieldAccessor();
|
|
237
|
+
|
|
238
|
+
public abstract object Get(Component component);
|
|
239
|
+
public abstract void Set(Component component, object value);
|
|
240
|
+
|
|
241
|
+
private sealed class NullFieldAccessor : FieldAccessor
|
|
242
|
+
{
|
|
243
|
+
public override object Get(Component component)
|
|
244
|
+
{
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public override void Set(Component component, object value) { }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private sealed class FieldAccessor<TComponent, TValue> : FieldAccessor
|
|
253
|
+
where TComponent : Component
|
|
254
|
+
{
|
|
255
|
+
private readonly FieldSetter<TComponent, TValue> setter;
|
|
256
|
+
private readonly Func<TComponent, TValue> getter;
|
|
257
|
+
|
|
258
|
+
public FieldAccessor(FieldInfo field)
|
|
259
|
+
{
|
|
260
|
+
this.setter = ReflectionHelpers.GetFieldSetter<TComponent, TValue>(field);
|
|
261
|
+
this.getter = ReflectionHelpers.GetFieldGetter<TComponent, TValue>(field);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
public override object Get(Component component)
|
|
265
|
+
{
|
|
266
|
+
if (component == null)
|
|
267
|
+
{
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
TComponent typedComponent = (TComponent)component;
|
|
272
|
+
return this.getter(typedComponent);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public override void Set(Component component, object value)
|
|
276
|
+
{
|
|
277
|
+
if (component == null)
|
|
278
|
+
{
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
TComponent typedComponent = (TComponent)component;
|
|
283
|
+
TValue typedValue = value != null ? (TValue)value : default;
|
|
284
|
+
this.setter(ref typedComponent, typedValue);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private static FieldAccessor CreateFieldAccessor(Type componentType, FieldInfo field)
|
|
289
|
+
{
|
|
290
|
+
if (componentType == null || !typeof(Component).IsAssignableFrom(componentType))
|
|
291
|
+
{
|
|
292
|
+
return FieldAccessor.Null;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
MethodInfo generic = CreateFieldAccessorGenericMethod.MakeGenericMethod(
|
|
296
|
+
componentType,
|
|
297
|
+
field.FieldType
|
|
298
|
+
);
|
|
299
|
+
return (FieldAccessor)generic.Invoke(null, new object[] { field });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private static FieldAccessor CreateFieldAccessorGeneric<TComponent, TValue>(FieldInfo field)
|
|
303
|
+
where TComponent : Component
|
|
304
|
+
{
|
|
305
|
+
return new FieldAccessor<TComponent, TValue>(field);
|
|
207
306
|
}
|
|
208
307
|
|
|
209
308
|
internal static FieldMetadata<TAttribute>[] GetFieldMetadata<TAttribute>(Type componentType)
|
|
@@ -275,6 +374,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
275
374
|
Func<int, IList> listCreator = null;
|
|
276
375
|
Func<int, object> hashSetCreator = null;
|
|
277
376
|
Action<object, object> hashSetAdder = null;
|
|
377
|
+
Action<object> hashSetClearer = null;
|
|
278
378
|
|
|
279
379
|
switch (kind)
|
|
280
380
|
{
|
|
@@ -291,6 +391,9 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
291
391
|
resolvedElementType
|
|
292
392
|
);
|
|
293
393
|
hashSetAdder = ReflectionHelpers.GetHashSetAdder(resolvedElementType);
|
|
394
|
+
hashSetClearer = ReflectionHelpers.GetHashSetClearer(
|
|
395
|
+
resolvedElementType
|
|
396
|
+
);
|
|
294
397
|
break;
|
|
295
398
|
}
|
|
296
399
|
|
|
@@ -304,18 +407,21 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
304
407
|
)
|
|
305
408
|
);
|
|
306
409
|
|
|
410
|
+
FilterParameters filters = new(attribute);
|
|
411
|
+
|
|
307
412
|
result.Add(
|
|
308
413
|
new FieldMetadata<TAttribute>(
|
|
309
414
|
field,
|
|
310
415
|
attribute,
|
|
311
|
-
|
|
312
|
-
|
|
416
|
+
filters,
|
|
417
|
+
CreateFieldAccessor(componentType, field),
|
|
313
418
|
kind,
|
|
314
419
|
resolvedElementType,
|
|
315
420
|
arrayCreator,
|
|
316
421
|
listCreator,
|
|
317
422
|
hashSetCreator,
|
|
318
423
|
hashSetAdder,
|
|
424
|
+
hashSetClearer,
|
|
319
425
|
isInterface
|
|
320
426
|
)
|
|
321
427
|
);
|
|
@@ -343,6 +449,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
343
449
|
Func<int, IList> listCreator = null;
|
|
344
450
|
Func<int, object> hashSetCreator = null;
|
|
345
451
|
Action<object, object> hashSetAdder = null;
|
|
452
|
+
Action<object> hashSetClearer = null;
|
|
346
453
|
|
|
347
454
|
switch (kind)
|
|
348
455
|
{
|
|
@@ -357,6 +464,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
357
464
|
elementType
|
|
358
465
|
);
|
|
359
466
|
hashSetAdder = ReflectionHelpers.GetHashSetAdder(elementType);
|
|
467
|
+
hashSetClearer = ReflectionHelpers.GetHashSetClearer(elementType);
|
|
360
468
|
break;
|
|
361
469
|
}
|
|
362
470
|
|
|
@@ -367,18 +475,21 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
367
475
|
|| (!elementType.IsSealed && elementType != typeof(Component))
|
|
368
476
|
);
|
|
369
477
|
|
|
478
|
+
FilterParameters filters = new(attribute);
|
|
479
|
+
|
|
370
480
|
return (FieldMetadata<TAttribute>?)
|
|
371
481
|
new FieldMetadata<TAttribute>(
|
|
372
482
|
field,
|
|
373
483
|
attribute,
|
|
374
|
-
|
|
375
|
-
|
|
484
|
+
filters,
|
|
485
|
+
CreateFieldAccessor(componentType, field),
|
|
376
486
|
kind,
|
|
377
487
|
elementType,
|
|
378
488
|
arrayCreator,
|
|
379
489
|
listCreator,
|
|
380
490
|
hashSetCreator,
|
|
381
491
|
hashSetAdder,
|
|
492
|
+
hashSetClearer,
|
|
382
493
|
isInterface
|
|
383
494
|
);
|
|
384
495
|
})
|
|
@@ -414,7 +525,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
414
525
|
|
|
415
526
|
internal static bool ShouldSkipAssignment<TAttribute>(
|
|
416
527
|
FieldMetadata<TAttribute> metadata,
|
|
417
|
-
|
|
528
|
+
Component component
|
|
418
529
|
)
|
|
419
530
|
where TAttribute : BaseRelationalComponentAttribute
|
|
420
531
|
{
|
|
@@ -423,7 +534,7 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
423
534
|
return false;
|
|
424
535
|
}
|
|
425
536
|
|
|
426
|
-
object currentValue = metadata.
|
|
537
|
+
object currentValue = metadata.GetValue(component);
|
|
427
538
|
|
|
428
539
|
return ValueHelpers.IsAssigned(currentValue);
|
|
429
540
|
}
|
|
@@ -453,26 +564,42 @@ namespace WallstopStudios.UnityHelpers.Core.Attributes
|
|
|
453
564
|
{
|
|
454
565
|
case FieldKind.Array:
|
|
455
566
|
|
|
456
|
-
metadata.
|
|
567
|
+
metadata.SetValue(component, metadata.arrayCreator(0));
|
|
457
568
|
|
|
458
569
|
break;
|
|
459
570
|
|
|
460
571
|
case FieldKind.List:
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
572
|
+
{
|
|
573
|
+
object existing = metadata.GetValue(component);
|
|
574
|
+
if (existing is IList list)
|
|
575
|
+
{
|
|
576
|
+
list.Clear();
|
|
577
|
+
}
|
|
578
|
+
else
|
|
579
|
+
{
|
|
580
|
+
metadata.SetValue(component, metadata.listCreator(0));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
464
583
|
break;
|
|
465
584
|
|
|
466
585
|
case FieldKind.HashSet:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
586
|
+
{
|
|
587
|
+
object existing = metadata.GetValue(component);
|
|
588
|
+
if (existing != null && metadata.hashSetClearer != null)
|
|
589
|
+
{
|
|
590
|
+
metadata.hashSetClearer(existing);
|
|
591
|
+
}
|
|
592
|
+
else
|
|
593
|
+
{
|
|
594
|
+
metadata.SetValue(component, metadata.hashSetCreator(0));
|
|
595
|
+
}
|
|
596
|
+
}
|
|
470
597
|
break;
|
|
471
598
|
}
|
|
472
599
|
}
|
|
473
600
|
|
|
474
601
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
475
|
-
|
|
602
|
+
internal static bool PassesStateAndFilters(
|
|
476
603
|
Component candidate,
|
|
477
604
|
FilterParameters filters,
|
|
478
605
|
bool filterDisabledComponents = true
|