├── .github
└── workflows
│ └── rust.yml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE-APACHE
├── LICENSE-MIT
├── README.md
├── macros
├── Cargo.lock
├── Cargo.toml
└── src
│ ├── data.rs
│ ├── generate
│ ├── cfg.rs
│ ├── mod.rs
│ ├── query.rs
│ ├── util.rs
│ └── world.rs
│ ├── lib.rs
│ └── parse
│ ├── attribute.rs
│ ├── cfg.rs
│ ├── mod.rs
│ ├── query.rs
│ ├── util.rs
│ └── world.rs
├── src
├── archetype
│ ├── components.rs
│ ├── iter.rs
│ ├── mod.rs
│ ├── slices.rs
│ ├── slot.rs
│ ├── storage.rs
│ └── view.rs
├── entity.rs
├── error.rs
├── index.rs
├── iter.rs
├── lib.rs
├── traits.rs
├── util.rs
└── version.rs
└── tests
├── test_bind.rs
├── test_direct.rs
├── test_entity_wild.rs
├── test_events.rs
├── test_generic.rs
├── test_impls.rs
├── test_iter.rs
├── test_iter_destroy.rs
├── test_multi.rs
├── test_one_of.rs
├── test_query_cfg.rs
├── test_raw.rs
├── test_select.rs
├── test_single.rs
├── test_single_dyn.rs
└── test_util.rs
/.github/workflows/rust.yml:
--------------------------------------------------------------------------------
1 | name: Rust
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 |
17 | steps:
18 | - uses: actions/checkout@v3
19 | - name: Build
20 | run: cargo build --verbose
21 | - name: Run tests
22 | run: cargo test --verbose
23 | - name: Run tests with events
24 | run: cargo test --verbose --features="events"
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # No build artifacts
2 | debug/
3 | target/
4 |
5 | # These are backup files generated by rustfmt
6 | **/*.rs.bk
7 |
8 | # MSVC Windows builds of rustc generate these, which store debugging information
9 | **/*.pdb
10 |
11 | # No editor or IDE files
12 | .vs/
13 | .idea/
14 | .vscode/
15 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "autocfg"
7 | version = "1.3.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
10 |
11 | [[package]]
12 | name = "base64"
13 | version = "0.22.1"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
16 |
17 | [[package]]
18 | name = "convert_case"
19 | version = "0.6.0"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
22 | dependencies = [
23 | "unicode-segmentation",
24 | ]
25 |
26 | [[package]]
27 | name = "gecs"
28 | version = "0.4.0"
29 | dependencies = [
30 | "gecs_macros",
31 | "seq-macro",
32 | ]
33 |
34 | [[package]]
35 | name = "gecs_macros"
36 | version = "0.4.0"
37 | dependencies = [
38 | "base64",
39 | "convert_case",
40 | "proc-macro2",
41 | "quote",
42 | "speedy",
43 | "syn",
44 | "xxhash-rust",
45 | ]
46 |
47 | [[package]]
48 | name = "memoffset"
49 | version = "0.9.1"
50 | source = "registry+https://github.com/rust-lang/crates.io-index"
51 | checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
52 | dependencies = [
53 | "autocfg",
54 | ]
55 |
56 | [[package]]
57 | name = "proc-macro2"
58 | version = "1.0.86"
59 | source = "registry+https://github.com/rust-lang/crates.io-index"
60 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
61 | dependencies = [
62 | "unicode-ident",
63 | ]
64 |
65 | [[package]]
66 | name = "quote"
67 | version = "1.0.36"
68 | source = "registry+https://github.com/rust-lang/crates.io-index"
69 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
70 | dependencies = [
71 | "proc-macro2",
72 | ]
73 |
74 | [[package]]
75 | name = "seq-macro"
76 | version = "0.3.5"
77 | source = "registry+https://github.com/rust-lang/crates.io-index"
78 | checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
79 |
80 | [[package]]
81 | name = "speedy"
82 | version = "0.8.7"
83 | source = "registry+https://github.com/rust-lang/crates.io-index"
84 | checksum = "da1992073f0e55aab599f4483c460598219b4f9ff0affa124b33580ab511e25a"
85 | dependencies = [
86 | "memoffset",
87 | "speedy-derive",
88 | ]
89 |
90 | [[package]]
91 | name = "speedy-derive"
92 | version = "0.8.7"
93 | source = "registry+https://github.com/rust-lang/crates.io-index"
94 | checksum = "658f2ca5276b92c3dfd65fa88316b4e032ace68f88d7570b43967784c0bac5ac"
95 | dependencies = [
96 | "proc-macro2",
97 | "quote",
98 | "syn",
99 | ]
100 |
101 | [[package]]
102 | name = "syn"
103 | version = "2.0.71"
104 | source = "registry+https://github.com/rust-lang/crates.io-index"
105 | checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
106 | dependencies = [
107 | "proc-macro2",
108 | "quote",
109 | "unicode-ident",
110 | ]
111 |
112 | [[package]]
113 | name = "unicode-ident"
114 | version = "1.0.12"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
117 |
118 | [[package]]
119 | name = "unicode-segmentation"
120 | version = "1.11.0"
121 | source = "registry+https://github.com/rust-lang/crates.io-index"
122 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
123 |
124 | [[package]]
125 | name = "xxhash-rust"
126 | version = "0.8.11"
127 | source = "registry+https://github.com/rust-lang/crates.io-index"
128 | checksum = "63658493314859b4dfdf3fb8c1defd61587839def09582db50b8a4e93afca6bb"
129 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "gecs"
3 | version = "0.4.0"
4 | authors = ["recatek"]
5 | description = "A generated entity component system."
6 | edition = "2021"
7 | repository = "https://github.com/recatek/gecs"
8 | license = "MIT OR Apache-2.0"
9 | readme = "README.md"
10 | keywords = ["ecs", "entity"]
11 | categories = ["data-structures", "game-engines"]
12 |
13 | [package.metadata.docs.rs]
14 | all-features = true
15 |
16 | [features]
17 | default = []
18 |
19 | # Allow archetypes to have up to 32 components (default is 16). Worsens compile times.
20 | 32_components = []
21 |
22 | # Wrap rather than panic when a version number overflows (4,294,967,295 max). Yields some small perf gains when
23 | # creating/destroying entities, but has a (probabilistically insignificant) risk of allowing reuse of stale handles.
24 | wrapping_version = []
25 |
26 | # Adds event tracking for entity creation/destruction. Has the following perf consequences:
27 | # - Adds additional allocation overhead to the ECS world for each per-archetype event queue.
28 | # - Every entity creation or destruction pushes an event to an archetype's event queue.
29 | # - Event queues will accumulate indefinitely and must be regularly drained using clear_events.
30 | events = ['gecs_macros/events']
31 |
32 | [dependencies]
33 | gecs_macros = { version = "0.4.0", path = "macros", default-features = false }
34 |
35 | seq-macro = { version = "0.3" } # For building "variadic" storage
36 |
--------------------------------------------------------------------------------
/LICENSE-APACHE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
--------------------------------------------------------------------------------
/LICENSE-MIT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | gecs 🦎
2 | -------
3 | A generated entity component system.
4 |
5 | [](https://docs.rs/gecs/)
6 | [](https://crates.io/crates/gecs)
7 |
8 | The gecs crate provides a compile-time generated, zero-overhead ECS for simulations
9 | on a budget. Unlike other ECS libraries, gecs takes a full ECS world structure
10 | definition from code and precompiles all queries to achieve better performance with
11 | no upfront cost or caching overhead. Queries in gecs can be inspected and checked at
12 | compile-time in order to catch what would otherwise be bugs presenting only in tests
13 | or execution. However, this comes at the cost of requiring all archetypes to be known
14 | and declared at compile-time, so that adding or removing components from entities at
15 | runtime isn't currently possible -- hybrid approaches could solve this in the future.
16 |
17 | The goals for gecs are (in descending priority order):
18 | - Fast iteration and find queries
19 | - Fast entity creation and destruction
20 | - Low, predictable memory overhead
21 | - A user-friendly library interface
22 | - Simplicity and focus in features
23 |
24 | All of the code that gecs generates in user crates is safe, and users of gecs can
25 | use `#![forbid(unsafe_code)]` in their own crates. Note that gecs does use unsafe
26 | code internally to allow for compiler optimizations around known invariants. It is
27 | not a goal of this library to be written entirely in safe Rust.
28 |
29 | # Getting Started
30 |
31 | See the [ecs_world!] macro for information on how to construct an ECS
32 | world, and the [ecs_find!] and [ecs_iter!] macros for
33 | information on how to perform full ECS queries. See the [World] and
34 | [Archetype] traits to see how to interact directly with the world.
35 |
36 | [ecs_world!]: https://docs.rs/gecs/latest/gecs/macro.ecs_world.html
37 | [ecs_find!]: https://docs.rs/gecs/latest/gecs/macro.ecs_find.html
38 | [ecs_iter!]: https://docs.rs/gecs/latest/gecs/macro.ecs_iter.html
39 | [World]: https://docs.rs/gecs/latest/gecs/traits/trait.World.html
40 | [Archetype]: https://docs.rs/gecs/latest/gecs/traits/trait.Archetype.html
41 |
42 |
43 | The following example creates a world with three components and two archetypes:
44 |
45 | ```rust
46 | use gecs::prelude::*;
47 |
48 | // Components -- these must be pub because the world is exported as pub as well.
49 | pub struct CompA(pub u32);
50 | pub struct CompB(pub u32);
51 | pub struct CompC(pub u32);
52 |
53 | ecs_world! {
54 | // Declare two archetypes, ArchFoo and ArchBar.
55 | ecs_archetype!(ArchFoo, CompA, CompB);
56 | ecs_archetype!(ArchBar, CompA, CompC);
57 | }
58 |
59 | fn main() {
60 | let mut world = EcsWorld::default(); // Initialize an empty new ECS world.
61 |
62 | // Add entities to the world by populating their components and receive their handles.
63 | let entity_a = world.create::((CompA(1), CompB(20)));
64 | let entity_b = world.create::((CompA(3), CompC(40)));
65 |
66 | // Each archetype now has one entity.
67 | assert_eq!(world.archetype::().len(), 1);
68 | assert_eq!(world.archetype::().len(), 1);
69 |
70 | // Look up each entity and check its CompB or CompC value.
71 | // We use the is_some() check here to make sure the entity was indeed found.
72 | assert!(ecs_find!(world, entity_a, |c: &CompB| assert_eq!(c.0, 20)).is_some());
73 | assert!(ecs_find!(world, entity_b, |c: &CompC| assert_eq!(c.0, 40)).is_some());
74 |
75 | // Add to entity_a's CompA value.
76 | ecs_find!(world, entity_a, |c: &mut CompA| { c.0 += 1; });
77 |
78 | // Sum both entities' CompA values with one iter despite being different archetypes.
79 | let mut sum = 0;
80 | ecs_iter!(world, |c: &CompA| { sum += c.0 });
81 | assert_eq!(sum, 5); // Adding 2 + 3 -- recall that we added 1 to entity_a's CompA.
82 |
83 | // Collect both entities that have a CompA component.
84 | let mut found = Vec::new();
85 | ecs_iter!(world, |entity: &EntityAny, _: &CompA| { found.push(*entity); });
86 | assert!(found == vec![entity_a.into(), entity_b.into()]);
87 |
88 | // Destroy both entities -- this will return an Option containing their components.
89 | assert!(world.destroy(entity_a).is_some());
90 | assert!(world.destroy(entity_b).is_some());
91 |
92 | // Try to look up a stale entity handle -- this will return None.
93 | assert!(ecs_find!(world, entity_a, |_: &Entity| { panic!() }).is_none());
94 | }
95 | ```
96 | License
97 | -------
98 |
99 | This library may be used under your choice of the Apache 2.0 or MIT license.
100 |
--------------------------------------------------------------------------------
/macros/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "convert_case"
7 | version = "0.6.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
10 | dependencies = [
11 | "unicode-segmentation",
12 | ]
13 |
14 | [[package]]
15 | name = "gecs_macros"
16 | version = "0.0.1"
17 | dependencies = [
18 | "convert_case",
19 | "lazy_static",
20 | "proc-macro2",
21 | "quote",
22 | "syn",
23 | ]
24 |
25 | [[package]]
26 | name = "lazy_static"
27 | version = "1.4.0"
28 | source = "registry+https://github.com/rust-lang/crates.io-index"
29 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
30 |
31 | [[package]]
32 | name = "proc-macro2"
33 | version = "1.0.58"
34 | source = "registry+https://github.com/rust-lang/crates.io-index"
35 | checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
36 | dependencies = [
37 | "unicode-ident",
38 | ]
39 |
40 | [[package]]
41 | name = "quote"
42 | version = "1.0.27"
43 | source = "registry+https://github.com/rust-lang/crates.io-index"
44 | checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
45 | dependencies = [
46 | "proc-macro2",
47 | ]
48 |
49 | [[package]]
50 | name = "syn"
51 | version = "2.0.16"
52 | source = "registry+https://github.com/rust-lang/crates.io-index"
53 | checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
54 | dependencies = [
55 | "proc-macro2",
56 | "quote",
57 | "unicode-ident",
58 | ]
59 |
60 | [[package]]
61 | name = "unicode-ident"
62 | version = "1.0.8"
63 | source = "registry+https://github.com/rust-lang/crates.io-index"
64 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
65 |
66 | [[package]]
67 | name = "unicode-segmentation"
68 | version = "1.10.1"
69 | source = "registry+https://github.com/rust-lang/crates.io-index"
70 | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
71 |
--------------------------------------------------------------------------------
/macros/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "gecs_macros"
3 | version = "0.4.0"
4 | authors = ["recatek"]
5 | description = "Procedural macros for the gecs crate."
6 | edition = "2021"
7 | repository = "https://github.com/recatek/gecs"
8 | license = "MIT OR Apache-2.0"
9 |
10 | [lib]
11 | proc-macro = true
12 |
13 | [features]
14 | default = []
15 | events = []
16 |
17 | [dependencies]
18 | convert_case = { version = "0.6" }
19 | proc-macro2 = { version = "1.0" }
20 | quote = { version = "1.0" }
21 | syn = { version = "2.0", features = ["full", "extra-traits"] }
22 |
23 | # For unique generation of query macros
24 | xxhash-rust = { version = "0.8", features = ["xxh3"] }
25 |
26 | # Serialization for passing world data to queries
27 | base64 = { version = "0.22" }
28 | speedy = { version = "0.8" }
29 |
--------------------------------------------------------------------------------
/macros/src/data.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use base64::Engine as _;
4 | use speedy::{Readable, Writable};
5 | use syn::Ident;
6 |
7 | use crate::parse::{HasAttributeId, ParseAttributeCfg, ParseCfgDecorated, ParseEcsWorld};
8 |
9 | #[derive(Debug, Readable, Writable)]
10 | pub struct DataWorld {
11 | pub name: String,
12 | pub archetypes: Vec,
13 | }
14 |
15 | #[derive(Debug, Readable, Writable)]
16 | pub struct DataArchetype {
17 | pub id: u8,
18 | pub name: String,
19 | pub components: Vec,
20 | }
21 |
22 | #[derive(Debug, Readable, Writable)]
23 | pub struct DataComponent {
24 | pub id: u8,
25 | pub name: String,
26 | }
27 |
28 | impl DataWorld {
29 | pub fn new(mut parse: ParseCfgDecorated) -> syn::Result {
30 | let cfg_lookup = parse.cfg_lookup;
31 |
32 | let mut archetypes = Vec::new();
33 | let mut archetype_ids = HashMap::new();
34 | let mut last_archetype_id = None;
35 |
36 | for mut archetype in parse.inner.archetypes.drain(..) {
37 | if evaluate_cfgs(&cfg_lookup, &archetype.cfgs) == false {
38 | continue;
39 | }
40 |
41 | // Advance the archetype ID, either implicitly or from the ID attribute
42 | last_archetype_id = advance_attribute_id(
43 | &archetype, //.
44 | &mut archetype_ids,
45 | last_archetype_id,
46 | )?;
47 |
48 | let mut component_ids = HashMap::new();
49 | let mut last_component_id = None;
50 |
51 | let mut components = Vec::new();
52 | for component in archetype.components.drain(..) {
53 | if evaluate_cfgs(&cfg_lookup, &component.cfgs) == false {
54 | continue;
55 | }
56 |
57 | // Advance the component ID, either implicitly or from the ID attribute
58 | last_component_id = advance_attribute_id(
59 | &component, //.
60 | &mut component_ids,
61 | last_component_id,
62 | )?;
63 |
64 | components.push(DataComponent {
65 | id: last_component_id.unwrap(), // TODO
66 | name: component.name.to_string(),
67 | });
68 | }
69 |
70 | archetypes.push(DataArchetype {
71 | id: last_archetype_id.unwrap(),
72 | name: archetype.name.to_string(),
73 | components,
74 | })
75 | }
76 |
77 | Ok(DataWorld {
78 | name: parse.inner.name.to_string(),
79 | archetypes,
80 | })
81 | }
82 |
83 | pub fn to_base64(&self) -> String {
84 | base64::engine::general_purpose::STANDARD_NO_PAD
85 | .encode(self.write_to_vec().expect("failed to serialize world"))
86 | }
87 |
88 | pub fn from_base64(base64: &str) -> Self {
89 | Self::read_from_buffer(
90 | &base64::engine::general_purpose::STANDARD_NO_PAD
91 | .decode(base64)
92 | .expect("failed to deserialize world"),
93 | )
94 | .expect("failed to deserialize world")
95 | }
96 | }
97 |
98 | impl DataArchetype {
99 | pub fn contains_component(&self, name: &Ident) -> bool {
100 | for component in self.components.iter() {
101 | if component.name == name.to_string() {
102 | return true;
103 | }
104 | }
105 | false
106 | }
107 | }
108 |
109 | fn evaluate_cfgs(cfg_lookup: &HashMap, cfgs: &[ParseAttributeCfg]) -> bool {
110 | for cfg in cfgs {
111 | let predicate = cfg.predicate.to_string();
112 | if *cfg_lookup.get(&predicate).unwrap() == false {
113 | return false;
114 | }
115 | }
116 | true
117 | }
118 |
119 | fn advance_attribute_id(
120 | item: &impl HasAttributeId,
121 | ids: &mut HashMap,
122 | last: Option,
123 | ) -> syn::Result