com.artromskiy.deltaecs.unity 0.0.38 → 0.0.40

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.
@@ -0,0 +1,258 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using Delta.ECS;
4
+ using Unity.Properties;
5
+
6
+ namespace Delta.ECS.Unity
7
+ {
8
+ internal static class PropertyBagValueAccess
9
+ {
10
+ internal static bool TryGetValue(object container, Type containerType, string[] path, out object value)
11
+ {
12
+ value = null;
13
+ if (container == null || path == null || path.Length == 0)
14
+ {
15
+ return false;
16
+ }
17
+
18
+ IPropertyBag propertyBag = PropertyBag.GetPropertyBag(containerType);
19
+ if (propertyBag == null)
20
+ {
21
+ return false;
22
+ }
23
+
24
+ var visitor = new ReadPathVisitor(path, 0);
25
+ propertyBag.Accept(visitor, ref container);
26
+ value = visitor.Value;
27
+ return visitor.Found;
28
+ }
29
+
30
+ internal static bool TrySetValue(ref object container, Type containerType, string[] path, object value)
31
+ {
32
+ if (container == null || path == null || path.Length == 0)
33
+ {
34
+ return false;
35
+ }
36
+
37
+ IPropertyBag propertyBag = PropertyBag.GetPropertyBag(containerType);
38
+ if (propertyBag == null)
39
+ {
40
+ return false;
41
+ }
42
+
43
+ var visitor = new WritePathVisitor(path, 0, value);
44
+ propertyBag.Accept(visitor, ref container);
45
+ return visitor.Succeeded;
46
+ }
47
+
48
+ internal static string FormatPath(IReadOnlyList<string> path)
49
+ {
50
+ return string.Join("/", path);
51
+ }
52
+
53
+ internal static string[] ParsePath(string path)
54
+ {
55
+ return string.IsNullOrEmpty(path) ? Array.Empty<string>() : path.Split('/');
56
+ }
57
+
58
+ internal static void CollectEntityReferences(object container, Type containerType,
59
+ Func<Entity, string> stableIdResolver, IList<EntityReference> references)
60
+ {
61
+ if (container == null)
62
+ {
63
+ return;
64
+ }
65
+
66
+ IPropertyBag propertyBag = PropertyBag.GetPropertyBag(containerType);
67
+ if (propertyBag == null)
68
+ {
69
+ return;
70
+ }
71
+
72
+ var visitor = new EntityReferenceVisitor(stableIdResolver, references);
73
+ propertyBag.Accept(visitor, ref container);
74
+ }
75
+
76
+ private sealed class ReadPathVisitor : IPropertyBagVisitor
77
+ {
78
+ private readonly string[] _path;
79
+ private readonly int _pathIndex;
80
+
81
+ internal object Value;
82
+ internal bool Found;
83
+
84
+ internal ReadPathVisitor(string[] path, int pathIndex)
85
+ {
86
+ _path = path;
87
+ _pathIndex = pathIndex;
88
+ }
89
+
90
+ public void Visit<TContainer>(IPropertyBag<TContainer> properties, ref TContainer container)
91
+ {
92
+ foreach (var property in properties.GetProperties(ref container))
93
+ {
94
+ if (!string.Equals(property.Name, _path[_pathIndex], StringComparison.Ordinal))
95
+ {
96
+ continue;
97
+ }
98
+
99
+ object value = property.GetValue(ref container);
100
+ if (_pathIndex == _path.Length - 1)
101
+ {
102
+ Value = value;
103
+ Found = true;
104
+ }
105
+ else if (value != null)
106
+ {
107
+ IPropertyBag nestedBag = PropertyBag.GetPropertyBag(value.GetType());
108
+ if (nestedBag != null)
109
+ {
110
+ var nestedVisitor = new ReadPathVisitor(_path, _pathIndex + 1);
111
+ nestedBag.Accept(nestedVisitor, ref value);
112
+ Value = nestedVisitor.Value;
113
+ Found = nestedVisitor.Found;
114
+ }
115
+ }
116
+
117
+ return;
118
+ }
119
+ }
120
+ }
121
+
122
+ private sealed class WritePathVisitor : IPropertyBagVisitor
123
+ {
124
+ private readonly string[] _path;
125
+ private readonly int _pathIndex;
126
+ private readonly object _value;
127
+
128
+ internal bool Succeeded;
129
+
130
+ internal WritePathVisitor(string[] path, int pathIndex, object value)
131
+ {
132
+ _path = path;
133
+ _pathIndex = pathIndex;
134
+ _value = value;
135
+ }
136
+
137
+ public void Visit<TContainer>(IPropertyBag<TContainer> properties, ref TContainer container)
138
+ {
139
+ TContainer updated = container;
140
+ foreach (var property in properties.GetProperties(ref updated))
141
+ {
142
+ if (!string.Equals(property.Name, _path[_pathIndex], StringComparison.Ordinal))
143
+ {
144
+ continue;
145
+ }
146
+
147
+ if (property.IsReadOnly)
148
+ {
149
+ return;
150
+ }
151
+
152
+ if (_pathIndex == _path.Length - 1)
153
+ {
154
+ property.SetValue(ref updated, _value);
155
+ Succeeded = true;
156
+ }
157
+ else
158
+ {
159
+ object nestedValue = property.GetValue(ref updated);
160
+ if (nestedValue == null)
161
+ {
162
+ return;
163
+ }
164
+
165
+ IPropertyBag nestedBag = PropertyBag.GetPropertyBag(nestedValue.GetType());
166
+ if (nestedBag == null)
167
+ {
168
+ return;
169
+ }
170
+
171
+ var nestedVisitor = new WritePathVisitor(_path, _pathIndex + 1, _value);
172
+ nestedBag.Accept(nestedVisitor, ref nestedValue);
173
+ if (nestedVisitor.Succeeded)
174
+ {
175
+ property.SetValue(ref updated, nestedValue);
176
+ Succeeded = true;
177
+ }
178
+ }
179
+
180
+ container = updated;
181
+ return;
182
+ }
183
+ }
184
+ }
185
+
186
+ private sealed class EntityReferenceVisitor : IPropertyBagVisitor, IPropertyVisitor
187
+ {
188
+ private readonly Func<Entity, string> _stableIdResolver;
189
+ private readonly IList<EntityReference> _references;
190
+ private readonly List<string> _path = new();
191
+ private readonly HashSet<object> _activeReferences = new HashSet<object>(new ReferenceSet());
192
+
193
+ internal EntityReferenceVisitor(Func<Entity, string> stableIdResolver, IList<EntityReference> references)
194
+ {
195
+ _stableIdResolver = stableIdResolver;
196
+ _references = references;
197
+ }
198
+
199
+ public void Visit<TContainer>(IPropertyBag<TContainer> properties, ref TContainer container)
200
+ {
201
+ foreach (var property in properties.GetProperties(ref container))
202
+ {
203
+ ((IPropertyAccept<TContainer>)property).Accept(this, ref container);
204
+ }
205
+ }
206
+
207
+ public void Visit<TContainer, TValue>(Property<TContainer, TValue> property, ref TContainer container)
208
+ {
209
+ TValue value = property.GetValue(ref container);
210
+ _path.Add(property.Name);
211
+ try
212
+ {
213
+ if (typeof(TValue) == typeof(Entity))
214
+ {
215
+ string targetId = _stableIdResolver((Entity)(object)value);
216
+ if (!string.IsNullOrEmpty(targetId))
217
+ {
218
+ _references.Add(new EntityReference(FormatPath(_path), targetId));
219
+ }
220
+ }
221
+ else if (_path.Count < 24 && value != null)
222
+ {
223
+ object activeReference = value;
224
+ bool trackReference = !typeof(TValue).IsValueType;
225
+ if (!trackReference || _activeReferences.Add(activeReference))
226
+ {
227
+ try
228
+ {
229
+ object nestedValue = value;
230
+ if (PropertyBag.TryGetPropertyBagForValue(ref nestedValue, out IPropertyBag nestedBag))
231
+ {
232
+ nestedBag.Accept(this, ref nestedValue);
233
+ }
234
+ }
235
+ finally
236
+ {
237
+ if (trackReference)
238
+ {
239
+ _activeReferences.Remove(activeReference);
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+ finally
246
+ {
247
+ _path.RemoveAt(_path.Count - 1);
248
+ }
249
+ }
250
+ }
251
+
252
+ private sealed class ReferenceSet : IEqualityComparer<object>
253
+ {
254
+ public new bool Equals(object left, object right) => ReferenceEquals(left, right);
255
+ public int GetHashCode(object value) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(value);
256
+ }
257
+ }
258
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 54aae609d31564e449408ffc4cfe0ffd
@@ -1,5 +1,6 @@
1
1
  using System;
2
2
  using System.Collections.Generic;
3
+ using System.Diagnostics.CodeAnalysis;
3
4
  using Delta.ECS;
4
5
  using Delta.ECS.Integration;
5
6
  using UnityEngine;
@@ -69,6 +70,20 @@ namespace Delta.ECS.Unity
69
70
  return false;
70
71
  }
71
72
 
73
+ /// <summary>Resolves the live ECS world and entity currently associated with a Unity view.</summary>
74
+ public static bool TryGetBoundEntity(GameObject view, [NotNullWhen(true)] out World world, out Entity entity)
75
+ {
76
+ if (view != null && TryFindLiveWorld(view, out SceneWorld sceneWorld, out entity))
77
+ {
78
+ world = sceneWorld.World;
79
+ return true;
80
+ }
81
+
82
+ world = null;
83
+ entity = default;
84
+ return false;
85
+ }
86
+
72
87
  public Entity[] AuthoredEntities
73
88
  {
74
89
  get
@@ -104,7 +119,7 @@ namespace Delta.ECS.Unity
104
119
 
105
120
  internal static SceneWorld CreatePreview(SceneData sceneData)
106
121
  {
107
- var world = new World(GeneratedComponentCatalog.CreateLayoutRegistry(), DefaultEntityCapacity);
122
+ var world = new World(CreateComponentLayouts(), DefaultEntityCapacity);
108
123
  try
109
124
  {
110
125
  return new SceneWorld(world, sceneData, null,
@@ -124,7 +139,7 @@ namespace Delta.ECS.Unity
124
139
  UnityThrowHelpers.ThrowArgumentNull(nameof(authoring));
125
140
  }
126
141
 
127
- var world = new World(GeneratedComponentCatalog.CreateLayoutRegistry(), DefaultEntityCapacity);
142
+ var world = new World(CreateComponentLayouts(), DefaultEntityCapacity);
128
143
  try
129
144
  {
130
145
  return new SceneWorld(world, authoring.SceneData, authoring,
@@ -144,7 +159,7 @@ namespace Delta.ECS.Unity
144
159
  UnityThrowHelpers.ThrowArgumentNull(nameof(authoring));
145
160
  }
146
161
 
147
- var world = new World(GeneratedComponentCatalog.CreateLayoutRegistry(), RuntimeEntityCapacity);
162
+ var world = new World(CreateComponentLayouts(), RuntimeEntityCapacity);
148
163
  try
149
164
  {
150
165
  return new SceneWorld(world, authoring.SceneData, authoring,
@@ -157,6 +172,17 @@ namespace Delta.ECS.Unity
157
172
  }
158
173
  }
159
174
 
175
+ private static ComponentLayoutRegistry CreateComponentLayouts()
176
+ {
177
+ var layouts = new ComponentLayoutRegistry();
178
+ foreach (IGeneratedComponentRegistration registration in GeneratedComponentCatalog.GetRegistrations())
179
+ {
180
+ layouts.Register(registration);
181
+ }
182
+
183
+ return layouts;
184
+ }
185
+
160
186
  internal void RebuildAuthoringEntities()
161
187
  {
162
188
  ThrowIfDisposed();
@@ -212,6 +238,11 @@ namespace Delta.ECS.Unity
212
238
  continue;
213
239
  }
214
240
 
241
+ if (descriptor.IsTag)
242
+ {
243
+ continue;
244
+ }
245
+
215
246
  object value = data.CreateValue(descriptor.ValueType, ResolveEntity);
216
247
  if (_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out _)
217
248
  && !_integration.TryWrite(entity, descriptor.Id, value, snapshot.Stamp, out _, out EcsWriteError error))
@@ -341,7 +372,7 @@ namespace Delta.ECS.Unity
341
372
  {
342
373
  if (TryGetEntity(authoring, out Entity entity))
343
374
  {
344
- return _integration.TryRead(entity, descriptor.Id, out snapshot, out error);
375
+ return TryRead(entity, descriptor, out snapshot, out error);
345
376
  }
346
377
 
347
378
  snapshot = default;
@@ -349,13 +380,50 @@ namespace Delta.ECS.Unity
349
380
  return false;
350
381
  }
351
382
 
383
+ internal bool TryRead(Entity entity, ComponentDescriptor descriptor,
384
+ out ComponentSnapshot snapshot, out EcsReadError error)
385
+ => _integration.TryRead(entity, descriptor.Id, out snapshot, out error);
386
+
387
+ internal bool TryGetComponents(Entity entity, Span<ComponentId> destination, out int totalCount)
388
+ {
389
+ totalCount = 0;
390
+ if (!_integration.IsAlive(entity))
391
+ {
392
+ return false;
393
+ }
394
+
395
+ ReadOnlyMemory<ComponentDescriptor> components = _integration.Catalog.Components;
396
+ for (int index = 0; index < components.Length; index++)
397
+ {
398
+ ComponentId component = components.Span[index].Id;
399
+ if (!_world.Has(entity, component))
400
+ {
401
+ continue;
402
+ }
403
+
404
+ if (totalCount < destination.Length)
405
+ {
406
+ destination[totalCount] = component;
407
+ }
408
+
409
+ totalCount++;
410
+ }
411
+
412
+ return true;
413
+ }
414
+
352
415
  internal bool TryWrite(EntityAuthoring authoring, ComponentData data,
353
416
  ComponentDescriptor descriptor, object value, out EcsWriteError error)
354
417
  {
355
- if (!TryGetEntity(authoring, out Entity entity)
356
- || !_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out _))
418
+ if (!TryGetEntity(authoring, out Entity entity) || !_integration.IsAlive(entity))
357
419
  {
358
- error = default;
420
+ error = new EcsWriteError(EcsWriteErrorCode.EntityNotAlive);
421
+ return false;
422
+ }
423
+
424
+ if (!_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out _))
425
+ {
426
+ error = new EcsWriteError(EcsWriteErrorCode.ComponentMissing);
359
427
  return false;
360
428
  }
361
429
 
@@ -364,7 +432,15 @@ namespace Delta.ECS.Unity
364
432
  return false;
365
433
  }
366
434
 
367
- data.StoreValue(value, GetStableIdOrEmpty);
435
+ if (descriptor.IsTag)
436
+ {
437
+ data.StoreDefaultTag();
438
+ }
439
+ else
440
+ {
441
+ data.StoreValue(value, GetStableIdOrEmpty);
442
+ }
443
+
368
444
  return true;
369
445
  }
370
446
 
@@ -395,6 +471,13 @@ namespace Delta.ECS.Unity
395
471
  return false;
396
472
  }
397
473
 
474
+ if (descriptor.IsTag)
475
+ {
476
+ data.StoreDefaultTag();
477
+ error = default;
478
+ return true;
479
+ }
480
+
398
481
  object value = data.CreateValue(descriptor.ValueType, ResolveEntity);
399
482
  if (!_integration.TryRead(entity, descriptor.Id, out ComponentSnapshot snapshot, out EcsReadError readError))
400
483
  {
@@ -0,0 +1,59 @@
1
+ #nullable enable
2
+
3
+ using System;
4
+ using System.Diagnostics.CodeAnalysis;
5
+ using Delta.ECS;
6
+ using UnityEngine;
7
+
8
+ namespace Delta.ECS.Unity
9
+ {
10
+ public static class SceneWorldExtensions
11
+ {
12
+ /// <summary>Returns the entity bound to this GameObject, or the default entity when no binding exists.</summary>
13
+ /// <param name="world">The bound live ECS world, or <see langword="null"/> when no binding exists.</param>
14
+ public static Entity GetEntity(this GameObject? view, out World? world)
15
+ {
16
+ return TryGetEntity(view, out Entity entity, out world) ? entity : default;
17
+ }
18
+
19
+ /// <summary>Returns whether this GameObject is bound to a live ECS entity.</summary>
20
+ public static bool TryGetEntity(
21
+ this GameObject? view,
22
+ out Entity entity,
23
+ [NotNullWhen(true)] out World? world)
24
+ {
25
+ if (view != null && SceneWorld.TryGetBoundEntity(view, out World boundWorld, out entity))
26
+ {
27
+ world = boundWorld;
28
+ return true;
29
+ }
30
+
31
+ entity = default;
32
+ world = null;
33
+ return false;
34
+ }
35
+
36
+ /// <summary>Returns the entity bound to this MonoBehaviour, or the default entity when no binding exists.</summary>
37
+ /// <param name="world">The bound live ECS world, or <see langword="null"/> when no binding exists.</param>
38
+ public static Entity GetEntity(this MonoBehaviour? behaviour, out World? world)
39
+ {
40
+ return TryGetEntity(behaviour, out Entity entity, out world) ? entity : default;
41
+ }
42
+
43
+ /// <summary>Returns whether this MonoBehaviour is bound to a live ECS entity.</summary>
44
+ public static bool TryGetEntity(
45
+ this MonoBehaviour? behaviour,
46
+ out Entity entity,
47
+ [NotNullWhen(true)] out World? world)
48
+ {
49
+ if (behaviour != null)
50
+ {
51
+ return behaviour.gameObject.TryGetEntity(out entity, out world);
52
+ }
53
+
54
+ entity = default;
55
+ world = null;
56
+ return false;
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: c7f592731c474af6b720a8169c7f4cc1
package/Runtime.meta ADDED
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 849d9b97c54a43d6b100098ea1278f34
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.artromskiy.deltaecs.unity",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "displayName": "DeltaECS Unity",
5
5
  "description": "Unity scene authoring and editor integration for DeltaECS.",
6
6
  "unity": "6000.7",
@@ -23,9 +23,15 @@
23
23
  },
24
24
  "files": [
25
25
  "Runtime/",
26
+ "Runtime.meta",
26
27
  "Editor/",
28
+ "Editor.meta",
27
29
  "README.md",
30
+ "README.md.meta",
28
31
  "CHANGELOG.md",
29
- "LICENSE.md"
32
+ "CHANGELOG.md.meta",
33
+ "LICENSE.md",
34
+ "LICENSE.md.meta",
35
+ "package.json.meta"
30
36
  ]
31
37
  }
@@ -0,0 +1,2 @@
1
+ fileFormatVersion: 2
2
+ guid: 97f36ccebac94385907d421b09dd5287