You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
For the past few months I have been working on a little ECS library called evenio. This is in response to some limitations I have encountered using bevy_ecs in Valence. In essence, I believe that "run once per frame" systems as the primary abstraction is fundamentally inadequate. This one fact has been the source of many design issues in Valence. Here are some of the problems and how evenio could fix them:
In bevy_ecs, there aren't any reliable ways to enforce that entities have certain combinations of components because there's always a delay between when the violation occurs and when it can be acted on. As a result, we don't even attempt to detect these potential mistakes. evenio lets us write handlers that run the moment before a component is added or removed, making these violations feasible to detect and act on.
Whenever an entity is despawned we almost always need to do some cleanup work. Bevy lets us detect that an entity is removed from the world, but doesn't give access to the entity's components because the entity is already gone. We work around this by using a Despawned component to signal removal, then letting systems in PostUpdate run before it's removed. This works, but it's very error prone because there's nothing stopping someone from calling .despawn() instead of adding the marker. evenio fixes this problem by signaling entity despawns with an event, allowing code to run immediately before the entity is removed.
A lot of valence's APIs are based on a pattern where we let the user make some changes to the world in Update and then defer handling the changes until PostUpdate. Compared to the simple procedural approach, This is a lot more cumbersome. Case in point, I tried to improve layers and clean things up in Redesign layers and valence_server schedule. #533 but the resulting code was so complicated and error prone that I gave up. In evenio, we can just use events to signal state changes instead of using change detection or diffing.
While reading packets from clients, we split the stream of packets into separate event buffers based on packet type. The problem is, this loses the order between different packet types. To get around this, I made an entirely separate schedule which runs in a loop to ensure that packet handler code is executed in the correct order. This is ugly and slow. Process client packets before run schedulers #587 tried to simplify this a bit without being aware of these details. We could have easily fixed this by using system callbacks, but this wouldn't really integrate with bevy's schedules. In evenio, event handlers are run in the order events are produced (regardless of type) so the problem goes away.
Every system added in Bevy has a cost. Systems run every frame even if they have no work to do. This is especially bad with the multithreaded executor due to the overhead involved. Add default fields for all entities in extractor #571 tried to fix the entity extractor and entity tracked data, but the solution would have caused an explosion in the number of systems. With evenio, systems don't have a cost unless the event they're listening for is actually sent.
Bevy's change detection feature is not flexible enough. Sometimes it works fine, but in other cases we end up using .bypass_change_detection() which results in footguns for end users.
Cancelable event #399 wants to introduce cancellable events similar to bukkit. This might be a lot easier with evenio events.
So, here's my plan. Let's burn it all to the ground and start over with evenio. We can leave valence_protocol and everything below it mostly intact, but everything else needs to go. Although evenio needs quite a lot of work before I would call it production ready, it should be usable enough to get started. Bevy has recently adopted some features like one-shot systems to address the above problems, but I don't really think they get at the root of the issue.
During this rewrite I would like to do a few other things:
For the past few months I have been working on a little ECS library called
evenio
. This is in response to some limitations I have encountered usingbevy_ecs
in Valence. In essence, I believe that "run once per frame" systems as the primary abstraction is fundamentally inadequate. This one fact has been the source of many design issues in Valence. Here are some of the problems and howevenio
could fix them:In bevy_ecs, there aren't any reliable ways to enforce that entities have certain combinations of components because there's always a delay between when the violation occurs and when it can be acted on. As a result, we don't even attempt to detect these potential mistakes.
evenio
lets us write handlers that run the moment before a component is added or removed, making these violations feasible to detect and act on.Whenever an entity is despawned we almost always need to do some cleanup work. Bevy lets us detect that an entity is removed from the world, but doesn't give access to the entity's components because the entity is already gone. We work around this by using a
Despawned
component to signal removal, then letting systems inPostUpdate
run before it's removed. This works, but it's very error prone because there's nothing stopping someone from calling.despawn()
instead of adding the marker.evenio
fixes this problem by signaling entity despawns with an event, allowing code to run immediately before the entity is removed.A lot of valence's APIs are based on a pattern where we let the user make some changes to the world in
Update
and then defer handling the changes untilPostUpdate
. Compared to the simple procedural approach, This is a lot more cumbersome. Case in point, I tried to improve layers and clean things up in Redesign layers andvalence_server
schedule. #533 but the resulting code was so complicated and error prone that I gave up. Inevenio
, we can just use events to signal state changes instead of using change detection or diffing.While reading packets from clients, we split the stream of packets into separate event buffers based on packet type. The problem is, this loses the order between different packet types. To get around this, I made an entirely separate schedule which runs in a loop to ensure that packet handler code is executed in the correct order. This is ugly and slow. Process client packets before run schedulers #587 tried to simplify this a bit without being aware of these details. We could have easily fixed this by using system callbacks, but this wouldn't really integrate with bevy's schedules. In
evenio
, event handlers are run in the order events are produced (regardless of type) so the problem goes away.Every system added in Bevy has a cost. Systems run every frame even if they have no work to do. This is especially bad with the multithreaded executor due to the overhead involved. Add default fields for all entities in extractor #571 tried to fix the entity extractor and entity tracked data, but the solution would have caused an explosion in the number of systems. With
evenio
, systems don't have a cost unless the event they're listening for is actually sent.In Poor performance in
bench_players
example. #323 I noticed poor performance while the server isn't doing anything. Likely related to the previous point.Bevy's change detection feature is not flexible enough. Sometimes it works fine, but in other cases we end up using
.bypass_change_detection()
which results in footguns for end users.evenio
doesn't have system order ambiguities, so Resolve system order ambiguities #542 goes away.Cancelable event #399 wants to introduce cancellable events similar to bukkit. This might be a lot easier with
evenio
events.So, here's my plan. Let's burn it all to the ground and start over with
evenio
. We can leavevalence_protocol
and everything below it mostly intact, but everything else needs to go. Althoughevenio
needs quite a lot of work before I would call it production ready, it should be usable enough to get started. Bevy has recently adopted some features like one-shot systems to address the above problems, but I don't really think they get at the root of the issue.During this rewrite I would like to do a few other things:
valence_network
#489).I have started work in #599
The text was updated successfully, but these errors were encountered: