├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src ├── dynamic_query.rs ├── lib.rs ├── reflect_value_ref.rs └── reflect_value_ref └── query.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bevy_ecs_dynamic" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | 7 | [dependencies] 8 | bevy_ecs = { version = "0.9.0", default-features = false } 9 | bevy_app = "0.9.0" 10 | bevy_reflect = "0.9.0" 11 | fixedbitset = "0.4.2" 12 | -------------------------------------------------------------------------------- /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 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2022 [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bevy_ecs_dynamic 2 | 3 | Utilities for working with `bevy_ecs` in situations where the types you're dealing with might not be known at compile time (e.g. scripting, modding). 4 | 5 | # Example Usage 6 | 7 | ## Dynamic Query 8 | 9 | ```rust 10 | let component_id_1 = world.init_component::(); 11 | let component_id_2 = world.init_component::(); 12 | 13 | world.spawn().insert(TestComponent1).insert(TestComponent2); 14 | world.spawn().insert(TestComponent1); 15 | 16 | let mut query = DynamicQuery::new(&world, vec![FetchKind::Ref(component_id_1)], vec![FilterKind::Without(component_id_2)]); 17 | assert_eq!(query.iter(&world).count(), 1); 18 | ``` 19 | 20 | 21 | # Bevy support table 22 | 23 | | bevy | bevy\_ecs\_dynamic | 24 | | ---- | ------------------ | 25 | | 0.8 | _unreleased_ | 26 | -------------------------------------------------------------------------------- /src/dynamic_query.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | 3 | use bevy_ecs::archetype::{ 4 | Archetype, ArchetypeComponentId, ArchetypeEntity, ArchetypeGeneration, ArchetypeId, Archetypes, 5 | }; 6 | use bevy_ecs::component::{ComponentId, ComponentTicks, StorageType}; 7 | use bevy_ecs::prelude::*; 8 | use bevy_ecs::ptr::{Ptr, PtrMut, ThinSlicePtr, UnsafeCellDeref}; 9 | use bevy_ecs::query::{Access, FilteredAccess, QueryEntityError}; 10 | use bevy_ecs::storage::{ComponentSparseSet, Table, TableId, Tables}; 11 | use bevy_ecs::world::WorldId; 12 | use fixedbitset::FixedBitSet; 13 | 14 | #[derive(Clone, Copy, Debug)] 15 | pub enum FetchKind { 16 | Ref(ComponentId), 17 | RefMut(ComponentId), 18 | } 19 | 20 | impl FetchKind { 21 | fn is_readonly(self) -> bool { 22 | match self { 23 | FetchKind::Ref(_) => true, 24 | FetchKind::RefMut(_) => false, 25 | } 26 | } 27 | } 28 | 29 | #[derive(Clone, Copy, Debug)] 30 | pub enum FilterKind { 31 | With(ComponentId), 32 | Without(ComponentId), 33 | Changed(ComponentId), 34 | Added(ComponentId), 35 | } 36 | 37 | pub enum FetchResult<'w> { 38 | Ref(Ptr<'w>), 39 | RefMut { 40 | value: PtrMut<'w>, 41 | ticks: &'w mut ComponentTicks, 42 | last_change_tick: u32, 43 | change_tick: u32, 44 | }, 45 | } 46 | 47 | impl FetchKind { 48 | fn component_id(self) -> ComponentId { 49 | match self { 50 | FetchKind::Ref(id) | FetchKind::RefMut(id) => id, 51 | } 52 | } 53 | 54 | fn matches_archetype(&self, archetype: &Archetype) -> bool { 55 | match *self { 56 | FetchKind::Ref(component_id) | FetchKind::RefMut(component_id) => { 57 | archetype.contains(component_id) 58 | } 59 | } 60 | } 61 | 62 | fn update_archetype_component_access( 63 | &self, 64 | archetype: &Archetype, 65 | access: &mut Access, 66 | ) { 67 | match *self { 68 | FetchKind::Ref(id) => { 69 | access.add_read(archetype.get_archetype_component_id(id).unwrap()) 70 | } 71 | FetchKind::RefMut(id) => { 72 | access.add_write(archetype.get_archetype_component_id(id).unwrap()) 73 | } 74 | } 75 | } 76 | 77 | fn update_component_access( 78 | &self, 79 | access: &mut FilteredAccess, 80 | ) -> Result<(), QueryError> { 81 | match *self { 82 | FetchKind::Ref(id) => { 83 | if access.access().has_write(id) { 84 | return Err(QueryError::ConflicingAccessFetch(*self)); 85 | } 86 | access.add_read(id); 87 | } 88 | FetchKind::RefMut(id) => { 89 | if access.access().has_read(id) { 90 | return Err(QueryError::ConflicingAccessFetch(*self)); 91 | } 92 | access.add_write(id) 93 | } 94 | } 95 | 96 | Ok(()) 97 | } 98 | } 99 | 100 | impl FilterKind { 101 | fn component_id(self) -> ComponentId { 102 | match self { 103 | FilterKind::With(id) 104 | | FilterKind::Without(id) 105 | | FilterKind::Changed(id) 106 | | FilterKind::Added(id) => id, 107 | } 108 | } 109 | 110 | fn matches_archetype(&self, archetype: &Archetype) -> bool { 111 | match *self { 112 | FilterKind::With(id) => archetype.contains(id), 113 | FilterKind::Without(id) => !archetype.contains(id), 114 | FilterKind::Changed(id) => archetype.contains(id), 115 | FilterKind::Added(id) => archetype.contains(id), 116 | } 117 | } 118 | 119 | fn update_archetype_component_access( 120 | &self, 121 | archetype: &Archetype, 122 | access: &mut Access, 123 | ) { 124 | match *self { 125 | FilterKind::With(_) => {} 126 | FilterKind::Without(_) => {} 127 | FilterKind::Changed(id) | FilterKind::Added(id) => { 128 | access.add_read(archetype.get_archetype_component_id(id).unwrap()) 129 | } 130 | } 131 | } 132 | 133 | fn update_component_access( 134 | &self, 135 | access: &mut FilteredAccess, 136 | ) -> Result<(), QueryError> { 137 | match *self { 138 | FilterKind::With(id) => access.add_with(id), 139 | FilterKind::Without(id) => access.add_without(id), 140 | FilterKind::Changed(id) => { 141 | if access.access().has_write(id) { 142 | return Err(QueryError::ConflicingAccessFilter(*self)); 143 | } 144 | access.add_read(id); 145 | } 146 | FilterKind::Added(id) => { 147 | if access.access().has_write(id) { 148 | return Err(QueryError::ConflicingAccessFilter(*self)); 149 | } 150 | access.add_read(id); 151 | } 152 | } 153 | 154 | Ok(()) 155 | } 156 | } 157 | 158 | #[derive(Debug)] 159 | pub enum QueryError { 160 | ConflicingAccessFilter(FilterKind), 161 | ConflicingAccessFetch(FetchKind), 162 | } 163 | 164 | impl std::fmt::Display for QueryError { 165 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 166 | match self { 167 | QueryError::ConflicingAccessFilter(filter) => { 168 | write!(f, "{filter:?} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.") 169 | } 170 | QueryError::ConflicingAccessFetch(fetch @ FetchKind::Ref(_)) => { 171 | write!(f ,"{fetch:?} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.") 172 | } 173 | QueryError::ConflicingAccessFetch(fetch @ FetchKind::RefMut(_)) => { 174 | write!(f, "{fetch:?} conflicts with a previous access in this query. Mutable component access must be unique.") 175 | } 176 | } 177 | } 178 | } 179 | 180 | impl std::error::Error for QueryError {} 181 | struct ComponentFetchState<'w> { 182 | table_components: Option>, 183 | entity_table_rows: Option>, 184 | sparse_set: Option<&'w ComponentSparseSet>, 185 | 186 | table_ticks: Option>>, 187 | last_change_tick: u32, 188 | change_tick: u32, 189 | 190 | fetch_kind: FetchKind, 191 | storage_type: StorageType, 192 | component_size: usize, 193 | } 194 | 195 | impl<'w> ComponentFetchState<'w> { 196 | unsafe fn init( 197 | world: &'w World, 198 | last_change_tick: u32, 199 | change_tick: u32, 200 | fetch_kind: FetchKind, 201 | ) -> ComponentFetchState<'w> { 202 | let component_id = fetch_kind.component_id(); 203 | let component_info = world.components().get_info(component_id).unwrap(); 204 | 205 | ComponentFetchState { 206 | table_components: None, 207 | entity_table_rows: None, 208 | sparse_set: None, 209 | table_ticks: None, 210 | last_change_tick, 211 | change_tick, 212 | fetch_kind, 213 | storage_type: component_info.storage_type(), 214 | component_size: component_info.layout().size(), 215 | } 216 | } 217 | 218 | #[inline] 219 | unsafe fn set_archetype(&mut self, archetype: &'w Archetype, tables: &'w Tables) { 220 | match self.storage_type { 221 | StorageType::Table => { 222 | let column = tables[archetype.table_id()] 223 | .get_column(self.fetch_kind.component_id()) 224 | .unwrap(); 225 | self.table_components = Some(column.get_data_ptr()); 226 | self.table_ticks = Some(column.get_ticks_slice().into()); 227 | } 228 | StorageType::SparseSet => {} 229 | } 230 | } 231 | 232 | #[inline] 233 | unsafe fn set_table(&mut self, table: &'w Table) { 234 | let column = table.get_column(self.fetch_kind.component_id()).unwrap(); 235 | self.table_components = Some(column.get_data_ptr()); 236 | self.table_ticks = Some(column.get_ticks_slice().into()); 237 | } 238 | 239 | #[inline] 240 | unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> FetchResult<'w> { 241 | match self.storage_type { 242 | StorageType::Table => { 243 | let (table_components, table_ticks) = 244 | self.table_components.zip(self.table_ticks).unwrap(); 245 | let value = table_components.byte_add(table_row * self.component_size); 246 | let component_ticks = table_ticks.get(table_row); 247 | match self.fetch_kind { 248 | FetchKind::Ref(_) => FetchResult::Ref(value), 249 | FetchKind::RefMut(_) => FetchResult::RefMut { 250 | value: value.assert_unique(), 251 | ticks: component_ticks.deref_mut(), 252 | last_change_tick: self.last_change_tick, 253 | change_tick: self.change_tick, 254 | }, 255 | } 256 | } 257 | StorageType::SparseSet => match self.storage_type { 258 | StorageType::Table => { 259 | let (entity_table_rows, (table_components, table_ticks)) = self 260 | .entity_table_rows 261 | .zip(self.table_components.zip(self.table_ticks)) 262 | .unwrap(); 263 | let table_row = *entity_table_rows.get(table_row); 264 | let value = table_components.byte_add(table_row * self.component_size); 265 | 266 | match self.fetch_kind { 267 | FetchKind::Ref(_) => FetchResult::Ref(value), 268 | FetchKind::RefMut(_) => { 269 | let component_ticks = table_ticks.get(table_row).deref_mut(); 270 | FetchResult::RefMut { 271 | value: value.assert_unique(), 272 | ticks: component_ticks, 273 | last_change_tick: self.last_change_tick, 274 | change_tick: self.change_tick, 275 | } 276 | } 277 | } 278 | } 279 | StorageType::SparseSet => { 280 | let sparse_set = self.sparse_set.unwrap(); 281 | let (value, component_ticks) = sparse_set.get_with_ticks(entity).unwrap(); 282 | match self.fetch_kind { 283 | FetchKind::Ref(_) => FetchResult::Ref(value), 284 | FetchKind::RefMut(_) => FetchResult::RefMut { 285 | value: value.assert_unique(), 286 | ticks: component_ticks.deref_mut(), 287 | last_change_tick: self.last_change_tick, 288 | change_tick: self.change_tick, 289 | }, 290 | } 291 | } 292 | }, 293 | } 294 | } 295 | } 296 | 297 | struct ComponentFetchStates<'w> { 298 | entities: Option>, 299 | components: Vec>, 300 | } 301 | 302 | impl<'w> ComponentFetchStates<'w> { 303 | fn is_dense(&self) -> bool { 304 | self.components 305 | .iter() 306 | .all(|component| component.storage_type == StorageType::Table) 307 | } 308 | } 309 | 310 | impl<'w> ComponentFetchStates<'w> { 311 | unsafe fn init( 312 | world: &'w World, 313 | last_change_tick: u32, 314 | change_tick: u32, 315 | component_fetches: &[FetchKind], 316 | ) -> ComponentFetchStates<'w> { 317 | ComponentFetchStates { 318 | entities: None, 319 | components: component_fetches 320 | .iter() 321 | .map(|kind| ComponentFetchState::init(world, last_change_tick, change_tick, *kind)) 322 | .collect(), 323 | } 324 | } 325 | 326 | #[inline] 327 | unsafe fn set_archetype(&mut self, archetype: &'w Archetype, tables: &'w Tables) { 328 | self.components 329 | .iter_mut() 330 | .for_each(|component| component.set_archetype(archetype, tables)); 331 | } 332 | 333 | #[inline] 334 | unsafe fn set_table(&mut self, table: &'w Table) { 335 | self.entities = Some(table.entities().into()); 336 | self.components 337 | .iter_mut() 338 | .for_each(|component| component.set_table(table)); 339 | } 340 | 341 | #[inline] 342 | unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> Vec> { 343 | self.components 344 | .iter_mut() 345 | .map(|component| component.fetch(entity, table_row)) 346 | .collect() 347 | } 348 | 349 | #[inline] 350 | unsafe fn entity(&mut self, index: usize) -> Entity { 351 | *self.entities.unwrap().get(index) 352 | } 353 | } 354 | 355 | struct ComponentFilterChangeDetection<'w> { 356 | table_ticks: Option>>, 357 | entities: Option>, 358 | sparse_set: Option<&'w ComponentSparseSet>, 359 | last_change_tick: u32, 360 | change_tick: u32, 361 | } 362 | 363 | impl<'w> ComponentFilterChangeDetection<'w> { 364 | unsafe fn archetype_ticks( 365 | &self, 366 | storage_type: StorageType, 367 | entity: Entity, 368 | table_row: usize, 369 | ) -> ComponentTicks { 370 | match storage_type { 371 | StorageType::Table => { 372 | let ticks = *self.table_ticks.unwrap().get(table_row).deref(); 373 | ticks 374 | } 375 | StorageType::SparseSet => { 376 | let ticks = self 377 | .sparse_set 378 | .unwrap() 379 | .get_ticks(entity) 380 | .map(|ticks| &*ticks.get()) 381 | .cloned() 382 | .unwrap(); 383 | ticks 384 | } 385 | } 386 | } 387 | } 388 | 389 | struct ComponentFilterState<'w> { 390 | component_id: ComponentId, 391 | storage_type: StorageType, 392 | kind: FilterKind, 393 | change_detection: ComponentFilterChangeDetection<'w>, 394 | } 395 | impl<'w> ComponentFilterState<'w> { 396 | fn init( 397 | world: &'w World, 398 | last_change_tick: u32, 399 | change_tick: u32, 400 | kind: FilterKind, 401 | ) -> ComponentFilterState<'w> { 402 | let component_id = kind.component_id(); 403 | let component_info = world.components().get_info(component_id).unwrap(); 404 | ComponentFilterState { 405 | component_id, 406 | storage_type: component_info.storage_type(), 407 | kind, 408 | change_detection: ComponentFilterChangeDetection { 409 | table_ticks: None, 410 | entities: None, 411 | sparse_set: (component_info.storage_type() == StorageType::SparseSet) 412 | .then(|| world.storages().sparse_sets.get(component_id).unwrap()), 413 | last_change_tick, 414 | change_tick, 415 | }, 416 | } 417 | } 418 | 419 | #[inline] 420 | unsafe fn set_archetype(&mut self, archetype: &'w Archetype, tables: &'w Tables) { 421 | match self.kind { 422 | FilterKind::With(_) | FilterKind::Without(_) => {} 423 | FilterKind::Changed(_) | FilterKind::Added(_) => match self.storage_type { 424 | StorageType::Table => { 425 | let table = &tables[archetype.table_id()]; 426 | self.change_detection.table_ticks = Some( 427 | table 428 | .get_column(self.component_id) 429 | .unwrap() 430 | .get_ticks_slice() 431 | .into(), 432 | ); 433 | } 434 | StorageType::SparseSet => { 435 | self.change_detection.entities = Some(archetype.entities().into()) 436 | } 437 | }, 438 | } 439 | } 440 | 441 | #[inline] 442 | unsafe fn set_table(&mut self, table: &'w Table) { 443 | match self.kind { 444 | FilterKind::With(_) | FilterKind::Without(_) => {} 445 | FilterKind::Changed(_) | FilterKind::Added(_) => { 446 | self.change_detection.table_ticks = Some( 447 | table 448 | .get_column(self.component_id) 449 | .unwrap() 450 | .get_ticks_slice() 451 | .into(), 452 | ); 453 | } 454 | } 455 | } 456 | 457 | #[inline] 458 | unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> bool { 459 | match self.storage_type { 460 | StorageType::Table => match self.kind { 461 | FilterKind::With(_) | FilterKind::Without(_) => true, 462 | FilterKind::Changed(_) => ComponentTicks::is_changed( 463 | (self.change_detection.table_ticks.unwrap().get(table_row)).deref(), 464 | self.change_detection.last_change_tick, 465 | self.change_detection.change_tick, 466 | ), 467 | FilterKind::Added(_) => ComponentTicks::is_added( 468 | (self.change_detection.table_ticks.unwrap().get(table_row)).deref(), 469 | self.change_detection.last_change_tick, 470 | self.change_detection.change_tick, 471 | ), 472 | }, 473 | StorageType::SparseSet => match self.kind { 474 | FilterKind::With(_) | FilterKind::Without(_) => true, 475 | FilterKind::Changed(_) => { 476 | let ticks = 477 | self.change_detection 478 | .archetype_ticks(self.storage_type, entity, table_row); 479 | ticks.is_changed( 480 | self.change_detection.last_change_tick, 481 | self.change_detection.change_tick, 482 | ) 483 | } 484 | FilterKind::Added(_) => { 485 | let ticks = 486 | self.change_detection 487 | .archetype_ticks(self.storage_type, entity, table_row); 488 | ticks.is_added( 489 | self.change_detection.last_change_tick, 490 | self.change_detection.change_tick, 491 | ) 492 | } 493 | }, 494 | } 495 | } 496 | } 497 | 498 | struct ComponentFilterStates<'w> { 499 | filters: Vec>, 500 | } 501 | 502 | impl<'w> ComponentFilterStates<'w> { 503 | fn is_dense(&self) -> bool { 504 | self.filters 505 | .iter() 506 | .all(|filter| filter.storage_type == StorageType::Table) 507 | } 508 | } 509 | 510 | impl<'w> ComponentFilterStates<'w> { 511 | unsafe fn init( 512 | world: &'w World, 513 | last_change_tick: u32, 514 | change_tick: u32, 515 | filters: &[FilterKind], 516 | ) -> ComponentFilterStates<'w> { 517 | ComponentFilterStates { 518 | filters: filters 519 | .iter() 520 | .map(|kind| ComponentFilterState::init(world, last_change_tick, change_tick, *kind)) 521 | .collect(), 522 | } 523 | } 524 | 525 | #[inline] 526 | unsafe fn set_archetype(&mut self, archetype: &'w Archetype, tables: &'w Tables) { 527 | self.filters 528 | .iter_mut() 529 | .for_each(|filter| filter.set_archetype(archetype, tables)); 530 | } 531 | 532 | #[inline] 533 | unsafe fn set_table(&mut self, table: &'w Table) { 534 | self.filters 535 | .iter_mut() 536 | .for_each(|filter| filter.set_table(table)); 537 | } 538 | 539 | #[inline] 540 | unsafe fn fetch(&mut self, entity: Entity, table_row: usize) -> bool { 541 | self.filters 542 | .iter_mut() 543 | .all(|filter| filter.fetch(entity, table_row)) 544 | } 545 | } 546 | 547 | pub struct DynamicQuery { 548 | world_id: WorldId, 549 | component_fetches: Vec, 550 | filters: Vec, 551 | 552 | archetype_generation: ArchetypeGeneration, 553 | // NOTE: we maintain both a TableId bitset and a vec because iterating the vec is faster 554 | matched_tables: FixedBitSet, 555 | matched_table_ids: Vec, 556 | // NOTE: we maintain both a ArchetypeId bitset and a vec because iterating the vec is faster 557 | matched_archetypes: FixedBitSet, 558 | matched_archetype_ids: Vec, 559 | #[allow(unused)] 560 | component_access: FilteredAccess, 561 | archetype_component_access: Access, 562 | } 563 | 564 | impl DynamicQuery { 565 | pub fn new( 566 | world: &World, 567 | component_fetches: Vec, 568 | filters: Vec, 569 | ) -> Result { 570 | let mut component_access = FilteredAccess::default(); 571 | component_fetches 572 | .iter() 573 | .try_for_each(|fetch| fetch.update_component_access(&mut component_access))?; 574 | 575 | // Use a temporary empty FilteredAccess for filters. This prevents them from conflicting with the 576 | // main Query's `fetch_state` access. Filters are allowed to conflict with the main query fetch 577 | // because they are evaluated *before* a specific reference is constructed. 578 | let mut filter_component_access = FilteredAccess::default(); 579 | filters 580 | .iter() 581 | .try_for_each(|filter| filter.update_component_access(&mut filter_component_access))?; 582 | 583 | // Merge the temporary filter access with the main access. This ensures that filter access is 584 | // properly considered in a global "cross-query" context (both within systems and across systems). 585 | component_access.extend(&filter_component_access); 586 | 587 | let mut query = DynamicQuery { 588 | world_id: world.id(), 589 | component_fetches, 590 | filters, 591 | component_access, 592 | archetype_generation: ArchetypeGeneration::initial(), 593 | matched_tables: Default::default(), 594 | matched_table_ids: Vec::new(), 595 | matched_archetypes: Default::default(), 596 | matched_archetype_ids: Vec::new(), 597 | archetype_component_access: Default::default(), 598 | }; 599 | query.update_archetypes(world); 600 | 601 | Ok(query) 602 | } 603 | 604 | pub fn fetches(&self) -> &[FetchKind] { 605 | &self.component_fetches 606 | } 607 | pub fn filters(&self) -> &[FilterKind] { 608 | &self.filters 609 | } 610 | 611 | fn is_readonly(&self) -> bool { 612 | self.fetches().iter().all(|fetch| fetch.is_readonly()) 613 | } 614 | 615 | fn validate_world(&self, world: &World) { 616 | assert!( 617 | world.id() == self.world_id, 618 | "Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.", 619 | std::any::type_name::(), 620 | ); 621 | } 622 | 623 | pub fn update_archetypes(&mut self, world: &World) { 624 | self.validate_world(world); 625 | let archetypes = world.archetypes(); 626 | let new_generation = archetypes.generation(); 627 | let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); 628 | let archetype_index_range = old_generation.value()..new_generation.value(); 629 | 630 | for archetype_index in archetype_index_range { 631 | self.new_archetype(&archetypes[ArchetypeId::new(archetype_index)]); 632 | } 633 | } 634 | 635 | fn new_archetype(&mut self, archetype: &Archetype) { 636 | if self 637 | .component_fetches 638 | .iter() 639 | .all(|f| f.matches_archetype(archetype)) 640 | && self.filters.iter().all(|f| f.matches_archetype(archetype)) 641 | { 642 | self.component_fetches.iter().for_each(|s| { 643 | s.update_archetype_component_access(archetype, &mut self.archetype_component_access) 644 | }); 645 | self.filters.iter().for_each(|s| { 646 | s.update_archetype_component_access(archetype, &mut self.archetype_component_access) 647 | }); 648 | let archetype_index = archetype.id().index(); 649 | if !self.matched_archetypes.contains(archetype_index) { 650 | self.matched_archetypes.grow(archetype_index + 1); 651 | self.matched_archetypes.set(archetype_index, true); 652 | self.matched_archetype_ids.push(archetype.id()); 653 | } 654 | let table_index = archetype.table_id().index(); 655 | if !self.matched_tables.contains(table_index) { 656 | self.matched_tables.grow(table_index + 1); 657 | self.matched_tables.set(table_index, true); 658 | self.matched_table_ids.push(archetype.table_id()); 659 | } 660 | } 661 | } 662 | 663 | /// Returns an [`Iterator`] over the query results for the given [`World`]. 664 | /// 665 | /// This can only be called for read-only queries, and will otherwise `panic`. 666 | /// See [`Self::iter_mut`] for write-queries. 667 | pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> DynamicQueryIter<'w, 's> { 668 | assert!( 669 | self.component_fetches 670 | .iter() 671 | .all(|fetch| fetch.is_readonly()), 672 | "attempted to call `DynamicQuery::iter` for non-read-only query" 673 | ); 674 | 675 | self.update_archetypes(world); 676 | 677 | // SAFETY: all fetches are read only, world is checked by `update_archetypes` 678 | unsafe { 679 | self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) 680 | } 681 | } 682 | 683 | /// Returns an [`Iterator`] over the query results for the given [`World`]. 684 | pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> DynamicQueryIter<'w, 's> { 685 | self.update_archetypes(world); 686 | 687 | // SAFETY: query has unique world access, world is checked by `update_archetypes` 688 | unsafe { 689 | self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) 690 | } 691 | } 692 | 693 | /// # Safety 694 | /// 695 | /// This does not check for mutable query correctness. To be safe, make sure mutable queries 696 | /// have unique access to the components they query. 697 | /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` 698 | /// with a mismatched [`WorldId`] is unsound. 699 | unsafe fn iter_unchecked_manual<'w, 's>( 700 | &'s self, 701 | world: &'w World, 702 | last_change_tick: u32, 703 | change_tick: u32, 704 | ) -> DynamicQueryIter<'w, 's> { 705 | DynamicQueryIter::new(world, self, last_change_tick, change_tick) 706 | } 707 | 708 | pub fn get_mut<'w, 's>( 709 | &'s mut self, 710 | world: &'w mut World, 711 | entity: Entity, 712 | ) -> Result>, QueryEntityError> { 713 | self.update_archetypes(world); 714 | 715 | // SAFETY: query has unique world access 716 | unsafe { self.get_unchecked(world, entity) } 717 | } 718 | 719 | pub fn get<'w, 's>( 720 | &'s mut self, 721 | world: &'w World, 722 | entity: Entity, 723 | ) -> Result>, QueryEntityError> { 724 | self.update_archetypes(world); 725 | 726 | assert!(self.is_readonly()); 727 | // SAFETY: query is readonly and borrows from 'w world 728 | unsafe { self.get_unchecked(world, entity) } 729 | } 730 | 731 | /// # Safety 732 | /// 733 | /// This does not check for mutable query correctness. To be safe, make sure mutable queries 734 | /// have unique access to the components they query. 735 | /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` 736 | /// with a mismatched [`WorldId`] is unsound. 737 | unsafe fn get_unchecked<'w, 's>( 738 | &'s mut self, 739 | world: &'w World, 740 | entity: Entity, 741 | ) -> Result>, QueryEntityError> { 742 | self.update_archetypes(world); 743 | 744 | self.get_unchecked_manual(world, entity) 745 | } 746 | /// # Safety 747 | /// 748 | /// This does not check for mutable query correctness. To be safe, make sure mutable queries 749 | /// have unique access to the components they query. 750 | /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` 751 | /// with a mismatched [`WorldId`] is unsound. 752 | unsafe fn get_unchecked_manual<'w, 's>( 753 | &'s mut self, 754 | world: &'w World, 755 | entity: Entity, 756 | ) -> Result>, QueryEntityError> { 757 | let last_change_tick = world.last_change_tick(); 758 | let change_tick = world.read_change_tick(); 759 | 760 | let location = world 761 | .entities() 762 | .get(entity) 763 | .ok_or(QueryEntityError::NoSuchEntity(entity))?; 764 | if !self 765 | .matched_archetypes 766 | .contains(location.archetype_id.index()) 767 | { 768 | return Err(QueryEntityError::QueryDoesNotMatch(entity)); 769 | } 770 | 771 | let archetype = &world.archetypes()[location.archetype_id]; 772 | 773 | let mut fetch = unsafe { 774 | ComponentFetchStates::init( 775 | world, 776 | last_change_tick, 777 | change_tick, 778 | &self.component_fetches, 779 | ) 780 | }; 781 | let mut filter = unsafe { 782 | ComponentFilterStates::init(world, last_change_tick, change_tick, &self.filters) 783 | }; 784 | 785 | fetch.set_archetype(archetype, &world.storages().tables); 786 | filter.set_archetype(archetype, &world.storages().tables); 787 | 788 | if filter.fetch(entity, location.index) { 789 | Ok(fetch.fetch(entity, location.index)) 790 | } else { 791 | Err(QueryEntityError::QueryDoesNotMatch(entity)) 792 | } 793 | } 794 | } 795 | 796 | pub struct DynamicQueryIter<'w, 's> { 797 | tables: &'w Tables, 798 | archetypes: &'w Archetypes, 799 | is_dense: bool, 800 | table_entities: &'w [Entity], 801 | archetype_entities: &'w [ArchetypeEntity], 802 | fetch: ComponentFetchStates<'w>, 803 | filter: ComponentFilterStates<'w>, 804 | table_id_iter: std::slice::Iter<'s, TableId>, 805 | archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, 806 | current_len: usize, 807 | current_index: usize, 808 | } 809 | impl<'w, 's> DynamicQueryIter<'w, 's> { 810 | fn new( 811 | world: &'w World, 812 | query: &'s DynamicQuery, 813 | last_change_tick: u32, 814 | change_tick: u32, 815 | ) -> DynamicQueryIter<'w, 's> { 816 | let fetch = unsafe { 817 | ComponentFetchStates::init( 818 | world, 819 | last_change_tick, 820 | change_tick, 821 | &query.component_fetches, 822 | ) 823 | }; 824 | let filter = unsafe { 825 | ComponentFilterStates::init(world, last_change_tick, change_tick, &query.filters) 826 | }; 827 | 828 | let is_dense = fetch.is_dense() && filter.is_dense(); 829 | 830 | DynamicQueryIter { 831 | tables: &world.storages().tables, 832 | archetypes: world.archetypes(), 833 | table_entities: &[], 834 | archetype_entities: &[], 835 | is_dense, 836 | fetch, 837 | filter, 838 | table_id_iter: query.matched_table_ids.iter(), 839 | archetype_id_iter: query.matched_archetype_ids.iter(), 840 | current_len: 0, 841 | current_index: 0, 842 | } 843 | } 844 | } 845 | 846 | pub struct DynamicQueryItem<'w> { 847 | pub entity: Entity, 848 | pub items: Vec>, 849 | } 850 | 851 | impl<'w, 's> Iterator for DynamicQueryIter<'w, 's> { 852 | type Item = DynamicQueryItem<'w>; 853 | 854 | fn next(&mut self) -> Option> { 855 | unsafe { 856 | if self.is_dense { 857 | loop { 858 | if self.current_index == self.current_len { 859 | let table_id = self.table_id_iter.next()?; 860 | let table = &self.tables[*table_id]; 861 | self.fetch.set_table(table); 862 | self.filter.set_table(table); 863 | self.table_entities = table.entities(); 864 | self.current_len = table.entity_count(); 865 | self.current_index = 0; 866 | continue; 867 | } 868 | 869 | // SAFETY: set_table was called prior. 870 | // `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed. 871 | let entity = self.table_entities.get_unchecked(self.current_index); 872 | if !self.filter.fetch(*entity, self.current_index) { 873 | self.current_index += 1; 874 | continue; 875 | } 876 | 877 | let entity = self.fetch.entity(self.current_index); 878 | let items = self.fetch.fetch(entity, self.current_index); 879 | 880 | self.current_index += 1; 881 | 882 | return Some(DynamicQueryItem { entity, items }); 883 | } 884 | } else { 885 | loop { 886 | if self.current_index == self.current_len { 887 | let archetype_id = self.archetype_id_iter.next()?; 888 | let archetype = &self.archetypes[*archetype_id]; 889 | self.fetch.set_archetype(archetype, self.tables); 890 | self.filter.set_archetype(archetype, self.tables); 891 | self.archetype_entities = archetype.entities(); 892 | self.current_len = archetype.len(); 893 | self.current_index = 0; 894 | continue; 895 | } 896 | 897 | // SAFETY: set_archetype was called prior. 898 | // `current_index` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed. 899 | let archetype_entity = 900 | self.archetype_entities.get_unchecked(self.current_index); 901 | if !self 902 | .filter 903 | .fetch(archetype_entity.entity(), archetype_entity.table_row()) 904 | { 905 | self.current_index += 1; 906 | continue; 907 | } 908 | 909 | let entity = self.fetch.entity(self.current_index); 910 | let items = self 911 | .fetch 912 | .fetch(archetype_entity.entity(), archetype_entity.table_row()); 913 | 914 | self.current_index += 1; 915 | 916 | return Some(DynamicQueryItem { entity, items }); 917 | } 918 | } 919 | } 920 | } 921 | } 922 | 923 | #[cfg(test)] 924 | mod tests { 925 | use super::{DynamicQuery, FetchKind, FetchResult, FilterKind}; 926 | use bevy_ecs::prelude::*; 927 | 928 | #[derive(Component)] 929 | struct TestComponent1; 930 | 931 | #[derive(Component)] 932 | struct TestComponent2; 933 | 934 | #[derive(Component)] 935 | struct TestComponentWithData(&'static str); 936 | 937 | #[test] 938 | fn query_empty() { 939 | let mut world = World::new(); 940 | let component_id = world.init_component::(); 941 | 942 | let mut query = 943 | DynamicQuery::new(&world, vec![FetchKind::Ref(component_id)], vec![]).unwrap(); 944 | let count = query.iter_mut(&mut world).count(); 945 | assert_eq!(count, 0); 946 | } 947 | 948 | #[test] 949 | fn query_fetch() { 950 | let mut world = World::new(); 951 | let component_id_1 = world.init_component::(); 952 | let component_id_2 = world.init_component::(); 953 | 954 | let entity = world 955 | .spawn((TestComponentWithData("test"), TestComponent2)) 956 | .id(); 957 | 958 | let mut query = DynamicQuery::new( 959 | &world, 960 | vec![ 961 | FetchKind::Ref(component_id_1), 962 | FetchKind::RefMut(component_id_2), 963 | ], 964 | vec![], 965 | ) 966 | .unwrap(); 967 | 968 | let results: Vec<_> = query.iter_mut(&mut world).collect(); 969 | assert_eq!(results.len(), 1); 970 | 971 | let item = &results[0]; 972 | assert_eq!(item.entity, entity); 973 | 974 | let ptr = match item.items[0] { 975 | FetchResult::Ref(ptr) => ptr, 976 | _ => unreachable!(), 977 | }; 978 | let value = unsafe { ptr.deref::() }; 979 | assert_eq!(value.0, "test"); 980 | } 981 | 982 | #[test] 983 | fn query_fetch_filter_with() { 984 | let mut world = World::new(); 985 | let component_id_1 = world.init_component::(); 986 | let component_id_2 = world.init_component::(); 987 | 988 | world.spawn((TestComponent1, TestComponent2)); 989 | world.spawn(TestComponent1); 990 | 991 | let mut query = 992 | DynamicQuery::new(&world, vec![], vec![FilterKind::With(component_id_2)]).unwrap(); 993 | let count = query.iter_mut(&mut world).count(); 994 | assert_eq!(count, 1); 995 | 996 | let mut query = 997 | DynamicQuery::new(&world, vec![], vec![FilterKind::With(component_id_1)]).unwrap(); 998 | let count = query.iter_mut(&mut world).count(); 999 | assert_eq!(count, 2); 1000 | } 1001 | 1002 | #[test] 1003 | fn query_fetch_filter_without() { 1004 | let mut world = World::new(); 1005 | let component_id_1 = world.init_component::(); 1006 | let component_id_2 = world.init_component::(); 1007 | 1008 | world.spawn((TestComponent1, TestComponent2)); 1009 | world.spawn(TestComponent1); 1010 | 1011 | let mut query = 1012 | DynamicQuery::new(&world, vec![], vec![FilterKind::Without(component_id_2)]).unwrap(); 1013 | let count = query.iter_mut(&mut world).count(); 1014 | assert_eq!(count, 1); 1015 | 1016 | let mut query = 1017 | DynamicQuery::new(&world, vec![], vec![FilterKind::Without(component_id_1)]).unwrap(); 1018 | let count = query.iter_mut(&mut world).count(); 1019 | assert_eq!(count, 0); 1020 | } 1021 | 1022 | #[test] 1023 | #[should_panic = "Ref(ComponentId(0)) conflicts with a previous access in this query"] 1024 | fn query_component_access_valid() { 1025 | let mut world = World::new(); 1026 | let component_id_1 = world.init_component::(); 1027 | 1028 | let err = DynamicQuery::new( 1029 | &world, 1030 | vec![ 1031 | FetchKind::RefMut(component_id_1), 1032 | FetchKind::Ref(component_id_1), 1033 | ], 1034 | vec![], 1035 | ) 1036 | .map(drop) 1037 | .unwrap_err(); 1038 | 1039 | panic!("{err}"); 1040 | } 1041 | 1042 | #[test] 1043 | fn query_filter_added() { 1044 | let mut world = World::new(); 1045 | let component_id_1 = world.init_component::(); 1046 | 1047 | world.spawn(TestComponent1); 1048 | 1049 | let mut query = 1050 | DynamicQuery::new(&world, vec![], vec![FilterKind::Added(component_id_1)]).unwrap(); 1051 | let count = query.iter_mut(&mut world).count(); 1052 | assert_eq!(count, 1); 1053 | 1054 | world.increment_change_tick(); 1055 | world.clear_trackers(); 1056 | 1057 | let mut query = 1058 | DynamicQuery::new(&world, vec![], vec![FilterKind::Added(component_id_1)]).unwrap(); 1059 | let count = query.iter_mut(&mut world).count(); 1060 | assert_eq!(count, 0); 1061 | } 1062 | } 1063 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod dynamic_query; 2 | #[warn(unsafe_op_in_unsafe_fn)] 3 | pub mod reflect_value_ref; 4 | -------------------------------------------------------------------------------- /src/reflect_value_ref.rs: -------------------------------------------------------------------------------- 1 | //! Types for referencing ecs values (like components and resources) in a way that doesn't deal with raw pointers. 2 | 3 | use std::cell::RefCell; 4 | use std::ops::{Deref, DerefMut}; 5 | use std::rc::Rc; 6 | 7 | use bevy_app::AppTypeRegistry; 8 | use bevy_ecs::component::ComponentId; 9 | use bevy_ecs::prelude::*; 10 | use bevy_ecs::ptr::Ptr; 11 | use bevy_ecs::{change_detection::MutUntyped, world::WorldId}; 12 | use bevy_reflect::{GetPath, Reflect, ReflectFromPtr}; 13 | 14 | pub mod query; 15 | 16 | /// Error type enum for errors dealing with [`ReflectValueRef`]s 17 | #[derive(Debug)] 18 | pub enum ReflectValueRefError { 19 | InexistentComponent(ComponentId), 20 | TypeRegistryNotInWorld, 21 | NoTypeId(ComponentId), 22 | NoReflectFromPtr(ComponentId), 23 | InvalidBaseValue(EcsBase), 24 | InvalidPath { error_message: String, path: String, type_name: String }, 25 | } 26 | 27 | impl std::error::Error for ReflectValueRefError {} 28 | impl std::fmt::Display for ReflectValueRefError { 29 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 30 | match self { 31 | ReflectValueRefError::InexistentComponent(id) => { 32 | write!(f, "Component {id:?} does not exist") 33 | } 34 | ReflectValueRefError::TypeRegistryNotInWorld => { 35 | write!(f, "no `TypeRegistry` exists in the world") 36 | } 37 | ReflectValueRefError::NoTypeId(id) => write!( 38 | f, 39 | "Component {id:?} has no associated `TypeId` and can't be reflected" 40 | ), 41 | ReflectValueRefError::NoReflectFromPtr(id) => write!( 42 | f, 43 | "Component {id:?} has no registered `ReflectFromPtr` type data" 44 | ), 45 | ReflectValueRefError::InvalidBaseValue(EcsBase::Component(entity, id)) => { 46 | write!( 47 | f, 48 | "The world has no component {id:?} at the entity {entity:?}" 49 | ) 50 | } 51 | ReflectValueRefError::InvalidBaseValue(EcsBase::Resource(id)) => { 52 | write!(f, "The world has no resource {id:?}") 53 | } 54 | ReflectValueRefError::InvalidPath { 55 | error_message: msg, 56 | path, 57 | type_name, 58 | } => { 59 | write!(f, "invalid path on {type_name}: {msg} (\"{path}\")") 60 | } 61 | } 62 | } 63 | } 64 | 65 | /// Either a `(Entity, ComponentId`) pair or a Resource-`ComponentId` 66 | #[derive(Debug, Clone, Copy)] 67 | pub enum EcsBase { 68 | Component(Entity, ComponentId), 69 | Resource(ComponentId), 70 | } 71 | 72 | impl EcsBase { 73 | pub fn access(self, world: &World) -> Option> { 74 | match self { 75 | EcsBase::Component(entity, component_id) => world.get_by_id(entity, component_id), 76 | EcsBase::Resource(component_id) => world.get_resource_by_id(component_id), 77 | } 78 | } 79 | 80 | pub fn access_mut(self, world: &mut World) -> Option> { 81 | match self { 82 | EcsBase::Component(entity, component_id) => world.get_mut_by_id(entity, component_id), 83 | EcsBase::Resource(component_id) => world.get_resource_mut_by_id(component_id), 84 | } 85 | } 86 | 87 | pub fn component_id(self) -> ComponentId { 88 | match self { 89 | EcsBase::Component(_, id) => id, 90 | EcsBase::Resource(id) => id, 91 | } 92 | } 93 | } 94 | 95 | /// [`EcsBase`] together with the necessary data for safely accessing its value as [`Reflect`] 96 | #[derive(Clone)] 97 | pub struct EcsValueRef { 98 | base: EcsBase, 99 | world_id: WorldId, 100 | // must be for the type referenced by the `WorldBase` component id 101 | reflect_from_ptr: ReflectFromPtr, 102 | } 103 | 104 | impl std::fmt::Debug for EcsValueRef { 105 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 106 | f.debug_struct("ReflectValueRef") 107 | .field("world_id", &self.world_id) 108 | .field("base", &self.base) 109 | .finish() 110 | } 111 | } 112 | 113 | // guarantees that `ComponentId` matches `ReflectFromPtr` 114 | fn get_reflect_from_ptr( 115 | world: &World, 116 | component_id: ComponentId, 117 | ) -> Result { 118 | let info = world 119 | .components() 120 | .get_info(component_id) 121 | .ok_or(ReflectValueRefError::InexistentComponent(component_id))?; 122 | let type_id = info 123 | .type_id() 124 | .ok_or(ReflectValueRefError::NoTypeId(component_id))?; 125 | 126 | let type_registry = world 127 | .get_resource::() 128 | .ok_or(ReflectValueRefError::TypeRegistryNotInWorld)?; 129 | let reflect_from_ptr = type_registry 130 | .read() 131 | .get_type_data::(type_id) 132 | .ok_or(ReflectValueRefError::NoReflectFromPtr(component_id))? 133 | .clone(); 134 | assert_eq!(reflect_from_ptr.type_id(), type_id); 135 | 136 | Ok(reflect_from_ptr) 137 | } 138 | 139 | impl EcsValueRef { 140 | pub fn component( 141 | world: &World, 142 | entity: Entity, 143 | component_id: ComponentId, 144 | ) -> Result { 145 | let reflect_from_ptr = get_reflect_from_ptr(world, component_id)?; 146 | let base = EcsBase::Component(entity, component_id); 147 | 148 | Ok(EcsValueRef { 149 | base, 150 | world_id: world.id(), 151 | reflect_from_ptr, 152 | }) 153 | } 154 | 155 | /// # Safety 156 | /// The `ComponentId` parameter must resolve to a component in the world with `world_id` 157 | /// that has the same type as the one `ReflectFromPtr` was created for 158 | pub unsafe fn component_unchecked( 159 | entity: Entity, 160 | component_id: ComponentId, 161 | reflect_from_ptr: ReflectFromPtr, 162 | world_id: WorldId, 163 | ) -> EcsValueRef { 164 | EcsValueRef { 165 | base: EcsBase::Component(entity, component_id), 166 | world_id, 167 | reflect_from_ptr, 168 | } 169 | } 170 | 171 | pub fn resource( 172 | world: &World, 173 | component_id: ComponentId, 174 | ) -> Result { 175 | let reflect_from_ptr = get_reflect_from_ptr(world, component_id)?; 176 | let base = EcsBase::Resource(component_id); 177 | 178 | Ok(EcsValueRef { 179 | base, 180 | world_id: world.id(), 181 | reflect_from_ptr, 182 | }) 183 | } 184 | 185 | /// # Safety 186 | /// The `ComponentId` parameter must resolve to a resource in the world with `world_id` 187 | /// that has the same type as the one `ReflectFromPtr` was created for 188 | pub unsafe fn resource_unchecked( 189 | component_id: ComponentId, 190 | reflect_from_ptr: ReflectFromPtr, 191 | world_id: WorldId, 192 | ) -> EcsValueRef { 193 | EcsValueRef { 194 | base: EcsBase::Resource(component_id), 195 | world_id, 196 | reflect_from_ptr, 197 | } 198 | } 199 | 200 | pub fn component_id(&self) -> ComponentId { 201 | self.base.component_id() 202 | } 203 | 204 | pub fn get<'w>(&self, world: &'w World) -> Result<&'w dyn Reflect, ReflectValueRefError> { 205 | assert_eq!(self.world_id, world.id()); 206 | 207 | let ptr = self 208 | .base 209 | .access(world) 210 | .ok_or(ReflectValueRefError::InvalidBaseValue(self.base))?; 211 | // SAFETY: 212 | // `val` is a pointer to value of the type that the `ReflectFromPtr` was constructed for, 213 | // because the mapping from `ComponentId -> TypeId` is immutable and `ReflectFromPtr` is checked to be 214 | // for the type of the `WorldBase`'s type id. 215 | let reflect = unsafe { self.reflect_from_ptr.as_reflect_ptr(ptr) }; 216 | 217 | Ok(reflect) 218 | } 219 | pub fn get_mut<'w>( 220 | &self, 221 | world: &'w mut World, 222 | ) -> Result<&'w mut dyn Reflect, ReflectValueRefError> { 223 | // SAFETY: unique world access 224 | unsafe { self.get_mut_unchecked(world) } 225 | } 226 | 227 | /// # Safety 228 | /// This will allow aliased mutable access to the value. The caller must ensure 229 | /// that there is either only one mutable access or multiple immutable accesses at a time. 230 | pub unsafe fn get_mut_unchecked<'w>( 231 | &self, 232 | world: &'w mut World, 233 | ) -> Result<&'w mut dyn Reflect, ReflectValueRefError> { 234 | assert_eq!(self.world_id, world.id()); 235 | 236 | let mut ptr = self 237 | .base 238 | .access_mut(world) 239 | .ok_or(ReflectValueRefError::InvalidBaseValue(self.base))?; 240 | 241 | // TODO: don't mark as changed on every access 242 | ptr.set_changed(); 243 | 244 | // SAFETY: 245 | // `val` is a pointer to value of the type that the `ReflectFromPtr` was constructed for, 246 | // because the mapping from `ComponentId -> TypeId` is immutable and `ReflectFromPtr` is checked to be 247 | // for the type of the `WorldBase`'s type id. 248 | let reflect = unsafe { self.reflect_from_ptr.as_reflect_ptr_mut(ptr.into_inner()) }; 249 | 250 | Ok(reflect) 251 | } 252 | } 253 | 254 | /// A [`ReflectValueRef`] without the path offset 255 | #[derive(Clone, Debug)] 256 | pub enum ReflectValueRefBase { 257 | EccRef(EcsValueRef), 258 | Free(Rc>>), 259 | } 260 | 261 | /// Either a [`EcsValueRef`] or a freestanding [`Reflect`] value, together with a reflect-path offset 262 | #[derive(Clone, Debug)] 263 | pub struct ReflectValueRef { 264 | base: ReflectValueRefBase, 265 | path: String, 266 | } 267 | impl From for ReflectValueRef { 268 | fn from(value: EcsValueRef) -> Self { 269 | ReflectValueRef { 270 | base: ReflectValueRefBase::EccRef(value), 271 | path: String::new(), 272 | } 273 | } 274 | } 275 | 276 | enum MaybeRef<'a, T: ?Sized> { 277 | Direct(&'a T), 278 | Ref(std::cell::Ref<'a, Box>), 279 | } 280 | 281 | /// Wraps a borrowed reference to a value in a [`ReflectValueRef`] 282 | pub struct ReflectValueRefBorrow<'a> { 283 | r: MaybeRef<'a, dyn Reflect>, 284 | path: &'a str, 285 | } 286 | impl std::fmt::Debug for ReflectValueRefBorrow<'_> { 287 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 288 | let base = match self.r { 289 | MaybeRef::Direct(r) => r, 290 | MaybeRef::Ref(ref r) => &***r, 291 | }; 292 | let value = base 293 | .path(self.path) 294 | .expect("paths are checked in `append_path`"); 295 | value.fmt(f) 296 | } 297 | } 298 | 299 | impl<'a> Deref for ReflectValueRefBorrow<'a> { 300 | type Target = dyn Reflect; 301 | fn deref(&self) -> &Self::Target { 302 | let base = match self.r { 303 | MaybeRef::Direct(r) => r, 304 | MaybeRef::Ref(ref r) => &***r, 305 | }; 306 | let value = base 307 | .path(self.path) 308 | .expect("paths are checked in `append_path`"); 309 | value 310 | } 311 | } 312 | 313 | enum MaybeRefMut<'a, T: ?Sized> { 314 | Direct(&'a mut T), 315 | Ref(std::cell::RefMut<'a, Box>), 316 | } 317 | 318 | /// Wraps a borrowed mutable reference to a value in a [`ReflectValueRef`] 319 | pub struct ReflectValueRefBorrowMut<'a> { 320 | r: MaybeRefMut<'a, dyn Reflect>, 321 | path: &'a str, 322 | } 323 | impl std::fmt::Debug for ReflectValueRefBorrowMut<'_> { 324 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 325 | let base = match &self.r { 326 | MaybeRefMut::Direct(r) => &**r, 327 | MaybeRefMut::Ref(ref r) => &***r, 328 | }; 329 | base.fmt(f) 330 | } 331 | } 332 | 333 | impl<'a> Deref for ReflectValueRefBorrowMut<'a> { 334 | type Target = dyn Reflect; 335 | 336 | fn deref(&self) -> &Self::Target { 337 | let base = match self.r { 338 | MaybeRefMut::Direct(ref r) => &**r, 339 | MaybeRefMut::Ref(ref r) => &***r, 340 | }; 341 | let value = base 342 | .path(self.path) 343 | .expect("paths are checked in `append_path`"); 344 | value 345 | } 346 | } 347 | impl<'a> DerefMut for ReflectValueRefBorrowMut<'a> { 348 | fn deref_mut(&mut self) -> &mut Self::Target { 349 | let base = match &mut self.r { 350 | MaybeRefMut::Direct(r) => &mut **r, 351 | MaybeRefMut::Ref(r) => &mut ***r, 352 | }; 353 | let value = base 354 | .path_mut(self.path) 355 | .expect("paths are checked in `append_path`"); 356 | value 357 | } 358 | } 359 | 360 | impl ReflectValueRef { 361 | pub fn free(value: Rc>>) -> Self { 362 | ReflectValueRef { 363 | base: ReflectValueRefBase::Free(value), 364 | path: String::new(), 365 | } 366 | } 367 | 368 | pub fn ecs_ref(value: EcsValueRef) -> Self { 369 | ReflectValueRef { 370 | base: ReflectValueRefBase::EccRef(value), 371 | path: String::new(), 372 | } 373 | } 374 | /// Appends a new path to the reflect path offset of the [`ReflectValueRef`] 375 | /// ```rust,no_run 376 | /// # let value: bevy_ecs_dynamic::reflect_value_ref::ReflectValueRef = unimplemented!(); 377 | /// # let world: bevy_ecs::prelude::World = unimplemented!(); 378 | /// value.append_path(".foo", &world); 379 | /// value.append_path("[3]", &world); 380 | /// ``` 381 | /// This method will *not* check the validity of the path, and further calls to [`get`](Self::get) or [`get_mut`](Self::get_mut) may fail afterwards. 382 | // TODO make this check the path, either by taking the world or accessing static info 383 | pub fn append_path(&self, path: &str, world: &World) -> Result { 384 | let new = ReflectValueRef { 385 | base: self.base.clone(), 386 | path: format!("{}{}", self.path, path), 387 | }; 388 | 389 | { 390 | // TODO: this can surely be less duplicated 391 | let borrow = new.get(world)?; 392 | let base = match borrow.r { 393 | MaybeRef::Direct(r) => r, 394 | MaybeRef::Ref(ref r) => &***r, 395 | }; 396 | base.path(&new.path) 397 | .map_err(|e| ReflectValueRefError::InvalidPath { 398 | type_name: base.type_name().to_string(), 399 | error_message: e.to_string(), 400 | path: new.path.clone(), 401 | })?; 402 | } 403 | 404 | Ok(new) 405 | } 406 | 407 | pub fn get<'s, 'w: 's>( 408 | &'s self, 409 | world: &'w World, 410 | ) -> Result, ReflectValueRefError> { 411 | match &self.base { 412 | ReflectValueRefBase::EccRef(value) => { 413 | let value = value.get(world)?; 414 | Ok(ReflectValueRefBorrow { 415 | r: MaybeRef::Direct(value), 416 | path: &self.path, 417 | }) 418 | } 419 | ReflectValueRefBase::Free(value) => { 420 | let value = value.borrow(); 421 | Ok(ReflectValueRefBorrow { 422 | r: MaybeRef::Ref(value), 423 | path: &self.path, 424 | }) 425 | } 426 | } 427 | } 428 | pub fn get_mut<'s, 'w: 's>( 429 | &'s mut self, 430 | world: &'w mut World, 431 | ) -> Result, ReflectValueRefError> { 432 | match &self.base { 433 | ReflectValueRefBase::EccRef(value) => { 434 | let value = value.get_mut(world)?; 435 | Ok(ReflectValueRefBorrowMut { 436 | r: MaybeRefMut::Direct(value), 437 | path: &self.path, 438 | }) 439 | } 440 | ReflectValueRefBase::Free(value) => { 441 | let value = value.borrow_mut(); 442 | Ok(ReflectValueRefBorrowMut { 443 | r: MaybeRefMut::Ref(value), 444 | path: &self.path, 445 | }) 446 | } 447 | } 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /src/reflect_value_ref/query.rs: -------------------------------------------------------------------------------- 1 | use bevy_ecs::{component::ComponentId, prelude::*, query::QueryEntityError, world::WorldId}; 2 | use bevy_reflect::ReflectFromPtr; 3 | 4 | use crate::dynamic_query::{DynamicQuery, DynamicQueryIter, FilterKind}; 5 | 6 | use super::{get_reflect_from_ptr, EcsValueRef}; 7 | 8 | /// Wrapper around [`DynamicQuery`] that yields [`EcsValueRef`]s 9 | pub struct EcsValueRefQuery { 10 | query: DynamicQuery, 11 | } 12 | 13 | impl EcsValueRefQuery { 14 | pub fn new(world: &World, components: &[ComponentId]) -> Self { 15 | Self { 16 | query: DynamicQuery::new( 17 | world, 18 | vec![], 19 | components.iter().map(|&id| FilterKind::With(id)).collect(), 20 | ) 21 | .expect("dynamic query construction cannot fail because only filters are supplied"), 22 | } 23 | } 24 | 25 | pub fn get<'w, 's>( 26 | &'s mut self, 27 | world: &'w World, 28 | entity: Entity, 29 | ) -> Result, QueryEntityError> { 30 | let components = self.components(world); 31 | self.query.get(world, entity)?; 32 | 33 | let results = components 34 | .iter() 35 | .map(|(component_id, reflect_from_ptr)| { 36 | // SAFETY: component_id matches the reflect_from_ptr 37 | unsafe { 38 | EcsValueRef::component_unchecked( 39 | entity, 40 | *component_id, 41 | reflect_from_ptr.clone(), 42 | world.id(), 43 | ) 44 | } 45 | }) 46 | .collect(); 47 | 48 | Ok(results) 49 | } 50 | 51 | pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> EcsValueRefQueryIter<'w, 's> { 52 | let components = self.components(world); 53 | 54 | EcsValueRefQueryIter { 55 | world_id: world.id(), 56 | components, 57 | iter: self.query.iter(world), 58 | } 59 | } 60 | 61 | fn components<'s>(&'s mut self, world: &World) -> Vec<(ComponentId, ReflectFromPtr)> { 62 | self.query 63 | .filters() 64 | .iter() 65 | .map(|filter| match *filter { 66 | FilterKind::With(id) => id, 67 | _ => unreachable!(), 68 | }) 69 | .map(|component_id| { 70 | // TODO no unwrap 71 | let reflect_from_ptr = get_reflect_from_ptr(world, component_id).unwrap(); 72 | (component_id, reflect_from_ptr) 73 | }) 74 | .collect() 75 | } 76 | } 77 | 78 | /// Iterator type of the [`EcsValueRefQuery`] 79 | pub struct EcsValueRefQueryIter<'w, 's> { 80 | world_id: WorldId, 81 | iter: DynamicQueryIter<'w, 's>, 82 | components: Vec<(ComponentId, ReflectFromPtr)>, 83 | } 84 | 85 | /// Item type of the [`EcsValueRefQuery`] 86 | pub struct EcsValueRefQueryItem { 87 | pub entity: Entity, 88 | pub items: Vec, 89 | } 90 | 91 | impl<'w, 's> Iterator for EcsValueRefQueryIter<'w, 's> { 92 | type Item = EcsValueRefQueryItem; 93 | 94 | fn next(&mut self) -> Option { 95 | let item = self.iter.next()?; 96 | 97 | let items: Vec<_> = self 98 | .components 99 | .iter() 100 | .map(|(component_id, reflect_from_ptr)| unsafe { 101 | EcsValueRef::component_unchecked( 102 | item.entity, 103 | *component_id, 104 | reflect_from_ptr.clone(), 105 | self.world_id, 106 | ) 107 | }) 108 | .collect(); 109 | 110 | Some(EcsValueRefQueryItem { 111 | entity: item.entity, 112 | items, 113 | }) 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use bevy_app::AppTypeRegistry; 120 | use bevy_ecs::prelude::*; 121 | use bevy_reflect::Reflect; 122 | 123 | use crate::reflect_value_ref::ReflectValueRef; 124 | 125 | use super::EcsValueRefQuery; 126 | 127 | #[derive(Component, Reflect)] 128 | 129 | struct TestComponent1 { 130 | value: String, 131 | } 132 | 133 | #[derive(Component, Reflect)] 134 | 135 | struct TestComponent2 { 136 | field: u8, 137 | } 138 | 139 | #[test] 140 | fn iter() { 141 | let mut world = World::new(); 142 | 143 | let component_id_1 = world.init_component::(); 144 | let component_id_2 = world.init_component::(); 145 | 146 | let type_registry = world.get_resource_or_insert_with(AppTypeRegistry::default); 147 | { 148 | let mut type_registry = type_registry.write(); 149 | type_registry.register::(); 150 | type_registry.register::(); 151 | } 152 | 153 | world.spawn(TestComponent1 { value: "no".into() }); 154 | world.spawn(( 155 | TestComponent1 { 156 | value: "yes".into(), 157 | }, 158 | TestComponent2 { field: 5 }, 159 | )); 160 | 161 | let mut query = EcsValueRefQuery::new(&world, &[component_id_1, component_id_2]); 162 | let results: Vec<_> = query.iter(&world).collect(); 163 | assert_eq!(results.len(), 1); 164 | 165 | match results[0].items.as_slice() { 166 | [a, b] => { 167 | assert_eq!(a.component_id(), component_id_1); 168 | assert_eq!(b.component_id(), component_id_2); 169 | 170 | let a = ReflectValueRef::from(a.clone()); 171 | assert_eq!( 172 | a.append_path(".value", &world) 173 | .unwrap() 174 | .get(&world) 175 | .unwrap() 176 | .downcast_ref::() 177 | .unwrap(), 178 | "yes" 179 | ) 180 | } 181 | _ => unreachable!(), 182 | } 183 | } 184 | } 185 | --------------------------------------------------------------------------------