From c1779b5ba47f283672e49f4dbece8ffaa76c8723 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Tue, 7 Mar 2023 17:13:22 -0500 Subject: [PATCH] Upgrade to Bevy 0.10 and release v 0.4 (#30) * Bump version numbers * Simple migrations * Repair gamepad.rs example * Faster CI * Add release notes stub * App:update no longer sends AppExit --- .github/workflows/ci.yml | 144 ++++++++++++++----------------------- Cargo.toml | 6 +- RELEASES.md | 6 ++ examples/gamepad.rs | 20 ++++-- examples/input_playback.rs | 6 +- src/input_capture.rs | 20 +++--- src/input_playback.rs | 35 ++++----- src/timestamped_input.rs | 8 +-- tests/input_capture.rs | 33 +++------ 9 files changed, 120 insertions(+), 158 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ab333e..b90cae1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,121 +1,85 @@ +# modified from: https://github.com/bevyengine/bevy/blob/main/.github/workflows/ci.yml name: CI on: pull_request: + branches: [main] + push: + branches: [main] env: CARGO_TERM_COLOR: always jobs: - build: - strategy: - matrix: - toolchain: [stable] - os: [windows-latest] - runs-on: ${{ matrix.os }} + check-lints: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-build-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.toml') }} + toolchain: stable + components: rustfmt, clippy + override: true + - name: Cache Cargo build files + uses: Leafwing-Studios/cargo-cache@v1.0.0 + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + - name: CI job + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- lints + + check-tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: - toolchain: ${{ matrix.toolchain }} + toolchain: stable override: true + - name: Cache Cargo build files + uses: Leafwing-Studios/cargo-cache@v1.0.0 - 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: Build & run tests - run: cargo test --workspace + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- test env: - CARGO_INCREMENTAL: 0 RUSTFLAGS: "-C debuginfo=0 -D warnings" - ci: - runs-on: windows-latest + check-compiles: + 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/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: stable - components: rustfmt, clippy override: true - - name: CI job + - name: Cache Cargo build files + uses: Leafwing-Studios/cargo-cache@v1.0.0 + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev + - name: Check Compile # See tools/ci/src/main.rs for the commands this runs - run: cargo run -p ci - - check-markdown-links: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - 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: cargo run -p ci -- compile - markdownlint: + check-doc: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 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 + toolchain: stable + - name: Cache Cargo build files + uses: Leafwing-Studios/cargo-cache@v1.0.0 + - name: Install alsa and udev + run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev + if: runner.os == 'linux' + - name: Build and check doc + # See tools/ci/src/main.rs for the commands this runs + run: cargo run -p ci -- doc env: - VALIDATE_ALL_CODEBASE: false - VALIDATE_MARKDOWN: true - DEFAULT_BRANCH: main + RUSTFLAGS: "-C debuginfo=0" + # - name: Installs cargo-deadlinks + # run: cargo install --force cargo-deadlinks + # - name: Checks dead links + # run: cargo deadlinks diff --git a/Cargo.toml b/Cargo.toml index c2bc840..8a021a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "leafwing_input_playback" description = "Input recording and mocking functionality for the Bevy game engine." -version = "0.3.0" +version = "0.4.0" authors = ["Leafwing Studios"] homepage = "https://leafwing-studios.com/" repository = "https://github.com/leafwing-studios/leafwing_input_playback" @@ -21,12 +21,12 @@ members = ["./", "tools/ci"] default = [] [dependencies] -bevy = {version ="0.9", default_features = false, features = ["serialize"]} +bevy = {version ="0.10", default_features = false, features = ["serialize"]} serde = {version = "1.0", features = ["derive"]} ron = "0.8" [dev-dependencies] -bevy = {version ="0.9", default_features = true, features = ["serialize"]} +bevy = {version ="0.10", default_features = true, features = ["serialize"]} [lib] name = "leafwing_input_playback" diff --git a/RELEASES.md b/RELEASES.md index 64c9eae..0a34b27 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,11 @@ # Release Notes +## Version 0.3 + +Migrated to `bevy 0.10`. + +Note that `App::update` no longer sends an `AppExit` event: this may affect your tests! + ## Version 0.1 ### Enhancements diff --git a/examples/gamepad.rs b/examples/gamepad.rs index 323da71..71ece22 100644 --- a/examples/gamepad.rs +++ b/examples/gamepad.rs @@ -102,7 +102,7 @@ mod gamepad_viewer_example { use std::f32::consts::PI; use bevy::{ - input::gamepad::{GamepadButton, GamepadSettings}, + input::gamepad::{GamepadButton, GamepadButtonChangedEvent, GamepadEvent, GamepadSettings}, prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; @@ -401,7 +401,7 @@ mod gamepad_viewer_example { style, }, ]) - .with_alignment(TextAlignment::BOTTOM_CENTER), + .with_alignment(TextAlignment::Center), ..default() }) .insert(TextWithAxes { x_axis, y_axis }); @@ -466,7 +466,7 @@ mod gamepad_viewer_example { color: TEXT_COLOR, }, ) - .with_alignment(TextAlignment::CENTER), + .with_alignment(TextAlignment::Center), ..default() }) .insert(TextWithButtonValue(button_type)); @@ -528,9 +528,14 @@ mod gamepad_viewer_example { mut query: Query<(&mut Text, &TextWithButtonValue)>, ) { for event in events.iter() { - if let GamepadEventType::ButtonChanged(button_type, value) = event.event_type { + if let GamepadEvent::Button(GamepadButtonChangedEvent { + gamepad: _, + button_type, + value, + }) = event + { for (mut text, text_with_button_value) in query.iter_mut() { - if button_type == **text_with_button_value { + if *button_type == **text_with_button_value { text.sections[0].value = format!("{:.3}", value); } } @@ -544,7 +549,10 @@ mod gamepad_viewer_example { mut text_query: Query<(&mut Text, &TextWithAxes)>, ) { for event in events.iter() { - if let GamepadEventType::AxisChanged(axis_type, value) = event.event_type { + if let GamepadEvent::Axis(axis_changed_event) = event { + let axis_type = axis_changed_event.axis_type; + let value = axis_changed_event.value; + for (mut transform, move_with) in query.iter_mut() { if axis_type == move_with.x_axis { transform.translation.x = value * move_with.scale; diff --git a/examples/input_playback.rs b/examples/input_playback.rs index dd35245..8195823 100644 --- a/examples/input_playback.rs +++ b/examples/input_playback.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, window::PrimaryWindow}; use leafwing_input_playback::{ input_capture::{InputCapturePlugin, InputModesCaptured}, @@ -56,14 +56,14 @@ struct Box; fn spawn_boxes( mut commands: Commands, - windows: Res, + windows: Query<&Window, With>, mouse_input: Res>, camera_query: Query<(&Transform, &Camera)>, ) { const BOX_SCALE: f32 = 50.0; if mouse_input.pressed(MouseButton::Left) { - let primary_window = windows.primary(); + let primary_window = windows.single(); // Don't break if we leave the window if let Some(cursor_pos) = cursor_pos_as_world_pos(primary_window, &camera_query) { commands diff --git a/src/input_capture.rs b/src/input_capture.rs index a006157..2dbc0f4 100644 --- a/src/input_capture.rs +++ b/src/input_capture.rs @@ -2,9 +2,9 @@ //! //! These are unified into a single [`TimestampedInputs`](crate::timestamped_input::TimestampedInputs) resource, which can be played back. -use bevy::app::{App, AppExit, CoreStage, Plugin}; +use bevy::app::{App, AppExit, CoreSet, Plugin}; use bevy::ecs::prelude::*; -use bevy::input::gamepad::GamepadEventRaw; +use bevy::input::gamepad::GamepadEvent; use bevy::input::keyboard::KeyboardInput; use bevy::input::mouse::{MouseButtonInput, MouseWheel}; use bevy::time::Time; @@ -30,20 +30,20 @@ impl Plugin for InputCapturePlugin { // Avoid double-adding frame_counter if !app.world.contains_resource::() { app.init_resource::() - .add_system_to_stage(CoreStage::First, frame_counter); + .add_system(frame_counter.in_base_set(CoreSet::First)); } app.init_resource::() .init_resource::() .init_resource::() - .add_system_to_stage( + .add_system( // Capture any mocked input as well - CoreStage::Last, - capture_input, + capture_input.in_base_set(CoreSet::Last), ) - .add_system_to_stage( - CoreStage::Last, - serialize_captured_input_on_exit.after(capture_input), + .add_system( + serialize_captured_input_on_exit + .in_base_set(CoreSet::Last) + .after(capture_input), ); } } @@ -100,7 +100,7 @@ pub fn capture_input( mut mouse_wheel_events: EventReader, mut cursor_moved_events: EventReader, mut keyboard_events: EventReader, - mut gamepad_events: EventReader, + mut gamepad_events: EventReader, mut app_exit_events: EventReader, mut timestamped_input: ResMut, input_modes_captured: Res, diff --git a/src/input_playback.rs b/src/input_playback.rs index 4de816b..e248e81 100644 --- a/src/input_playback.rs +++ b/src/input_playback.rs @@ -2,9 +2,9 @@ //! //! These are played back by emulating assorted Bevy input events. -use bevy::app::{App, AppExit, CoreStage, Plugin}; +use bevy::app::{App, AppExit, CoreSet, Plugin}; use bevy::ecs::{prelude::*, system::SystemParam}; -use bevy::input::gamepad::GamepadEventRaw; +use bevy::input::gamepad::GamepadEvent; use bevy::input::{ keyboard::KeyboardInput, mouse::{MouseButtonInput, MouseWheel}, @@ -12,7 +12,7 @@ use bevy::input::{ use bevy::log::warn; use bevy::time::Time; use bevy::utils::Duration; -use bevy::window::{CursorMoved, Windows}; +use bevy::window::{CursorMoved, Window}; use ron::de::from_reader; use std::fs::File; @@ -33,7 +33,7 @@ impl Plugin for InputPlaybackPlugin { // Avoid double-adding frame_counter if !app.world.contains_resource::() { app.init_resource::() - .add_system_to_stage(CoreStage::First, frame_counter); + .add_system(frame_counter.in_base_set(CoreSet::First)); } app.init_resource::() @@ -41,9 +41,10 @@ impl Plugin for InputPlaybackPlugin { .init_resource::() .init_resource::() .add_startup_system(deserialize_timestamped_inputs) - .add_system_to_stage( - CoreStage::First, - playback_timestamped_input.after(frame_counter), + .add_system( + playback_timestamped_input + .after(frame_counter) + .in_base_set(CoreSet::First), ); } } @@ -94,13 +95,13 @@ pub enum PlaybackStrategy { #[derive(SystemParam)] #[allow(missing_docs)] pub struct InputWriters<'w, 's> { - pub keyboard_input: EventWriter<'w, 's, KeyboardInput>, - pub mouse_button_input: EventWriter<'w, 's, MouseButtonInput>, - pub mouse_wheel: EventWriter<'w, 's, MouseWheel>, - pub cursor_moved: EventWriter<'w, 's, CursorMoved>, - pub windows: ResMut<'w, Windows>, - pub gamepad: EventWriter<'w, 's, GamepadEventRaw>, - pub app_exit: EventWriter<'w, 's, AppExit>, + pub keyboard_input: EventWriter<'w, KeyboardInput>, + pub mouse_button_input: EventWriter<'w, MouseButtonInput>, + pub mouse_wheel: EventWriter<'w, MouseWheel>, + pub cursor_moved: EventWriter<'w, CursorMoved>, + pub windows: Query<'w, 's, &'static mut Window>, + pub gamepad: EventWriter<'w, GamepadEvent>, + pub app_exit: EventWriter<'w, AppExit>, } // `TimestampedInputs` is an iterator, so we need mutable access to be able to track which events we've seen @@ -196,10 +197,10 @@ fn send_playback_events( // Window events MUST update the `Window` struct itself // BLOCKED: https://github.com/bevyengine/bevy/issues/6163 CursorMoved(e) => { - if let Some(window) = input_writers.windows.get_mut(e.id) { - window.set_cursor_position(e.position); + if let Ok(mut window) = input_writers.windows.get_mut(e.window) { + window.set_cursor_position(Some(e.position)); } else { - warn!("Window ID was not found when attempting to play back {e:?}") + warn!("Window entity was not found when attempting to play back {e:?}") } input_writers.cursor_moved.send(e) diff --git a/src/timestamped_input.rs b/src/timestamped_input.rs index c1883d5..2695051 100644 --- a/src/timestamped_input.rs +++ b/src/timestamped_input.rs @@ -4,7 +4,7 @@ use bevy::app::AppExit; use bevy::ecs::prelude::*; -use bevy::input::gamepad::GamepadEventRaw; +use bevy::input::gamepad::GamepadEvent; use bevy::input::keyboard::KeyboardInput; use bevy::input::mouse::{MouseButtonInput, MouseWheel}; use bevy::utils::Duration; @@ -361,7 +361,7 @@ pub enum InputEvent { MouseButton(MouseButtonInput), MouseWheel(MouseWheel), CursorMoved(CursorMoved), - Gamepad(GamepadEventRaw), + Gamepad(GamepadEvent), AppExit, } @@ -389,8 +389,8 @@ impl From for InputEvent { } } -impl From for InputEvent { - fn from(event: GamepadEventRaw) -> Self { +impl From for InputEvent { + fn from(event: GamepadEvent) -> Self { InputEvent::Gamepad(event) } } diff --git a/tests/input_capture.rs b/tests/input_capture.rs index afe97ba..8f78a14 100644 --- a/tests/input_capture.rs +++ b/tests/input_capture.rs @@ -39,17 +39,6 @@ fn capture_app() -> App { app } -#[test] -fn app_update_sends_app_exit() { - let mut app = capture_app(); - - app.update(); - let timestamped_input = app.world.resource::(); - assert_eq!(timestamped_input.len(), 1); - let timestamped_event = timestamped_input.events.first().unwrap(); - assert_eq!(timestamped_event.input_event, InputEvent::AppExit); -} - #[test] fn capture_sent_events() { let mut app = capture_app(); @@ -60,7 +49,7 @@ fn capture_sent_events() { app.update(); let timestamped_input = app.world.resource::(); - assert_eq!(timestamped_input.len(), 3); + assert_eq!(timestamped_input.len(), 2); } #[test] @@ -82,13 +71,11 @@ fn identity_of_sent_events() { let first_event: TimestampedInputEvent = iterator.next().unwrap(); let second_event: TimestampedInputEvent = iterator.next().unwrap(); - let third_event: TimestampedInputEvent = iterator.next().unwrap(); // Unfortunately these input types don't impl PartialEq :( assert!(matches!(first_event.input_event, InputEvent::Keyboard(_))); - assert!(matches!(second_event.input_event, InputEvent::AppExit)); assert!(matches!( - third_event.input_event, + second_event.input_event, InputEvent::MouseButton(_) )); } @@ -109,16 +96,13 @@ fn framecount_of_sent_events() { let mut timestamped_input = app.world.resource_mut::(); let mut iterator = timestamped_input.iter_all().into_iter(); - let first_event: TimestampedInputEvent = iterator.next().unwrap(); - let second_event: TimestampedInputEvent = iterator.next().unwrap(); - let third_event: TimestampedInputEvent = iterator.next().unwrap(); + let first_event: TimestampedInputEvent = iterator.next().expect("Keyboard event failed."); + let second_event: TimestampedInputEvent = iterator.next().expect("Mouse event failed."); // The frame count is recorded based on the frame it is read, // which counts up immediately assert_eq!(first_event.frame, FrameCount(1)); - // The app exit event - assert_eq!(second_event.frame, FrameCount(1)); - assert_eq!(third_event.frame, FrameCount(2)); + assert_eq!(second_event.frame, FrameCount(2)); } #[test] @@ -133,7 +117,7 @@ fn toggle_input_capture() { // Inputs are captured while input capturing is enabled by default let timestamped_input = app.world.resource::(); - assert_eq!(timestamped_input.len(), 3); + assert_eq!(timestamped_input.len(), 2); // Disabling input capture let mut input_modes_captured = app.world.resource_mut::(); @@ -146,9 +130,8 @@ fn toggle_input_capture() { app.update(); // Inputs are not captured while input capturing is disabled - // Note that each app.update() always sends an `AppExit` event let timestamped_input = app.world.resource::(); - assert_eq!(timestamped_input.len(), 4); + assert_eq!(timestamped_input.len(), 2); // Partially re-enabling input capture let mut input_modes_captured = app.world.resource_mut::(); @@ -168,5 +151,5 @@ fn toggle_input_capture() { // Only the keyboard events (and app exit events) were captured let timestamped_input = app.world.resource::(); - assert_eq!(timestamped_input.len(), 6); + assert_eq!(timestamped_input.len(), 3); }