From 5583f9a3bcf8e23f3368143fe5f7baf7a783b6fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Mon, 26 Jul 2021 00:22:42 +0000 Subject: [PATCH 01/79] Android uninstall 31 (#2533) # Objective - Related to #2514 - not sure if it's a proper fix long term - CI was complaining Error: Path `"/usr/local/lib/android/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android31-clang"` was not found - A lot of questions started popping up 10 days ago about ["android build tools 31 corrupted"](https://www.google.com/search?q=android+build+tools+31+corrupted) - https://stackoverflow.com/questions/68387270/android-studio-error-installed-build-tools-revision-31-0-0-is-corrupted - Uninstalling `"build-tools;31.0.0"` doesn't seem to work, as it removes other components even though `"build-tools;30.0.3"` are available ## Solution - Uninstalling `"platforms;android-31"` seems to do the trick and `cargo-apk` stops trying to target `...31` Android is not my thing, this solution was found with a lot of trials and errors. I am not sure how long term it is, I don't know the release schedule of android build tools, or if we need to target this 31 thing. I just wanted to stop all those failed ci everywhere... --- .github/bors.toml | 1 + .github/workflows/ci.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/bors.toml b/.github/bors.toml index 15d17706b39b8..ab84c8e3e133b 100644 --- a/.github/bors.toml +++ b/.github/bors.toml @@ -5,6 +5,7 @@ status = [ "build (nightly, ubuntu-latest)", "build-wasm (stable, ubuntu-latest)", "build-wasm (nightly, ubuntu-latest)", + "build-android", "markdownlint", "check-markdown-links", "run-examples", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index af23b4c778af9..4f5f34c0434e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,6 +71,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Uninstall android-31 + run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --uninstall "platforms;android-31" - name: Install Android targets run: rustup target add aarch64-linux-android armv7-linux-androideabi - name: Install Cargo APK From d3ae816e3e773b37713c7a51acc236122d07b14f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois?= Date: Tue, 27 Jul 2021 02:01:11 +0000 Subject: [PATCH 02/79] Ci stability and speed improvements (#2551) # Objective - There are a few random failures in CI, mostly due to contacting crates.io or checking for deadlinks - CI can take some time, more than 20 minutes for a full status - A clippy/format issue stops running tests on other platforms ## Solution - Use GitHub cache for cargo artefacts - This speeds up builds and reduce dependencies on outside world - Reorder and add dependencies between short jobs. They are still setup to run even if one of the dependency failed - This reduce the number of parallel jobs that are running for one PR. On GitHub free tier, we're limited to 20. - Split CI checks (format & clippy) in its own job - This speeds up test jobs, and allow us to not kill all platform tests for a format issue - Retry in case of dead links check failure - Internet is just that kind of place where things may seem dead at some point but back alive 5 seconds later ## Before Screenshot 2021-07-27 at 01 18 52 ## After (with all cache live) Screenshot 2021-07-27 at 01 18 28 --- .github/workflows/ci.yml | 144 +++++++++++++++++++++++++++++++-------- 1 file changed, 117 insertions(+), 27 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f5f34c0434e8..e8876a38cebdd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,28 +24,52 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-build-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} - components: rustfmt, clippy override: true - - name: Install alsa and udev run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev if: runner.os == 'linux' - - - name: Check the format - # See tools/ci/src/main.rs for the commands this runs - run: cargo run -p ci - if: runner.os == 'linux' && matrix.toolchain == 'stable' - - name: Build & run tests run: cargo test --workspace env: CARGO_INCREMENTAL: 0 RUSTFLAGS: "-C debuginfo=0 -D warnings" + ci: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-ci-${{ hashFiles('**/Cargo.toml') }} + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt, clippy + override: true + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + - name: CI job + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci + build-wasm: strategy: matrix: @@ -54,13 +78,20 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-build-wasm-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.toolchain }} target: wasm32-unknown-unknown override: true - - name: Check wasm uses: actions-rs/cargo@v1 with: @@ -71,23 +102,33 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-build-android-${{ hashFiles('**/Cargo.toml') }} - name: Uninstall android-31 run: $ANDROID_SDK_ROOT/cmdline-tools/latest/bin/sdkmanager --uninstall "platforms;android-31" - name: Install Android targets run: rustup target add aarch64-linux-android armv7-linux-androideabi - name: Install Cargo APK - run: cargo install cargo-apk + run: cargo install --force cargo-apk - name: Build APK run: cargo apk build --example android markdownlint: runs-on: ubuntu-latest + needs: check-missing-examples-in-docs + if: always() steps: - uses: actions/checkout@v2 with: # Full git history is needed to get a proper list of changed files within `super-linter` fetch-depth: 0 - - name: Run Markdown Lint uses: docker://ghcr.io/github/super-linter:slim-v4 env: @@ -99,41 +140,83 @@ jobs: check-markdown-links: runs-on: ubuntu-latest + needs: markdownlint + if: always() steps: - uses: actions/checkout@v2 - - uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 + - name: check dead links + continue-on-error: true + id: run1 + uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 + with: + use-quiet-mode: 'yes' + use-verbose-mode: 'yes' + config-file: '.github/linters/markdown-link-check.json' + - name: Sleep for 30 seconds + if: steps.run1.outcome=='failure' + run: sleep 30s + shell: bash + - name: check dead links (retry) + continue-on-error: true + id: run2 + if: steps.run1.outcome=='failure' + uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 + with: + use-quiet-mode: 'yes' + use-verbose-mode: 'yes' + config-file: '.github/linters/markdown-link-check.json' + - name: Sleep for 30 seconds + if: steps.run2.outcome=='failure' + run: sleep 30s + shell: bash + - name: check dead links (retry 2) + continue-on-error: true + id: run3 + if: steps.run2.outcome=='failure' + uses: gaurav-nelson/github-action-markdown-link-check@9710f0fec812ce0a3b98bef4c9d842fc1f39d976 with: use-quiet-mode: 'yes' use-verbose-mode: 'yes' config-file: '.github/linters/markdown-link-check.json' + - name: set the status + if: always() + run: | + if ${{ steps.run1.outcome=='success' || steps.run2.outcome=='success' || steps.run3.outcome=='success' }}; then + echo success + else + exit 1 + fi run-examples: runs-on: ubuntu-latest - steps: - name: Install dependencies run: | sudo apt-get update; DEBIAN_FRONTEND=noninteractive sudo apt-get install --no-install-recommends -yq \ libasound2-dev libudev-dev wget unzip xvfb; - - uses: actions/checkout@v2 - + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-run-examples-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/toolchain@v1 with: toolchain: stable - - name: Setup swiftshader run: | wget https://github.com/qarmin/gtk_library_store/releases/download/3.24.0/swiftshader.zip; unzip swiftshader.zip; curr="$(pwd)/libvk_swiftshader.so"; sed -i "s|PATH_TO_CHANGE|$curr|" vk_swiftshader_icd.json; - - name: Build bevy run: | cargo build --no-default-features --features "bevy_dynamic_plugin,bevy_gilrs,bevy_gltf,bevy_wgpu,bevy_winit,render,png,hdr,x11,bevy_ci_testing" - - name: Run examples run: | for example in .github/example-run/*.ron; do @@ -145,19 +228,21 @@ jobs: check-doc: runs-on: ubuntu-latest + needs: check-markdown-links + if: always() steps: - uses: actions/checkout@v2 - name: Install alsa and udev run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev if: runner.os == 'linux' - name: Installs cargo-deadlinks - run: cargo install cargo-deadlinks + run: cargo install --force cargo-deadlinks - name: Build and check doc run: RUSTDOCFLAGS='-D warnings' cargo doc --all-features --no-deps - name: Checks dead links run: cargo deadlinks --dir target/doc/bevy continue-on-error: true - + check-missing-examples-in-docs: runs-on: ubuntu-latest steps: @@ -175,17 +260,22 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - + - uses: actions/cache@v2 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-check-unused-dependencies-${{ hashFiles('**/Cargo.toml') }} - uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true - - name: Installs cargo-udeps - run: cargo install cargo-udeps - + run: cargo install --force cargo-udeps - name: Install alsa and udev run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev - - name: Run cargo udeps run: cargo udeps From c83a184e2ff92cfc5589c096ba477ebb6003045b Mon Sep 17 00:00:00 2001 From: Boxy Date: Tue, 27 Jul 2021 05:16:47 +0000 Subject: [PATCH 03/79] Dedupe move logic in remove_bundle and remove_bundle_intersection (#2521) This logic was in both `remove_bundle` and ` remove_bundle_intersection` but only differed by whether we call `.._forget_missing_..` or `.._drop_missing_..` --- crates/bevy_ecs/src/world/entity_ref.rs | 106 ++++++++++++++---------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 11a0a8b842800..6e7185dc5b625 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -340,6 +340,42 @@ impl<'w> EntityMut<'w> { }) }; + unsafe { + Self::move_entity_from_remove::( + entity, + &mut self.location, + old_location.archetype_id, + old_location, + entities, + archetypes, + storages, + new_archetype_id, + ); + } + + Some(result) + } + + /// Safety: `new_archetype_id` must have the same or a subset of the components + /// in `old_archetype_id`. Probably more safety stuff too, audit a call to + /// this fn as if the code here was written inline + /// + /// when DROP is true removed components will be dropped otherwise they will be forgotten + /// + // We use a const generic here so that we are less reliant on + // inlining for rustc to optimize out the `match DROP` + #[allow(clippy::too_many_arguments)] + unsafe fn move_entity_from_remove( + entity: Entity, + self_location: &mut EntityLocation, + old_archetype_id: ArchetypeId, + old_location: EntityLocation, + entities: &mut Entities, + archetypes: &mut Archetypes, + storages: &mut Storages, + new_archetype_id: ArchetypeId, + ) { + let old_archetype = &mut archetypes[old_archetype_id]; let remove_result = old_archetype.swap_remove(old_location.index); if let Some(swapped_entity) = remove_result.swapped_entity { entities.meta[swapped_entity.id as usize].location = old_location; @@ -349,34 +385,34 @@ impl<'w> EntityMut<'w> { let new_archetype = &mut archetypes[new_archetype_id]; let new_location = if old_table_id == new_archetype.table_id() { - unsafe { new_archetype.allocate(entity, old_table_row) } + new_archetype.allocate(entity, old_table_row) } else { let (old_table, new_table) = storages .tables .get_2_mut(old_table_id, new_archetype.table_id()); - // SAFE: table_row exists. All "missing" components have been extracted into the bundle - // above and the caller takes ownership - let move_result = - unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) }; + // SAFE: old_table_row exists + let move_result = if DROP { + old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) + } else { + old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) + }; - // SAFE: new_table_row is a valid position in new_archetype's table - let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; + // SAFE: move_result.new_row is a valid position in new_archetype's table + let new_location = new_archetype.allocate(entity, move_result.new_row); // if an entity was moved into this entity's table spot, update its table row if let Some(swapped_entity) = move_result.swapped_entity { let swapped_location = entities.get(swapped_entity).unwrap(); - let archetype = &mut archetypes[swapped_location.archetype_id]; - archetype.set_entity_table_row(swapped_location.index, old_table_row); + archetypes[swapped_location.archetype_id] + .set_entity_table_row(swapped_location.index, old_table_row); } new_location }; - self.location = new_location; - entities.meta[self.entity.id as usize].location = new_location; - - Some(result) + *self_location = new_location; + entities.meta[entity.id as usize].location = new_location; } /// Remove any components in the bundle that the entity has. @@ -425,40 +461,18 @@ impl<'w> EntityMut<'w> { } } - let remove_result = old_archetype.swap_remove(old_location.index); - if let Some(swapped_entity) = remove_result.swapped_entity { - entities.meta[swapped_entity.id as usize].location = old_location; + unsafe { + Self::move_entity_from_remove::( + entity, + &mut self.location, + old_location.archetype_id, + old_location, + entities, + archetypes, + storages, + new_archetype_id, + ) } - let old_table_row = remove_result.table_row; - let old_table_id = old_archetype.table_id(); - let new_archetype = &mut archetypes[new_archetype_id]; - - let new_location = if old_table_id == new_archetype.table_id() { - unsafe { new_archetype.allocate(entity, old_table_row) } - } else { - let (old_table, new_table) = storages - .tables - .get_2_mut(old_table_id, new_archetype.table_id()); - - // SAFE: table_row exists - let move_result = - unsafe { old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) }; - - // SAFE: new_table_row is a valid position in new_archetype's table - let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; - - // if an entity was moved into this entity's table spot, update its table row - if let Some(swapped_entity) = move_result.swapped_entity { - let swapped_location = entities.get(swapped_entity).unwrap(); - archetypes[swapped_location.archetype_id] - .set_entity_table_row(swapped_location.index, old_table_row); - } - - new_location - }; - - self.location = new_location; - entities.meta[self.entity.id as usize].location = new_location; } pub fn insert(&mut self, value: T) -> &mut Self { From 6d6bc2a8b4c243a670b34032de8ed9dec7d70d44 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Tue, 27 Jul 2021 20:21:06 +0000 Subject: [PATCH 04/79] Merge AppBuilder into App (#2531) This is extracted out of eb8f973646476b4a4926ba644a77e2b3a5772159 and includes some additional changes to remove all references to AppBuilder and fix examples that still used App::build() instead of App::new(). In addition I didn't extract the sub app feature as it isn't ready yet. You can use `git diff --diff-filter=M eb8f973646476b4a4926ba644a77e2b3a5772159` to find all differences in this PR. The `--diff-filtered=M` filters all files added in the original commit but not in this commit away. Co-Authored-By: Carter Anderson --- crates/bevy_app/src/app.rs | 541 ++++++++++++++++- crates/bevy_app/src/app_builder.rs | 559 ------------------ crates/bevy_app/src/ci_testing.rs | 4 +- crates/bevy_app/src/lib.rs | 7 +- crates/bevy_app/src/plugin.rs | 6 +- crates/bevy_app/src/plugin_group.rs | 4 +- crates/bevy_app/src/schedule_runner.rs | 11 +- crates/bevy_asset/src/assets.rs | 12 +- .../asset_count_diagnostics_plugin.rs | 2 +- crates/bevy_asset/src/lib.rs | 12 +- crates/bevy_audio/src/lib.rs | 2 +- crates/bevy_core/src/lib.rs | 10 +- .../src/entity_count_diagnostics_plugin.rs | 4 +- .../src/frame_time_diagnostics_plugin.rs | 2 +- crates/bevy_diagnostic/src/lib.rs | 2 +- .../src/log_diagnostics_plugin.rs | 2 +- crates/bevy_dynamic_plugin/src/loader.rs | 4 +- crates/bevy_ecs/src/event.rs | 6 +- crates/bevy_ecs/src/schedule/stage.rs | 2 +- crates/bevy_ecs/src/system/system.rs | 2 +- crates/bevy_gilrs/src/lib.rs | 4 +- crates/bevy_gltf/src/lib.rs | 2 +- crates/bevy_input/src/lib.rs | 2 +- crates/bevy_log/src/lib.rs | 16 +- crates/bevy_pbr/src/lib.rs | 6 +- crates/bevy_render/src/lib.rs | 6 +- crates/bevy_render/src/wireframe/mod.rs | 4 +- crates/bevy_scene/src/lib.rs | 2 +- crates/bevy_sprite/src/lib.rs | 9 +- crates/bevy_text/src/lib.rs | 2 +- crates/bevy_transform/src/lib.rs | 2 +- crates/bevy_ui/src/lib.rs | 4 +- .../wgpu_resource_diagnostics_plugin.rs | 2 +- crates/bevy_wgpu/src/lib.rs | 4 +- crates/bevy_window/src/lib.rs | 11 +- crates/bevy_winit/src/lib.rs | 4 +- examples/2d/contributors.rs | 2 +- examples/2d/many_sprites.rs | 2 +- examples/2d/mesh.rs | 2 +- examples/2d/sprite.rs | 2 +- examples/2d/sprite_flipping.rs | 2 +- examples/2d/sprite_sheet.rs | 2 +- examples/2d/text2d.rs | 2 +- examples/2d/texture_atlas.rs | 2 +- examples/3d/3d_scene.rs | 2 +- examples/3d/load_gltf.rs | 2 +- examples/3d/msaa.rs | 2 +- examples/3d/orthographic.rs | 2 +- examples/3d/parenting.rs | 2 +- examples/3d/pbr.rs | 2 +- examples/3d/render_to_texture.rs | 2 +- examples/3d/spawner.rs | 2 +- examples/3d/texture.rs | 2 +- examples/3d/update_gltf_scene.rs | 2 +- examples/3d/wireframe.rs | 2 +- examples/3d/z_sort_debug.rs | 2 +- examples/android/android.rs | 2 +- examples/app/custom_loop.rs | 2 +- examples/app/drag_and_drop.rs | 2 +- examples/app/empty.rs | 2 +- examples/app/empty_defaults.rs | 2 +- examples/app/headless.rs | 4 +- examples/app/logs.rs | 2 +- examples/app/plugin.rs | 4 +- examples/app/plugin_group.rs | 6 +- examples/app/return_after_run.rs | 4 +- examples/app/thread_pool_resources.rs | 2 +- examples/asset/asset_loading.rs | 2 +- examples/asset/custom_asset.rs | 2 +- examples/asset/custom_asset_io.rs | 6 +- examples/asset/hot_asset_reloading.rs | 2 +- examples/async_tasks/async_compute.rs | 2 +- examples/audio/audio.rs | 2 +- examples/diagnostics/custom_diagnostic.rs | 2 +- examples/diagnostics/log_diagnostics.rs | 2 +- examples/ecs/component_change_detection.rs | 2 +- examples/ecs/ecs_guide.rs | 2 +- examples/ecs/event.rs | 2 +- examples/ecs/fixed_timestep.rs | 2 +- examples/ecs/hierarchy.rs | 2 +- examples/ecs/iter_combinations.rs | 2 +- examples/ecs/parallel_query.rs | 2 +- examples/ecs/query_bundle.rs | 2 +- examples/ecs/removal_detection.rs | 2 +- examples/ecs/startup_system.rs | 2 +- examples/ecs/state.rs | 2 +- examples/ecs/system_chaining.rs | 2 +- examples/ecs/system_param.rs | 2 +- examples/ecs/system_sets.rs | 2 +- examples/ecs/timers.rs | 2 +- examples/game/alien_cake_addict.rs | 2 +- examples/game/breakout.rs | 2 +- examples/hello_world.rs | 2 +- examples/input/char_input_events.rs | 2 +- examples/input/gamepad_input.rs | 2 +- examples/input/gamepad_input_events.rs | 2 +- examples/input/keyboard_input.rs | 2 +- examples/input/keyboard_input_events.rs | 2 +- examples/input/keyboard_modifiers.rs | 2 +- examples/input/mouse_input.rs | 2 +- examples/input/mouse_input_events.rs | 2 +- examples/input/touch_input.rs | 2 +- examples/input/touch_input_events.rs | 2 +- examples/ios/src/lib.rs | 2 +- examples/reflection/generic_reflection.rs | 2 +- examples/reflection/reflection.rs | 2 +- examples/reflection/reflection_types.rs | 2 +- examples/reflection/trait_reflection.rs | 2 +- examples/scene/scene.rs | 2 +- examples/shader/animate_shader.rs | 2 +- examples/shader/array_texture.rs | 2 +- examples/shader/hot_shader_reloading.rs | 2 +- examples/shader/mesh_custom_attribute.rs | 2 +- examples/shader/shader_custom_material.rs | 2 +- examples/shader/shader_defs.rs | 2 +- examples/tools/bevymark.rs | 2 +- examples/ui/button.rs | 2 +- examples/ui/font_atlas_debug.rs | 2 +- examples/ui/text.rs | 2 +- examples/ui/text_debug.rs | 2 +- examples/ui/ui.rs | 2 +- examples/wasm/assets_wasm.rs | 2 +- examples/wasm/headless_wasm.rs | 2 +- examples/wasm/hello_wasm.rs | 2 +- examples/wasm/winit_wasm.rs | 2 +- examples/window/clear_color.rs | 2 +- examples/window/multiple_windows.rs | 2 +- examples/window/scale_factor_override.rs | 2 +- examples/window/window_settings.rs | 2 +- src/lib.rs | 2 +- 130 files changed, 712 insertions(+), 764 deletions(-) delete mode 100644 crates/bevy_app/src/app_builder.rs diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index d30303a4ac9bd..cf64ea7b0a6c1 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,8 +1,15 @@ -use crate::app_builder::AppBuilder; +use crate::{CoreStage, Events, Plugin, PluginGroup, PluginGroupBuilder, StartupStage}; use bevy_ecs::{ - schedule::{Schedule, Stage}, + component::{Component, ComponentDescriptor}, + prelude::{FromWorld, IntoExclusiveSystem, IntoSystem}, + schedule::{ + IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage, + }, world::World, }; +use bevy_utils::tracing::debug; +use std::{fmt::Debug, hash::Hash}; + #[cfg(feature = "trace")] use bevy_utils::tracing::info_span; @@ -20,7 +27,7 @@ use bevy_utils::tracing::info_span; /// # use bevy_ecs::prelude::*; /// /// fn main() { -/// App::build() +/// App::new() /// .add_system(hello_world_system.system()) /// .run(); /// } @@ -37,21 +44,34 @@ pub struct App { impl Default for App { fn default() -> Self { - Self { - world: Default::default(), - schedule: Default::default(), - runner: Box::new(run_once), + let mut app = App::empty(); + #[cfg(feature = "bevy_reflect")] + app.init_resource::(); + + app.add_default_stages() + .add_event::() + .add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system()); + + #[cfg(feature = "bevy_ci_testing")] + { + crate::ci_testing::setup_app(&mut app); } - } -} -fn run_once(mut app: App) { - app.update(); + app + } } impl App { - pub fn build() -> AppBuilder { - AppBuilder::default() + pub fn new() -> App { + App::default() + } + + pub fn empty() -> App { + Self { + world: Default::default(), + schedule: Default::default(), + runner: Box::new(run_once), + } } pub fn update(&mut self) { @@ -62,15 +82,504 @@ impl App { self.schedule.run(&mut self.world); } - pub fn run(mut self) { + /// Start the application (through main runner) + /// + /// Runs the application main loop. + /// + /// Usually the main loop is handled by Bevy integrated plugins (`winit`), but + /// one can also set the runner function through [`App::set_runner`]. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # + /// App::new() + /// // all required plugin insertions, systems, etc inserted here + /// // finally, call: + /// .run(); + /// ``` + pub fn run(&mut self) { #[cfg(feature = "trace")] let bevy_app_run_span = info_span!("bevy_app"); #[cfg(feature = "trace")] let _bevy_app_run_guard = bevy_app_run_span.enter(); - let runner = std::mem::replace(&mut self.runner, Box::new(run_once)); - (runner)(self); + let mut app = std::mem::replace(self, App::empty()); + let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); + (runner)(app); + } + + pub fn add_stage(&mut self, label: impl StageLabel, stage: S) -> &mut Self { + self.schedule.add_stage(label, stage); + self + } + + pub fn add_stage_after( + &mut self, + target: impl StageLabel, + label: impl StageLabel, + stage: S, + ) -> &mut Self { + self.schedule.add_stage_after(target, label, stage); + self + } + + pub fn add_stage_before( + &mut self, + target: impl StageLabel, + label: impl StageLabel, + stage: S, + ) -> &mut Self { + self.schedule.add_stage_before(target, label, stage); + self + } + + pub fn add_startup_stage(&mut self, label: impl StageLabel, stage: S) -> &mut Self { + self.schedule + .stage(CoreStage::Startup, |schedule: &mut Schedule| { + schedule.add_stage(label, stage) + }); + self + } + + pub fn add_startup_stage_after( + &mut self, + target: impl StageLabel, + label: impl StageLabel, + stage: S, + ) -> &mut Self { + self.schedule + .stage(CoreStage::Startup, |schedule: &mut Schedule| { + schedule.add_stage_after(target, label, stage) + }); + self + } + + pub fn add_startup_stage_before( + &mut self, + target: impl StageLabel, + label: impl StageLabel, + stage: S, + ) -> &mut Self { + self.schedule + .stage(CoreStage::Startup, |schedule: &mut Schedule| { + schedule.add_stage_before(target, label, stage) + }); + self + } + + pub fn stage &mut T>( + &mut self, + label: impl StageLabel, + func: F, + ) -> &mut Self { + self.schedule.stage(label, func); + self + } + + /// Adds a system that runs every time `app.update()` is called by the runner + /// + /// Systems are the main building block in the Bevy ECS app model. You can define + /// normal rust functions, and call `.system()` to make them be Bevy systems. + /// + /// System functions can have parameters, through which one can query and + /// mutate Bevy ECS states. + /// See [The Bevy Book](https://bevyengine.org/learn/book/introduction/) for more information. + /// + /// Systems are run in parallel, and the execution order is not deterministic. + /// If you want more fine-grained control for order, see [`App::add_system_to_stage`]. + /// + /// For adding a system that runs only at app startup, see [`App::add_startup_system`]. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; + /// # + /// fn my_system(_commands: Commands) { + /// println!("My system, triggered once per frame"); + /// } + /// + /// App::new() + /// .add_system(my_system.system()); + /// ``` + pub fn add_system(&mut self, system: impl IntoSystemDescriptor) -> &mut Self { + self.add_system_to_stage(CoreStage::Update, system) + } + + pub fn add_system_set(&mut self, system_set: SystemSet) -> &mut Self { + self.add_system_set_to_stage(CoreStage::Update, system_set) + } + + pub fn add_system_to_stage( + &mut self, + stage_label: impl StageLabel, + system: impl IntoSystemDescriptor, + ) -> &mut Self { + self.schedule.add_system_to_stage(stage_label, system); + self + } + + pub fn add_system_set_to_stage( + &mut self, + stage_label: impl StageLabel, + system_set: SystemSet, + ) -> &mut Self { + self.schedule + .add_system_set_to_stage(stage_label, system_set); + self + } + + /// Adds a system that is run once at application startup + /// + /// Startup systems run exactly once BEFORE all other systems. These are generally used for + /// app initialization code (ex: adding entities and resources). + /// + /// * For adding a system that runs for every frame, see [`App::add_system`]. + /// * For adding a system to specific stage, see [`App::add_system_to_stage`]. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # use bevy_ecs::prelude::*; + /// # + /// fn my_startup_system(_commands: Commands) { + /// println!("My startup system"); + /// } + /// + /// App::new() + /// .add_startup_system(my_startup_system.system()); + /// ``` + pub fn add_startup_system( + &mut self, + system: impl IntoSystemDescriptor, + ) -> &mut Self { + self.add_startup_system_to_stage(StartupStage::Startup, system) + } + + pub fn add_startup_system_set(&mut self, system_set: SystemSet) -> &mut Self { + self.add_startup_system_set_to_stage(StartupStage::Startup, system_set) + } + + pub fn add_startup_system_to_stage( + &mut self, + stage_label: impl StageLabel, + system: impl IntoSystemDescriptor, + ) -> &mut Self { + self.schedule + .stage(CoreStage::Startup, |schedule: &mut Schedule| { + schedule.add_system_to_stage(stage_label, system) + }); + self + } + + pub fn add_startup_system_set_to_stage( + &mut self, + stage_label: impl StageLabel, + system_set: SystemSet, + ) -> &mut Self { + self.schedule + .stage(CoreStage::Startup, |schedule: &mut Schedule| { + schedule.add_system_set_to_stage(stage_label, system_set) + }); + self + } + + /// Adds a new [State] with the given `initial` value. + /// This inserts a new `State` resource and adds a new "driver" to [CoreStage::Update]. + /// Each stage that uses `State` for system run criteria needs a driver. If you need to use + /// your state in a different stage, consider using [Self::add_state_to_stage] or manually + /// adding [State::get_driver] to additional stages you need it in. + pub fn add_state(&mut self, initial: T) -> &mut Self + where + T: Component + Debug + Clone + Eq + Hash, + { + self.add_state_to_stage(CoreStage::Update, initial) + } + + /// Adds a new [State] with the given `initial` value. + /// This inserts a new `State` resource and adds a new "driver" to the given stage. + /// Each stage that uses `State` for system run criteria needs a driver. If you need to use + /// your state in more than one stage, consider manually adding [State::get_driver] to the + /// stages you need it in. + pub fn add_state_to_stage(&mut self, stage: impl StageLabel, initial: T) -> &mut Self + where + T: Component + Debug + Clone + Eq + Hash, + { + self.insert_resource(State::new(initial)) + .add_system_set_to_stage(stage, State::::get_driver()) + } + + pub fn add_default_stages(&mut self) -> &mut Self { + self.add_stage(CoreStage::First, SystemStage::parallel()) + .add_stage( + CoreStage::Startup, + Schedule::default() + .with_run_criteria(RunOnce::default()) + .with_stage(StartupStage::PreStartup, SystemStage::parallel()) + .with_stage(StartupStage::Startup, SystemStage::parallel()) + .with_stage(StartupStage::PostStartup, SystemStage::parallel()), + ) + .add_stage(CoreStage::PreUpdate, SystemStage::parallel()) + .add_stage(CoreStage::Update, SystemStage::parallel()) + .add_stage(CoreStage::PostUpdate, SystemStage::parallel()) + .add_stage(CoreStage::Last, SystemStage::parallel()) + } + + /// Setup the application to manage events of type `T`. + /// + /// This is done by adding a `Resource` of type `Events::`, + /// and inserting a `Events::::update_system` system into `CoreStage::First`. + pub fn add_event(&mut self) -> &mut Self + where + T: Component, + { + self.insert_resource(Events::::default()) + .add_system_to_stage(CoreStage::First, Events::::update_system.system()) + } + + /// Inserts a resource to the current [App] and overwrites any resource previously added of the same type. + /// + /// A resource in Bevy represents globally unique data. Resources must be added to Bevy Apps + /// before using them. This happens with [`App::insert_resource`]. + /// + /// See also `init_resource` for resources that implement `Default` or [`FromResources`]. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # + /// struct MyCounter { + /// counter: usize, + /// } + /// + /// App::new() + /// .insert_resource(MyCounter { counter: 0 }); + /// ``` + pub fn insert_resource(&mut self, resource: T) -> &mut Self + where + T: Component, + { + self.world.insert_resource(resource); + self + } + + /// Inserts a non-send resource to the app + /// + /// You usually want to use `insert_resource`, but there are some special cases when a resource must + /// be non-send. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # + /// struct MyCounter { + /// counter: usize, + /// } + /// + /// App::new() + /// .insert_non_send_resource(MyCounter { counter: 0 }); + /// ``` + pub fn insert_non_send_resource(&mut self, resource: T) -> &mut Self + where + T: 'static, + { + self.world.insert_non_send(resource); + self + } + + /// Initialize a resource in the current [App], if it does not exist yet + /// + /// Adds a resource that implements `Default` or [`FromResources`] trait. + /// If the resource already exists, `init_resource` does nothing. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # + /// struct MyCounter { + /// counter: usize, + /// } + /// + /// impl Default for MyCounter { + /// fn default() -> MyCounter { + /// MyCounter { + /// counter: 100 + /// } + /// } + /// } + /// + /// App::new() + /// .init_resource::(); + /// ``` + pub fn init_resource(&mut self) -> &mut Self + where + R: FromWorld + Send + Sync + 'static, + { + // PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed + // not to modify the map. However, we would need to be borrowing resources both + // mutably and immutably, so we would need to be extremely certain this is correct + if !self.world.contains_resource::() { + let resource = R::from_world(&mut self.world); + self.insert_resource(resource); + } + self + } + + pub fn init_non_send_resource(&mut self) -> &mut Self + where + R: FromWorld + 'static, + { + // See perf comment in init_resource + if self.world.get_non_send_resource::().is_none() { + let resource = R::from_world(&mut self.world); + self.world.insert_non_send(resource); + } + self + } + + /// Sets the main runner loop function for this Bevy App + /// + /// Usually the main loop is handled by Bevy integrated plugins ([`WinitPlugin`]), but + /// in some cases one might wish to implement their own main loop. + /// + /// This method sets the main loop function, overwriting a previous runner if any. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # + /// fn my_runner(mut app: App) { + /// loop { + /// println!("In main loop"); + /// app.update(); + /// } + /// } + /// + /// App::new() + /// .set_runner(my_runner); + /// ``` + pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static) -> &mut Self { + self.runner = Box::new(run_fn); + self } + + /// Adds a single plugin + /// + /// One of Bevy's core principles is modularity. All Bevy engine features are implemented + /// as plugins. This includes internal features like the renderer. + /// + /// Bevy also provides a few sets of default plugins. See [`App::add_plugins`]. + /// + /// ## Example + /// ``` + /// # use bevy_app::prelude::*; + /// # + /// App::new().add_plugin(bevy_log::LogPlugin::default()); + /// ``` + pub fn add_plugin(&mut self, plugin: T) -> &mut Self + where + T: Plugin, + { + debug!("added plugin: {}", plugin.name()); + plugin.build(self); + self + } + + /// Adds a group of plugins + /// + /// Bevy plugins can be grouped into a set of plugins. Bevy provides + /// built-in PluginGroups that provide core engine functionality. + /// + /// The plugin groups available by default are [`DefaultPlugins`] and [`MinimalPlugins`]. + /// + /// ## Example + /// ``` + /// # use bevy_app::{prelude::*, PluginGroupBuilder}; + /// # + /// # // Dummy created to avoid using bevy_internal, which pulls in to many dependencies. + /// # struct MinimalPlugins; + /// # impl PluginGroup for MinimalPlugins { + /// # fn build(&mut self, group: &mut PluginGroupBuilder){;} + /// # } + /// # + /// App::new() + /// .add_plugins(MinimalPlugins); + /// ``` + pub fn add_plugins(&mut self, mut group: T) -> &mut Self { + let mut plugin_group_builder = PluginGroupBuilder::default(); + group.build(&mut plugin_group_builder); + plugin_group_builder.finish(self); + self + } + + /// Adds a group of plugins with an initializer method + /// + /// Can be used to add a group of plugins, where the group is modified + /// before insertion into Bevy application. For example, you can add + /// extra plugins at a specific place in the plugin group, or deactivate + /// specific plugins while keeping the rest. + /// + /// ## Example + /// ``` + /// # use bevy_app::{prelude::*, PluginGroupBuilder}; + /// # + /// # // Dummies created to avoid using bevy_internal which pulls in to many dependencies. + /// # struct DefaultPlugins; + /// # impl PluginGroup for DefaultPlugins { + /// # fn build(&mut self, group: &mut PluginGroupBuilder){ + /// # group.add(bevy_log::LogPlugin::default()); + /// # } + /// # } + /// # + /// # struct MyOwnPlugin; + /// # impl Plugin for MyOwnPlugin { + /// # fn build(&self, app: &mut App){;} + /// # } + /// # + /// App::new() + /// .add_plugins_with(DefaultPlugins, |group| { + /// group.add_before::(MyOwnPlugin) + /// }); + /// ``` + pub fn add_plugins_with(&mut self, mut group: T, func: F) -> &mut Self + where + T: PluginGroup, + F: FnOnce(&mut PluginGroupBuilder) -> &mut PluginGroupBuilder, + { + let mut plugin_group_builder = PluginGroupBuilder::default(); + group.build(&mut plugin_group_builder); + func(&mut plugin_group_builder); + plugin_group_builder.finish(self); + self + } + + /// Registers a new component using the given [ComponentDescriptor]. Components do not need to + /// be manually registered. This just provides a way to override default configuration. + /// Attempting to register a component with a type that has already been used by [World] + /// will result in an error. + /// + /// See [World::register_component] + pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self { + self.world.register_component(descriptor).unwrap(); + self + } + + #[cfg(feature = "bevy_reflect")] + pub fn register_type(&mut self) -> &mut Self { + { + let registry = self + .world + .get_resource_mut::() + .unwrap(); + registry.write().register::(); + } + self + } +} + +fn run_once(mut app: App) { + app.update(); } /// An event that indicates the app should exit. This will fully exit the app process. diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs deleted file mode 100644 index 7c454cd7045bc..0000000000000 --- a/crates/bevy_app/src/app_builder.rs +++ /dev/null @@ -1,559 +0,0 @@ -use crate::{ - app::{App, AppExit}, - plugin::Plugin, - CoreStage, PluginGroup, PluginGroupBuilder, StartupStage, -}; -use bevy_ecs::{ - component::{Component, ComponentDescriptor}, - event::Events, - schedule::{ - IntoSystemDescriptor, RunOnce, Schedule, Stage, StageLabel, State, SystemSet, SystemStage, - }, - system::{IntoExclusiveSystem, IntoSystem}, - world::{FromWorld, World}, -}; -use bevy_utils::tracing::debug; -use std::{fmt::Debug, hash::Hash}; - -/// Configure [App]s using the builder pattern -pub struct AppBuilder { - pub app: App, -} - -impl Default for AppBuilder { - fn default() -> Self { - let mut app_builder = AppBuilder { - app: App::default(), - }; - - #[cfg(feature = "bevy_reflect")] - app_builder.init_resource::(); - - app_builder - .add_default_stages() - .add_event::() - .add_system_to_stage(CoreStage::Last, World::clear_trackers.exclusive_system()); - - #[cfg(feature = "bevy_ci_testing")] - { - crate::ci_testing::setup_app(&mut app_builder); - } - app_builder - } -} - -impl AppBuilder { - pub fn empty() -> AppBuilder { - AppBuilder { - app: App::default(), - } - } - - /// Start the application (through main runner) - /// - /// Runs the application main loop. - /// - /// Usually the main loop is handled by Bevy integrated plugins (`winit`), but - /// but one can also set the runner function through [`AppBuilder::set_runner`]. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # - /// App::build() - /// // all required plugin insertions, systems, etc inserted here - /// // finally, call: - /// .run(); - /// ``` - pub fn run(&mut self) { - let app = std::mem::take(&mut self.app); - app.run(); - } - - pub fn world(&mut self) -> &World { - &self.app.world - } - - pub fn world_mut(&mut self) -> &mut World { - &mut self.app.world - } - - pub fn set_world(&mut self, world: World) -> &mut Self { - self.app.world = world; - self - } - - pub fn add_stage(&mut self, label: impl StageLabel, stage: S) -> &mut Self { - self.app.schedule.add_stage(label, stage); - self - } - - pub fn add_stage_after( - &mut self, - target: impl StageLabel, - label: impl StageLabel, - stage: S, - ) -> &mut Self { - self.app.schedule.add_stage_after(target, label, stage); - self - } - - pub fn add_stage_before( - &mut self, - target: impl StageLabel, - label: impl StageLabel, - stage: S, - ) -> &mut Self { - self.app.schedule.add_stage_before(target, label, stage); - self - } - - pub fn add_startup_stage(&mut self, label: impl StageLabel, stage: S) -> &mut Self { - self.app - .schedule - .stage(CoreStage::Startup, |schedule: &mut Schedule| { - schedule.add_stage(label, stage) - }); - self - } - - pub fn add_startup_stage_after( - &mut self, - target: impl StageLabel, - label: impl StageLabel, - stage: S, - ) -> &mut Self { - self.app - .schedule - .stage(CoreStage::Startup, |schedule: &mut Schedule| { - schedule.add_stage_after(target, label, stage) - }); - self - } - - pub fn add_startup_stage_before( - &mut self, - target: impl StageLabel, - label: impl StageLabel, - stage: S, - ) -> &mut Self { - self.app - .schedule - .stage(CoreStage::Startup, |schedule: &mut Schedule| { - schedule.add_stage_before(target, label, stage) - }); - self - } - - pub fn stage &mut T>( - &mut self, - label: impl StageLabel, - func: F, - ) -> &mut Self { - self.app.schedule.stage(label, func); - self - } - - /// Adds a system that runs every time `app.update()` is called by the runner - /// - /// Systems are the main building block in the Bevy ECS app model. You can define - /// normal rust functions, and call `.system()` to make them be Bevy systems. - /// - /// System functions can have parameters, through which one can query and - /// mutate Bevy ECS states. - /// See [The Bevy Book](https://bevyengine.org/learn/book/introduction/) for more information. - /// - /// Systems are run in parallel, and the execution order is not deterministic. - /// If you want more fine-grained control for order, see [`AppBuilder::add_system_to_stage`]. - /// - /// For adding a system that runs only at app startup, see [`AppBuilder::add_startup_system`]. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # use bevy_ecs::prelude::*; - /// # - /// fn my_system(_commands: Commands) { - /// println!("My system, triggered once per frame"); - /// } - /// - /// App::build() - /// .add_system(my_system.system()); - /// ``` - pub fn add_system(&mut self, system: impl IntoSystemDescriptor) -> &mut Self { - self.add_system_to_stage(CoreStage::Update, system) - } - - pub fn add_system_set(&mut self, system_set: SystemSet) -> &mut Self { - self.add_system_set_to_stage(CoreStage::Update, system_set) - } - - pub fn add_system_to_stage( - &mut self, - stage_label: impl StageLabel, - system: impl IntoSystemDescriptor, - ) -> &mut Self { - self.app.schedule.add_system_to_stage(stage_label, system); - self - } - - pub fn add_system_set_to_stage( - &mut self, - stage_label: impl StageLabel, - system_set: SystemSet, - ) -> &mut Self { - self.app - .schedule - .add_system_set_to_stage(stage_label, system_set); - self - } - - /// Adds a system that is run once at application startup - /// - /// Startup systems run exactly once BEFORE all other systems. These are generally used for - /// app initialization code (ex: adding entities and resources). - /// - /// * For adding a system that runs for every frame, see [`AppBuilder::add_system`]. - /// * For adding a system to specific stage, see [`AppBuilder::add_system_to_stage`]. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # use bevy_ecs::prelude::*; - /// # - /// fn my_startup_system(_commands: Commands) { - /// println!("My startup system"); - /// } - /// - /// App::build() - /// .add_startup_system(my_startup_system.system()); - /// ``` - pub fn add_startup_system( - &mut self, - system: impl IntoSystemDescriptor, - ) -> &mut Self { - self.add_startup_system_to_stage(StartupStage::Startup, system) - } - - pub fn add_startup_system_set(&mut self, system_set: SystemSet) -> &mut Self { - self.add_startup_system_set_to_stage(StartupStage::Startup, system_set) - } - - pub fn add_startup_system_to_stage( - &mut self, - stage_label: impl StageLabel, - system: impl IntoSystemDescriptor, - ) -> &mut Self { - self.app - .schedule - .stage(CoreStage::Startup, |schedule: &mut Schedule| { - schedule.add_system_to_stage(stage_label, system) - }); - self - } - - pub fn add_startup_system_set_to_stage( - &mut self, - stage_label: impl StageLabel, - system_set: SystemSet, - ) -> &mut Self { - self.app - .schedule - .stage(CoreStage::Startup, |schedule: &mut Schedule| { - schedule.add_system_set_to_stage(stage_label, system_set) - }); - self - } - - /// Adds a new [State] with the given `initial` value. - /// This inserts a new `State` resource and adds a new "driver" to [CoreStage::Update]. - /// Each stage that uses `State` for system run criteria needs a driver. If you need to use - /// your state in a different stage, consider using [Self::add_state_to_stage] or manually - /// adding [State::get_driver] to additional stages you need it in. - pub fn add_state(&mut self, initial: T) -> &mut Self - where - T: Component + Debug + Clone + Eq + Hash, - { - self.add_state_to_stage(CoreStage::Update, initial) - } - - /// Adds a new [State] with the given `initial` value. - /// This inserts a new `State` resource and adds a new "driver" to the given stage. - /// Each stage that uses `State` for system run criteria needs a driver. If you need to use - /// your state in more than one stage, consider manually adding [State::get_driver] to the - /// stages you need it in. - pub fn add_state_to_stage(&mut self, stage: impl StageLabel, initial: T) -> &mut Self - where - T: Component + Debug + Clone + Eq + Hash, - { - self.insert_resource(State::new(initial)) - .add_system_set_to_stage(stage, State::::get_driver()) - } - - pub fn add_default_stages(&mut self) -> &mut Self { - self.add_stage(CoreStage::First, SystemStage::parallel()) - .add_stage( - CoreStage::Startup, - Schedule::default() - .with_run_criteria(RunOnce::default()) - .with_stage(StartupStage::PreStartup, SystemStage::parallel()) - .with_stage(StartupStage::Startup, SystemStage::parallel()) - .with_stage(StartupStage::PostStartup, SystemStage::parallel()), - ) - .add_stage(CoreStage::PreUpdate, SystemStage::parallel()) - .add_stage(CoreStage::Update, SystemStage::parallel()) - .add_stage(CoreStage::PostUpdate, SystemStage::parallel()) - .add_stage(CoreStage::Last, SystemStage::parallel()) - } - - /// Setup the application to manage events of type `T`. - /// - /// This is done by adding a `Resource` of type `Events::`, - /// and inserting a `Events::::update_system` system into `CoreStage::First`. - pub fn add_event(&mut self) -> &mut Self - where - T: Component, - { - self.insert_resource(Events::::default()) - .add_system_to_stage(CoreStage::First, Events::::update_system.system()) - } - - /// Inserts a resource to the current [App] and overwrites any resource previously added of the same type. - /// - /// A resource in Bevy represents globally unique data. Resources must be added to Bevy Apps - /// before using them. This happens with [`AppBuilder::insert_resource`]. - /// - /// See also `init_resource` for resources that implement `Default` or [`FromResources`]. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # - /// struct MyCounter { - /// counter: usize, - /// } - /// - /// App::build() - /// .insert_resource(MyCounter { counter: 0 }); - /// ``` - pub fn insert_resource(&mut self, resource: T) -> &mut Self - where - T: Component, - { - self.app.world.insert_resource(resource); - self - } - - /// Inserts a non-send resource to the app - /// - /// You usually want to use `insert_resource`, but there are some special cases when a resource must - /// be non-send. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # - /// struct MyCounter { - /// counter: usize, - /// } - /// - /// App::build() - /// .insert_non_send_resource(MyCounter { counter: 0 }); - /// ``` - pub fn insert_non_send_resource(&mut self, resource: T) -> &mut Self - where - T: 'static, - { - self.app.world.insert_non_send(resource); - self - } - - /// Initialize a resource in the current [App], if it does not exist yet - /// - /// Adds a resource that implements `Default` or [`FromResources`] trait. - /// If the resource already exists, `init_resource` does nothing. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # - /// struct MyCounter { - /// counter: usize, - /// } - /// - /// impl Default for MyCounter { - /// fn default() -> MyCounter { - /// MyCounter { - /// counter: 100 - /// } - /// } - /// } - /// - /// App::build() - /// .init_resource::(); - /// ``` - pub fn init_resource(&mut self) -> &mut Self - where - R: FromWorld + Send + Sync + 'static, - { - // PERF: We could avoid double hashing here, since the `from_resources` call is guaranteed - // not to modify the map. However, we would need to be borrowing resources both - // mutably and immutably, so we would need to be extremely certain this is correct - if !self.world_mut().contains_resource::() { - let resource = R::from_world(self.world_mut()); - self.insert_resource(resource); - } - self - } - - pub fn init_non_send_resource(&mut self) -> &mut Self - where - R: FromWorld + 'static, - { - // See perf comment in init_resource - if self.app.world.get_non_send_resource::().is_none() { - let resource = R::from_world(self.world_mut()); - self.app.world.insert_non_send(resource); - } - self - } - - /// Sets the main runner loop function for this Bevy App - /// - /// Usually the main loop is handled by Bevy integrated plugins ([`WinitPlugin`]), but - /// in some cases one might wish to implement their own main loop. - /// - /// This method sets the main loop function, overwriting a previous runner if any. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # - /// fn my_runner(mut app: App) { - /// loop { - /// println!("In main loop"); - /// app.update(); - /// } - /// } - /// - /// App::build() - /// .set_runner(my_runner); - /// ``` - pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static) -> &mut Self { - self.app.runner = Box::new(run_fn); - self - } - - /// Adds a single plugin - /// - /// One of Bevy's core principles is modularity. All Bevy engine features are implemented - /// as plugins. This includes internal features like the renderer. - /// - /// Bevy also provides a few sets of default plugins. See [`AppBuilder::add_plugins`]. - /// - /// ## Example - /// ``` - /// # use bevy_app::prelude::*; - /// # - /// App::build().add_plugin(bevy_log::LogPlugin::default()); - /// ``` - pub fn add_plugin(&mut self, plugin: T) -> &mut Self - where - T: Plugin, - { - debug!("added plugin: {}", plugin.name()); - plugin.build(self); - self - } - - /// Adds a group of plugins - /// - /// Bevy plugins can be grouped into a set of plugins. Bevy provides - /// built-in PluginGroups that provide core engine functionality. - /// - /// The plugin groups available by default are [`DefaultPlugins`] and [`MinimalPlugins`]. - /// - /// ## Example - /// ``` - /// # use bevy_app::{prelude::*, PluginGroupBuilder}; - /// # - /// # // Dummy created to avoid using bevy_internal, which pulls in to many dependencies. - /// # struct MinimalPlugins; - /// # impl PluginGroup for MinimalPlugins { - /// # fn build(&mut self, group: &mut PluginGroupBuilder){;} - /// # } - /// # - /// App::build() - /// .add_plugins(MinimalPlugins); - /// ``` - pub fn add_plugins(&mut self, mut group: T) -> &mut Self { - let mut plugin_group_builder = PluginGroupBuilder::default(); - group.build(&mut plugin_group_builder); - plugin_group_builder.finish(self); - self - } - - /// Adds a group of plugins with an initializer method - /// - /// Can be used to add a group of plugins, where the group is modified - /// before insertion into Bevy application. For example, you can add - /// extra plugins at a specific place in the plugin group, or deactivate - /// specific plugins while keeping the rest. - /// - /// ## Example - /// ``` - /// # use bevy_app::{prelude::*, PluginGroupBuilder}; - /// # - /// # // Dummies created to avoid using bevy_internal which pulls in to many dependencies. - /// # struct DefaultPlugins; - /// # impl PluginGroup for DefaultPlugins { - /// # fn build(&mut self, group: &mut PluginGroupBuilder){ - /// # group.add(bevy_log::LogPlugin::default()); - /// # } - /// # } - /// # - /// # struct MyOwnPlugin; - /// # impl Plugin for MyOwnPlugin { - /// # fn build(&self, app: &mut AppBuilder){;} - /// # } - /// # - /// App::build() - /// .add_plugins_with(DefaultPlugins, |group| { - /// group.add_before::(MyOwnPlugin) - /// }); - /// ``` - pub fn add_plugins_with(&mut self, mut group: T, func: F) -> &mut Self - where - T: PluginGroup, - F: FnOnce(&mut PluginGroupBuilder) -> &mut PluginGroupBuilder, - { - let mut plugin_group_builder = PluginGroupBuilder::default(); - group.build(&mut plugin_group_builder); - func(&mut plugin_group_builder); - plugin_group_builder.finish(self); - self - } - - /// Registers a new component using the given [ComponentDescriptor]. Components do not need to - /// be manually registered. This just provides a way to override default configuration. - /// Attempting to register a component with a type that has already been used by [World] - /// will result in an error. - /// - /// See [World::register_component] - pub fn register_component(&mut self, descriptor: ComponentDescriptor) -> &mut Self { - self.world_mut().register_component(descriptor).unwrap(); - self - } - - #[cfg(feature = "bevy_reflect")] - pub fn register_type(&mut self) -> &mut Self { - { - let registry = self - .world_mut() - .get_resource_mut::() - .unwrap(); - registry.write().register::(); - } - self - } -} diff --git a/crates/bevy_app/src/ci_testing.rs b/crates/bevy_app/src/ci_testing.rs index 21e22bec04e9a..4a41166449b12 100644 --- a/crates/bevy_app/src/ci_testing.rs +++ b/crates/bevy_app/src/ci_testing.rs @@ -1,6 +1,6 @@ use serde::Deserialize; -use crate::{app::AppExit, AppBuilder}; +use crate::{app::AppExit, App}; use bevy_ecs::system::IntoSystem; /// Configuration for automated testing on CI @@ -23,7 +23,7 @@ fn ci_testing_exit_after( *current_frame += 1; } -pub(crate) fn setup_app(app_builder: &mut AppBuilder) -> &mut AppBuilder { +pub(crate) fn setup_app(app_builder: &mut App) -> &mut App { let filename = std::env::var("CI_TESTING_CONFIG").unwrap_or_else(|_| "ci_testing_config.ron".to_string()); let config: CiTestingConfig = ron::from_str( diff --git a/crates/bevy_app/src/lib.rs b/crates/bevy_app/src/lib.rs index e5cd52fd6e7cf..7349ce86b1055 100644 --- a/crates/bevy_app/src/lib.rs +++ b/crates/bevy_app/src/lib.rs @@ -1,5 +1,4 @@ mod app; -mod app_builder; mod plugin; mod plugin_group; mod schedule_runner; @@ -8,7 +7,6 @@ mod schedule_runner; mod ci_testing; pub use app::*; -pub use app_builder::*; pub use bevy_derive::DynamicPlugin; pub use bevy_ecs::event::*; pub use plugin::*; @@ -17,10 +15,7 @@ pub use schedule_runner::*; pub mod prelude { #[doc(hidden)] - pub use crate::{ - app::App, app_builder::AppBuilder, CoreStage, DynamicPlugin, Plugin, PluginGroup, - StartupStage, - }; + pub use crate::{app::App, CoreStage, DynamicPlugin, Plugin, PluginGroup, StartupStage}; } use bevy_ecs::schedule::StageLabel; diff --git a/crates/bevy_app/src/plugin.rs b/crates/bevy_app/src/plugin.rs index ea5ef0021a4d9..38115af2ced49 100644 --- a/crates/bevy_app/src/plugin.rs +++ b/crates/bevy_app/src/plugin.rs @@ -1,12 +1,12 @@ -use crate::AppBuilder; +use crate::App; use std::any::Any; /// A collection of Bevy App logic and configuration /// -/// Plugins use [AppBuilder] to configure an [App](crate::App). When an [App](crate::App) registers +/// Plugins configure an [App](crate::App). When an [App](crate::App) registers /// a plugin, the plugin's [Plugin::build] function is run. pub trait Plugin: Any + Send + Sync { - fn build(&self, app: &mut AppBuilder); + fn build(&self, app: &mut App); fn name(&self) -> &str { std::any::type_name::() } diff --git a/crates/bevy_app/src/plugin_group.rs b/crates/bevy_app/src/plugin_group.rs index 074a2d3083cd2..6bb41483bba89 100644 --- a/crates/bevy_app/src/plugin_group.rs +++ b/crates/bevy_app/src/plugin_group.rs @@ -1,4 +1,4 @@ -use crate::{AppBuilder, Plugin}; +use crate::{App, Plugin}; use bevy_utils::{tracing::debug, HashMap}; use std::any::TypeId; @@ -96,7 +96,7 @@ impl PluginGroupBuilder { self } - pub fn finish(self, app: &mut AppBuilder) { + pub fn finish(self, app: &mut App) { for ty in self.order.iter() { if let Some(entry) = self.plugins.get(ty) { if entry.enabled { diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index 7b27cc43c8e32..9c80d237e6def 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -1,5 +1,8 @@ -use super::{App, AppBuilder}; -use crate::{app::AppExit, plugin::Plugin, ManualEventReader}; +use crate::{ + app::{App, AppExit}, + plugin::Plugin, + ManualEventReader, +}; use bevy_ecs::event::Events; use bevy_utils::{Duration, Instant}; @@ -48,9 +51,9 @@ impl ScheduleRunnerSettings { pub struct ScheduleRunnerPlugin {} impl Plugin for ScheduleRunnerPlugin { - fn build(&self, app: &mut AppBuilder) { + fn build(&self, app: &mut App) { let settings = app - .world_mut() + .world .get_resource_or_insert_with(ScheduleRunnerSettings::default) .to_owned(); app.set_runner(move |mut app: App| { diff --git a/crates/bevy_asset/src/assets.rs b/crates/bevy_asset/src/assets.rs index 299be3e3b9530..29a409f1316ac 100644 --- a/crates/bevy_asset/src/assets.rs +++ b/crates/bevy_asset/src/assets.rs @@ -2,7 +2,7 @@ use crate::{ update_asset_storage_system, Asset, AssetLoader, AssetServer, AssetStage, Handle, HandleId, RefChange, }; -use bevy_app::{AppBuilder, EventWriter, Events}; +use bevy_app::{App, EventWriter, Events}; use bevy_ecs::{ system::{IntoSystem, ResMut}, world::FromWorld, @@ -193,7 +193,7 @@ impl Assets { } } -/// [AppBuilder] extension methods for adding new asset types +/// [App] extension methods for adding new asset types pub trait AddAsset { fn add_asset(&mut self) -> &mut Self where @@ -206,13 +206,13 @@ pub trait AddAsset { T: AssetLoader; } -impl AddAsset for AppBuilder { +impl AddAsset for App { fn add_asset(&mut self) -> &mut Self where T: Asset, { let assets = { - let asset_server = self.world().get_resource::().unwrap(); + let asset_server = self.world.get_resource::().unwrap(); asset_server.register_asset_type::() }; @@ -233,7 +233,7 @@ impl AddAsset for AppBuilder { where T: AssetLoader + FromWorld, { - let result = T::from_world(self.world_mut()); + let result = T::from_world(&mut self.world); self.add_asset_loader(result) } @@ -241,7 +241,7 @@ impl AddAsset for AppBuilder { where T: AssetLoader, { - self.world_mut() + self.world .get_resource_mut::() .expect("AssetServer does not exist. Consider adding it as a resource.") .add_loader(loader); diff --git a/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs b/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs index 4dae5198d85b1..a915d8fe88ef6 100644 --- a/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs +++ b/crates/bevy_asset/src/diagnostic/asset_count_diagnostics_plugin.rs @@ -17,7 +17,7 @@ impl Default for AssetCountDiagnosticsPlugin { } impl Plugin for AssetCountDiagnosticsPlugin { - fn build(&self, app: &mut AppBuilder) { + fn build(&self, app: &mut App) { app.add_startup_system(Self::setup_system.system()) .add_system(Self::diagnostic_system.system()); } diff --git a/crates/bevy_asset/src/lib.rs b/crates/bevy_asset/src/lib.rs index 62808e38a42c2..12a3149872d4f 100644 --- a/crates/bevy_asset/src/lib.rs +++ b/crates/bevy_asset/src/lib.rs @@ -26,7 +26,7 @@ pub use io::*; pub use loader::*; pub use path::*; -use bevy_app::{prelude::Plugin, AppBuilder}; +use bevy_app::{prelude::Plugin, App}; use bevy_ecs::{ schedule::{StageLabel, SystemStage}, system::IntoSystem, @@ -61,9 +61,9 @@ impl Default for AssetServerSettings { /// /// This is useful when providing a custom `AssetIo` instance that needs to /// delegate to the default `AssetIo` for the platform. -pub fn create_platform_default_asset_io(app: &mut AppBuilder) -> Box { +pub fn create_platform_default_asset_io(app: &mut App) -> Box { let settings = app - .world_mut() + .world .get_resource_or_insert_with(AssetServerSettings::default); #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))] @@ -77,10 +77,10 @@ pub fn create_platform_default_asset_io(app: &mut AppBuilder) -> Box().is_none() { + fn build(&self, app: &mut App) { + if app.world.get_resource::().is_none() { let task_pool = app - .world() + .world .get_resource::() .expect("`IoTaskPool` resource not found.") .0 diff --git a/crates/bevy_audio/src/lib.rs b/crates/bevy_audio/src/lib.rs index eb9f148460ed2..5f44d68f9f40b 100644 --- a/crates/bevy_audio/src/lib.rs +++ b/crates/bevy_audio/src/lib.rs @@ -20,7 +20,7 @@ use bevy_ecs::system::IntoExclusiveSystem; pub struct AudioPlugin; impl Plugin for AudioPlugin { - fn build(&self, app: &mut AppBuilder) { + fn build(&self, app: &mut App) { app.init_non_send_resource::>() .add_asset::() .init_resource::>() diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 75fd87adb5ebb..50ab218cd61f1 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -38,13 +38,13 @@ pub enum CoreSystem { } impl Plugin for CorePlugin { - fn build(&self, app: &mut AppBuilder) { + fn build(&self, app: &mut App) { // Setup the default bevy task pools - app.world_mut() + app.world .get_resource::() .cloned() .unwrap_or_else(DefaultTaskPoolOptions::default) - .create_default_pools(app.world_mut()); + .create_default_pools(&mut app.world); app.init_resource::