95 lines
3.1 KiB
C#
95 lines
3.1 KiB
C#
using OECS;
|
|
using R3;
|
|
|
|
namespace OECS.PlayTest;
|
|
|
|
/// <summary>
|
|
/// Captures all entity changes from the World's observable API and produces
|
|
/// a compact text log on demand. Call <see cref="FormatWith{T}"/> to enrich
|
|
/// component entries with human-readable values.
|
|
/// </summary>
|
|
public sealed class ObservableCapture : IDisposable
|
|
{
|
|
private readonly World _world;
|
|
private readonly IDisposable _subscription;
|
|
private readonly List<CapturedEvent> _events = new();
|
|
private readonly Dictionary<Type, Func<World, Entity, string>> _formatters = new();
|
|
|
|
public ObservableCapture(World world)
|
|
{
|
|
_world = world;
|
|
_subscription = world.ObserveEntityChanges().Subscribe(OnChange);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers a formatter for component type <typeparamref name="T"/>.
|
|
/// When a change for this type is captured, the formatted value is
|
|
/// included in the log output.
|
|
/// </summary>
|
|
public void FormatWith<T>(Func<T, string> formatter) where T : struct
|
|
{
|
|
_formatters[typeof(T)] = (w, e) =>
|
|
{
|
|
var value = w.ReadComponent<T>(e);
|
|
return formatter(value);
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the captured log as a single string with one line per change.
|
|
/// </summary>
|
|
public string GetLog() => string.Join(Environment.NewLine, GetLogLines());
|
|
|
|
/// <summary>
|
|
/// Returns the captured log as a list of lines, one per change.
|
|
/// </summary>
|
|
public List<string> GetLogLines()
|
|
{
|
|
var lines = new List<string>(_events.Count);
|
|
foreach (var evt in _events)
|
|
{
|
|
var change = evt.Change;
|
|
var kind = change.Kind.ToString();
|
|
if (change.ComponentType != null)
|
|
{
|
|
var typeName = change.ComponentType.Name;
|
|
if (evt.FormattedValue != null)
|
|
lines.Add($"{kind} {typeName} = {evt.FormattedValue} on {change.Entity}");
|
|
else
|
|
lines.Add($"{kind} {typeName} on {change.Entity}");
|
|
}
|
|
else
|
|
{
|
|
lines.Add($"{kind} on {change.Entity}");
|
|
}
|
|
}
|
|
return lines;
|
|
}
|
|
|
|
public void Dispose() => _subscription.Dispose();
|
|
|
|
private void OnChange(EntityChange change)
|
|
{
|
|
string? formattedValue = null;
|
|
if (change.ComponentType != null
|
|
&& change.Kind != ChangeKind.ComponentRemoved
|
|
&& change.Kind != ChangeKind.EntityRemoved
|
|
&& change.Kind != ChangeKind.RelationshipReordered
|
|
&& _formatters.TryGetValue(change.ComponentType, out var formatter))
|
|
{
|
|
formattedValue = formatter(_world, change.Entity);
|
|
}
|
|
_events.Add(new CapturedEvent(change, formattedValue));
|
|
}
|
|
|
|
private readonly struct CapturedEvent
|
|
{
|
|
public readonly EntityChange Change;
|
|
public readonly string? FormattedValue;
|
|
public CapturedEvent(EntityChange change, string? formattedValue)
|
|
{
|
|
Change = change;
|
|
FormattedValue = formattedValue;
|
|
}
|
|
}
|
|
} |