Conversation
Add `On` method returning a Signale. `On` as the same signature than `Add`. Meaning you can specify if you whant to handle relation. // This an exemple, avoid lambda you can't remove the handler world.On<Position>().Added += (EntityRef e, ref Position p) => ...; world.On<Position>().Removed += (EntityRef e, in Position p) => ...; Signale are contained in a Dictionary with for key the type of the compoent tarrget. Then a list do the job to raise every matching (base on the Expression). World lock are by default on signal to avoid raising other signale will handling one. Open question: - Should the Expression follow a patern ? Or give a new `Signale` each time? Currently the way of storing Expression kind a bad. For 2 differente Expression matching a same case, it will be raised depending base on the first call of `On`. fix: prevent stack overflow when bulk deferred operations signal
Open question, should the Expcetion expose a method to allow for continutation of the operation? Or finishing the operation should not be an option and it should be donne whatever.
Open question: Should orphan Signals be added back to the World? Or should we only count alive signal and remove clean of the GC?
NB: I call this Event because most ECS use the term of Event, where it's a simple Signal.
There was a problem hiding this comment.
4 issues found across 20 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/fennecs/World.API.cs">
<violation number="1" location="src/fennecs/World.API.cs:327">
P2: When a World has no active signal subscribers, `GC()` skips orphan-signal cleanup entirely. Remove the subscriber guard so GC also removes Signals with no handlers; otherwise repeated transient `On<T>()` registrations can retain the signal registry indefinitely.</violation>
</file>
<file name="src/fennecs/World.cs">
<violation number="1" location="src/fennecs/World.cs:131">
P1: When the despawned entity is only a relation target, this condition misses signals on entities that reference it. `Aspect.DespawnDependencies` then dispatches `TargetDespawned` without a World lock, so a handler such as `e.Add<Position>()` can move the relating entity before the pending relation migration and leave the relation pointing at the dead target. Keep the lock around every despawn that can emit dependency signals.</violation>
</file>
<file name="src/fennecs/Aspect.CRUD.cs">
<violation number="1" location="src/fennecs/Aspect.CRUD.cs:191">
P2: When a batch removes a wildcard expression, narrower Signals can be silently skipped. The fast guard matches only the Signal expression against the batch pattern, so `On<T>().Removed` does not fire for `Batch.Remove<T>(Match.Any)`; make the preflight match in both directions, or route wildcard removals through the signalling path.</violation>
</file>
<file name="src/fennecs/World.Deferred.cs">
<violation number="1" location="src/fennecs/World.Deferred.cs:52">
P2: When a Signal handler mutates another World that is already locked, this branch treats its queued operation as non-signal. Propagate signal origin across World boundaries so catch-up failures still surface as `SignalException`.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
|
This doesn't provide advanced component tracking; it's just enough to handle major changes. It’s useful when you want to use a physics engine or networking that doesn't integrate seamlessly with ECS. I would have liked to use a flag system that notifies mark entity and lets the user query them, However, that introduces too many complications. Such as what happens if the user doesn't query, and the need to store the components and entity before destruction. Meaning an other world just holding data before final change. One issue with Signal is the fact of not knowing what the other handler can do. Like doing a loop of removing adding back component. Or adding a component that an other handler already added. All of this to say, i look for the simplest solution to feed the lake of notification. There is no problem to fully refuse this draft. I will keep the work for me. |
|
And i added an entry to EntityRef, can be remove it's only add this page because i find the need to mention it in the doc of Signal. |
GC_Drops_Signals_Once_Everyone_Has_Unsubscribed test will need a rewrite base on the anwser of the open question (GC one). Currently it's the only whay to check. Else it will imply relfection.
I run the benchmarks longueur, this to check if the last change on the benchmark change somthing. I use those metric for the doc.
Add
Onmethod returning a Signale.Onas the same signature thanAdd. Meaning you can specify if you whant to handle relation.Signale are contained in a Dictionary with for key the type of the compoent tarrget. Then a list do the job to raise every matching (base on the Expression).
World lock are by default on signal to avoid raising other signale will handling one. This also allow to pass a EntityRef.
Open question:
Signaleeach time? Currently the way of storing Expression kind a bad. For exemple: 2 differente Expression matching a same case, it will be raised depending base on the first call ofOn.For new we only count alive signal. Orphan Signal get remove of the list of Signal to free up space (like GC imply).
Actually Exception are raise and there is now way to fix up/continue the current Action except retrying.
What is done to do:
Summary by cubic
Introduces Component Signals: structural add/remove/despawn now raise events. Previously
Add/Remove/Despawnwere silent; nowAddedfires after storage,Removedfires just before removal (including batch, spawn/despawn, relations/links), and handler failures surface asSignalException.World.On<T>(Match)returns a memoized-per-expressionSignal<T>exposingAddedandRemoved. Handlers receive anEntityRef.RemoveCausereportsRemoved,Despawned, orTargetDespawned.SignalExceptionwraps handler faults; deferred handler work that fails is flagged withDeferred = true.Added. Despawn emitsRemovedfor all components and for relations whose targets die.World.GCdrops signals with no subscribers; storingSignal<T>leads to stale orphans. Disposal clears signals and subscription counts.Signal<T>. Always subscribe viaWorld.On<T>()and unsubscribe with the same call site. If you need to separate observer faults from caller errors, catchSignalException.Written for commit e1d487b. Summary will update on new commits.