refactor: remove Source property from IRelationship
Remove the redundant `Source` property from `IRelationship` and its implementations. Since the relationship component is inherently stored on the source entity, the `Source` field was unnecessary and required manual synchronization during deserialization. This change simplifies the relationship model, reduces memory usage, and removes the need for reflection-based fixups in the serializer.
This commit is contained in:
@@ -2,19 +2,14 @@ namespace OECS;
|
||||
|
||||
/// <summary>
|
||||
/// Marker interface for components that represent a directed relationship
|
||||
/// between two entities. The component is stored on the <see cref="Source"/>
|
||||
/// entity and points to the <see cref="Target"/> entity.
|
||||
/// between two entities. The component is stored on the source entity
|
||||
/// (the entity it's added to) and points to the <see cref="Target"/> entity.
|
||||
///
|
||||
/// The <see cref="World"/> automatically maintains a reverse index so that
|
||||
/// all sources pointing to a given target can be looked up efficiently.
|
||||
/// </summary>
|
||||
public interface IRelationship
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity that owns this relationship component.
|
||||
/// </summary>
|
||||
Entity Source { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that this relationship points to.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,7 +14,6 @@ namespace OECS;
|
||||
/// // Define a "ChildOf" relationship between a child and a parent entity.
|
||||
/// world.AddComponent(child, new Relationship<ChildOf, Parent>
|
||||
/// {
|
||||
/// Source = child,
|
||||
/// Target = parent
|
||||
/// });
|
||||
///
|
||||
@@ -27,15 +26,9 @@ namespace OECS;
|
||||
public struct Relationship<TSelf, TTarget> : IRelationship
|
||||
where TSelf : struct where TTarget : struct
|
||||
{
|
||||
/// <summary>
|
||||
/// The entity that owns this relationship component.
|
||||
/// </summary>
|
||||
[Key(0)]
|
||||
public Entity Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that this relationship points to.
|
||||
/// </summary>
|
||||
[Key(1)]
|
||||
[Key(0)]
|
||||
public Entity Target { get; set; }
|
||||
}
|
||||
|
||||
+1
-12
@@ -203,19 +203,8 @@ public class World : IDisposable
|
||||
ThrowIfNotAlive(entity);
|
||||
|
||||
// If replacing an existing relationship, remove the old index entry first.
|
||||
if (component is IRelationship newRel)
|
||||
if (component is IRelationship)
|
||||
{
|
||||
// Guard against mismatched Source: the relationship must be stored
|
||||
// on the entity it claims as its Source, otherwise the reverse
|
||||
// index becomes corrupted.
|
||||
if (newRel.Source != entity)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"Relationship of type {typeof(T).Name} has Source={newRel.Source} " +
|
||||
$"but is being added to entity {entity}. " +
|
||||
$"The Source must match the entity the component is added to.");
|
||||
}
|
||||
|
||||
if (_components.TryGet<T>(entity, out var old))
|
||||
{
|
||||
var oldRel = (IRelationship)(object)old;
|
||||
|
||||
+1
-24
@@ -1,4 +1,3 @@
|
||||
using System.Reflection;
|
||||
using MessagePack;
|
||||
|
||||
namespace OECS;
|
||||
@@ -94,32 +93,10 @@ public static class WorldSerializer
|
||||
$"Ensure the component type is used with the World " +
|
||||
$"API so the source generator can discover it.");
|
||||
|
||||
// Deserialize and fix up IRelationship.Source before adding.
|
||||
// Deserialize and add via the typed internal method.
|
||||
var component = desc.Deserialize(ce.Data);
|
||||
if (component is IRelationship rel)
|
||||
FixupRelationshipSource(rel, entity);
|
||||
|
||||
// Add via the typed internal method — no reflection for the add itself.
|
||||
world.AddComponentBoxed(entity, component, desc.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the Source field on a deserialized IRelationship to match
|
||||
/// the entity it's being restored to. Uses a small cache of PropertyInfo
|
||||
/// to avoid repeated reflection lookups.
|
||||
/// </summary>
|
||||
private static void FixupRelationshipSource(IRelationship rel, Entity entity)
|
||||
{
|
||||
var type = rel.GetType();
|
||||
if (!_sourcePropCache.TryGetValue(type, out var prop))
|
||||
{
|
||||
prop = type.GetProperty("Source");
|
||||
_sourcePropCache[type] = prop;
|
||||
}
|
||||
prop?.SetValue(rel, entity);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<Type, PropertyInfo?> _sourcePropCache = new();
|
||||
}
|
||||
Reference in New Issue
Block a user