diff --git a/src/OECS.SourceGen/ComponentDiscoveryGenerator.cs b/src/OECS.SourceGen/ComponentDiscoveryGenerator.cs index b845fdc..c0cf19b 100644 --- a/src/OECS.SourceGen/ComponentDiscoveryGenerator.cs +++ b/src/OECS.SourceGen/ComponentDiscoveryGenerator.cs @@ -211,7 +211,8 @@ public class ComponentDiscoveryGenerator : IIncrementalGenerator sb.AppendLine($" typeName: \"{aqn}\","); sb.AppendLine($" type: typeof({fqn}),"); sb.AppendLine($" serialize: obj => MessagePackSerializer.Serialize(({fqn})obj),"); - sb.AppendLine($" deserialize: data => MessagePackSerializer.Deserialize<{fqn}>(data)"); + sb.AppendLine($" deserializeAndAdd: (world, entity, data) =>"); + sb.AppendLine($" world.AddComponent(entity, MessagePackSerializer.Deserialize<{fqn}>(data))"); sb.Append(" )"); } diff --git a/src/OECS/ComponentDescriptor.cs b/src/OECS/ComponentDescriptor.cs index d91efa8..c3f9252 100644 --- a/src/OECS/ComponentDescriptor.cs +++ b/src/OECS/ComponentDescriptor.cs @@ -23,19 +23,20 @@ public sealed class ComponentDescriptor public Func Serialize { get; } /// - /// Deserializes a MessagePack byte array into a boxed component instance. + /// Deserializes a MessagePack byte array and adds the component to the entity + /// via the typed method. No reflection. /// - public Func Deserialize { get; } + public Action DeserializeAndAdd { get; } public ComponentDescriptor( string typeName, Type type, Func serialize, - Func deserialize) + Action deserializeAndAdd) { TypeName = typeName; Type = type; Serialize = serialize; - Deserialize = deserialize; + DeserializeAndAdd = deserializeAndAdd; } } \ No newline at end of file diff --git a/src/OECS/World.cs b/src/OECS/World.cs index b5eeb65..cadf8bb 100644 --- a/src/OECS/World.cs +++ b/src/OECS/World.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.Reflection; using R3; namespace OECS; @@ -665,27 +664,6 @@ public class World : IDisposable /// internal ComponentStore Components => _components; - /// - /// Adds a boxed component to an entity by its runtime type. - /// Used by during deserialization to - /// invoke the typed without reflection. - /// - internal void AddComponentBoxed(Type componentType, Entity entity, object component) - { - // Use a cached delegate per type to avoid reflection on every call. - if (!_addComponentDelegates.TryGetValue(componentType, out var del)) - { - var method = typeof(World).GetMethod(nameof(AddComponent))! - .MakeGenericMethod(componentType); - del = (Action)Delegate.CreateDelegate( - typeof(Action), method); - _addComponentDelegates[componentType] = del; - } - del(this, entity, component); - } - - private readonly Dictionary> _addComponentDelegates = new(); - // ── Helpers ─────────────────────────────────────────────────────── private void ThrowIfNotAlive(Entity entity) diff --git a/src/OECS/WorldSerializer.cs b/src/OECS/WorldSerializer.cs index 8c45b17..4877ebf 100644 --- a/src/OECS/WorldSerializer.cs +++ b/src/OECS/WorldSerializer.cs @@ -93,12 +93,8 @@ public static class WorldSerializer $"Ensure the component type is used with the World " + $"API so the source generator can discover it."); - var component = desc.Deserialize(ce.Data) - ?? throw new InvalidOperationException( - $"Failed to deserialize component of type '{ce.TypeName}'."); - - // Use the generated typed delegate to add the component. - world.AddComponentBoxed(desc.Type, entity, component); + // Deserialize and add in one typed operation — no reflection. + desc.DeserializeAndAdd(world, entity, ce.Data); } } }