refactor: replace boxed component addition with typed delegate
This commit is contained in:
parent
96ba037432
commit
1da0ca30d3
|
|
@ -211,7 +211,8 @@ public class ComponentDiscoveryGenerator : IIncrementalGenerator
|
||||||
sb.AppendLine($" typeName: \"{aqn}\",");
|
sb.AppendLine($" typeName: \"{aqn}\",");
|
||||||
sb.AppendLine($" type: typeof({fqn}),");
|
sb.AppendLine($" type: typeof({fqn}),");
|
||||||
sb.AppendLine($" serialize: obj => MessagePackSerializer.Serialize(({fqn})obj),");
|
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(" )");
|
sb.Append(" )");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,19 +23,20 @@ public sealed class ComponentDescriptor
|
||||||
public Func<object, byte[]> Serialize { get; }
|
public Func<object, byte[]> Serialize { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 <see cref="World.AddComponent{T}"/> method. No reflection.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<byte[], object> Deserialize { get; }
|
public Action<World, Entity, byte[]> DeserializeAndAdd { get; }
|
||||||
|
|
||||||
public ComponentDescriptor(
|
public ComponentDescriptor(
|
||||||
string typeName,
|
string typeName,
|
||||||
Type type,
|
Type type,
|
||||||
Func<object, byte[]> serialize,
|
Func<object, byte[]> serialize,
|
||||||
Func<byte[], object> deserialize)
|
Action<World, Entity, byte[]> deserializeAndAdd)
|
||||||
{
|
{
|
||||||
TypeName = typeName;
|
TypeName = typeName;
|
||||||
Type = type;
|
Type = type;
|
||||||
Serialize = serialize;
|
Serialize = serialize;
|
||||||
Deserialize = deserialize;
|
DeserializeAndAdd = deserializeAndAdd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
|
||||||
using R3;
|
using R3;
|
||||||
|
|
||||||
namespace OECS;
|
namespace OECS;
|
||||||
|
|
@ -665,27 +664,6 @@ public class World : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal ComponentStore Components => _components;
|
internal ComponentStore Components => _components;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a boxed component to an entity by its runtime type.
|
|
||||||
/// Used by <see cref="WorldSerializer"/> during deserialization to
|
|
||||||
/// invoke the typed <see cref="AddComponent{T}"/> without reflection.
|
|
||||||
/// </summary>
|
|
||||||
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<World, Entity, object>)Delegate.CreateDelegate(
|
|
||||||
typeof(Action<World, Entity, object>), method);
|
|
||||||
_addComponentDelegates[componentType] = del;
|
|
||||||
}
|
|
||||||
del(this, entity, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly Dictionary<Type, Action<World, Entity, object>> _addComponentDelegates = new();
|
|
||||||
|
|
||||||
// ── Helpers ───────────────────────────────────────────────────────
|
// ── Helpers ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
private void ThrowIfNotAlive(Entity entity)
|
private void ThrowIfNotAlive(Entity entity)
|
||||||
|
|
|
||||||
|
|
@ -93,12 +93,8 @@ public static class WorldSerializer
|
||||||
$"Ensure the component type is used with the World " +
|
$"Ensure the component type is used with the World " +
|
||||||
$"API so the source generator can discover it.");
|
$"API so the source generator can discover it.");
|
||||||
|
|
||||||
var component = desc.Deserialize(ce.Data)
|
// Deserialize and add in one typed operation — no reflection.
|
||||||
?? throw new InvalidOperationException(
|
desc.DeserializeAndAdd(world, entity, ce.Data);
|
||||||
$"Failed to deserialize component of type '{ce.TypeName}'.");
|
|
||||||
|
|
||||||
// Use the generated typed delegate to add the component.
|
|
||||||
world.AddComponentBoxed(desc.Type, entity, component);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue