My Archetype Invariants Exploration & Reference Implementation #23657
Replies: 1 comment 4 replies
-
|
My 2c:
As any dynamic APIs, we would have to consider what happens when an existing archetype suddently becomes invalid after adding one such new rule.
I would suggest you to try to make c) achievable, since historically that has been the main motivator for archetype invariants.
The return type is unfortunately not enough to ensure soundness (we are not in a language with dependent types). For example:
Also, the idea of returning
I don't think you can achieve this in polynomial complexity without having false positives. You are effectively solving a SAT problem where you're pretty much trying to identify if
I don't see a mention of support for query overlap detection in the roadmap. Regarding the UX of the feature, I like the idea of a simple and consistent model for the requirements ( |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Update(4/7/2026): Article structure refinement and clarification
Refer to Archetype Invariants #1481
All my PRs are drafts for experimentation and exposing problems
The previous title was "Proposal", which might cause confusion for the community. This is not a proposal for a specific API. This is a study of the problem space, with experimental implementations to validate the approach.
I am continuing to explore whether there are any simple, opt-in solutions to avoid over-engineering...
1. Insight
Archetype is immutable after creation, which is directly consistent with tables in SQL. I referred to the theoretical concepts of databases and found that "JSON Schema" and "Web Ontology Language" directly address constraint issues in "open-world" data models.
2. Abstract Architecture
Core primitives
Controlled Side Effects
Each controlled API auto-registers the corresponding constraints. The system can statically verify that the side effects don't violate any constraints.
bevy-cliis a better alternative that can significantly reduce the compilation/registeration pressure andbevy_ecscodebase expansionValidation & Final fallback
The validation point is in
get_id_or_insert- the sole entry point for Archetype creation. There is no bypassing path. The Archetype cannot be changed after creation.3. Constraint Primitives
Three primitives are sufficient to express any structural constraint:
require(T)not(expr)and(exprs...)OPTIONAL:
or(exprs...)only(A, B, C)forbids because new components can be registered at any time in registeration phase.forbid can be naturally represented by primitives
forbid(T)=not(require(T))Coverage of Issue #1481 Requirements
forbid(B)require(B)only(B, C)require(B), B:require(A)forbidall othersor(require(B), require(C))(requires Or)and(or(require(B), require(C)), not(and(require(B), require(C))))(requires Or)or(require(A), require(B), require(C))(requires Or)4. Implementation
Disjunctive Normal Form (DNF)
The current
QueryFilteris exactly the same as this implementation.Each constraint expression compiles to DNF - a disjunction (OR) of conjunctive clauses. Each clause is a pair of
FixedBitSets:Validation is short-circuit OR over clauses, each clause is two bitwise operations:
Logic is as same as
is_ruled_outinquery/state.rsWithout the Or primitive(CNF), each component's constraint compiles to a single clause. Validation becomes one
contains_all+ oneintersectsper component - O(N/64) bitwise operations. No SAT solving involved.Verification Point(No any other points that can be bypassed)
Validation occurs only on the Archetype creation path (
get_id_or_insert), with complexity O(components being inserted). Existing Archetypes are never re-checked because they are immutable. It is guaranteed by data structure.Benchmarks
Note: Further benchmarking with more diverse scenarios is needed.
5. Integration with Query Overlap Detection
The principle is straightforward:
#[constraint(forbid(B))]on component A compiles to: A's forbidden BitSet contains B.Query<With<A>>vsQuery<With<B>>.experiment (from experimental code):
constraint.rs: Functions for determining whether any two components are mutually exclusive.info.rs: Method for updating the mutual exclusion matrix.query/state.rs: Infrom_states_uninitialized, inject all mutually exclusive components ofwith_idwithinAccessFilter::withintoAccessFilter::without.6. Controlled Side Effects + Constraints - UX API
Static safety verification
Because side effects are restricted to three forms -
Add(X),Remove(X),Mutual Exclusion- the system can statically verify at registration time that no combination of side effects can violate any constraint:bevy-cliis a better alternative that can significantly reduce the compilation/registeration pressure andbevy_ecscodebase expansionBetter not to put side effects on component declaration
Something like
[constraint(forbid(A, replace))]. This is because they can be combined. For example:Meanwhile, side effects are context-dependent. The same mutual exclusion might need different strategies in different apps:
Constraints are universal truths about the data model. Side effects are policy decisions.
This is almost a consensus among all data models, and it is also the experience gained from lessons in the SQL language.
7. Relationship with Existing Features
Current PR #22818 (Mutually Exclusive Components)
It solves the immediate need for mutual exclusion. My study is complementary - it provides the theoretical foundation and architectural direction for a unified constraint system.
register_mutually_exclusive_components::<(A, B)>()is equivalent to:forbid(B), B:forbid(A)// Only constraintsworld.register_mutually_exclusive_components::<(CompA, CompB)>()// Only side effectsThe current PR's problem is mixed invariant maintaince and auto-replace policy, therefore soundness bug exists
Current
#[require(T)]The existing
#[require(T)]is a side effect. It does not enforce invariants(Is there any possible direct solution to the problem without proposed primitives?). Combining with#[constraint(require(T))]creates a complete guarantee:#[require]makes it convenient (auto-inserts Position).#[constraint]makes it safe (blocks any Archetype where Health exists without Position).8. Tradeoffs
Fallible API
To refuse illegal Archetype creation,
get_id_or_insertmust returnResult(Is there any simple solution?), which propagates through downstream code. This is the largest engineering cost.Proposed behavior for existing APIs:
spawn(): If validation fails, entity lives in the empty Archetype (no components). User cantry_insertordespawn.insert(): If validation fails silently, entity remains in existing Archetype.remove(): If validation fails silently, entity retains original components.despawn(): Always succeeds.This might not be the best solution, but it is the only way to avoid disrupting the existing API.
Runtime Registration of Constraints(Addressed by review)
My constraint validation layer does not support dynamic registration of new constraints on existing Components at runtime. Otherwise, this is a problem that cannot be easily solved. This requires more in-depth engineering diagnosis and decision-making, such as completely deleting all the illgal Archetypes. However, it cannot guarantee that UB will definitely not occur. Query Overlap Detection also requires that all modifications be able to be made at runtime.
In such context, runtime is indicating the system is in
Updatephase, after allRegisterationphase9. Edge Cases
Insert order
This is a real problem. Batch commands #5074 need to be solved. If combined with PR #22818 's auto replamcenet. Batch command resolving can be delayed. But it still cannot solve the fundamental issue.
Contradictory constraints
The component can never exist in any Archetype. This is detected at DNF compilation (required ∩ forbidden ≠ ∅). Cross-component contradictions are detected via implication graph analysis.
bevy-cliis a better alternative that can significantly reduce the compilation/registeration pressure andbevy_ecscodebase expansionThis does not break invariants - it makes the component unusable.
10. Roadmap
debug_asserton Archetype creationdebug_asserttoResult<T, E>with proper error propagationOn<ComponentConstraintViolation>register_cascade_remove11. References
If anyone has thoughts, questions, or concerns, welcome to the discussion. Thank you!
Beta Was this translation helpful? Give feedback.
All reactions