mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
Add a brigadier example for use inside a bevy App (#146)
This commit is contained in:
parent
afcf497989
commit
8138d388e7
3 changed files with 199 additions and 1 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -274,6 +274,8 @@ version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"azalea-buf",
|
"azalea-buf",
|
||||||
"azalea-chat",
|
"azalea-chat",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,14 @@ version = "0.10.0"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bevy_app = "0.13.0"
|
||||||
|
bevy_ecs = "0.13.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
azalea-buf = { path = "../azalea-buf", version = "0.10.0", optional = true }
|
azalea-buf = { path = "../azalea-buf", version = "0.10.0", optional = true }
|
||||||
azalea-chat = { path = "../azalea-chat", version = "0.10.0", optional = true }
|
azalea-chat = { path = "../azalea-chat", version = "0.10.0", optional = true }
|
||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
azalea-buf = ["dep:azalea-buf", "dep:azalea-chat"]
|
azalea-buf = ["dep:azalea-buf", "dep:azalea-chat", "azalea-chat/azalea-buf"]
|
||||||
|
|
192
azalea-brigadier/tests/bevy_app_usage.rs
Normal file
192
azalea-brigadier/tests/bevy_app_usage.rs
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use azalea_brigadier::{
|
||||||
|
arguments::integer_argument_type::integer,
|
||||||
|
builder::{literal_argument_builder::literal, required_argument_builder::argument},
|
||||||
|
command_dispatcher::CommandDispatcher,
|
||||||
|
context::CommandContext,
|
||||||
|
};
|
||||||
|
use bevy_app::App;
|
||||||
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
|
query::With,
|
||||||
|
system::{Query, Resource, RunSystemOnce},
|
||||||
|
world::{FromWorld, World},
|
||||||
|
};
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bevy_app() {
|
||||||
|
let mut app = App::new();
|
||||||
|
|
||||||
|
// Initialize the dispatcher using FromWorld
|
||||||
|
app.init_resource::<DispatchStorage>();
|
||||||
|
|
||||||
|
// Process commands from bevy
|
||||||
|
app.world
|
||||||
|
.run_system_once(DispatchStorage::bevy_process_commands);
|
||||||
|
|
||||||
|
// Verify spawned entities exist after processing commands
|
||||||
|
app.world
|
||||||
|
.run_system_once(DispatchStorage::verify_spawned_entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct DispatchStorage {
|
||||||
|
/// The [`CommandDispatcher`].
|
||||||
|
///
|
||||||
|
/// Processes incoming commands.
|
||||||
|
dispatch: CommandDispatcher<WorldAccessor>,
|
||||||
|
/// The world accessor.
|
||||||
|
///
|
||||||
|
/// Allows the dispatcher to query the [`World`].
|
||||||
|
world: WorldAccessor,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement [`FromWorld`] to initialize the dispatcher.
|
||||||
|
///
|
||||||
|
/// Allows the dispatcher to query the [`World`]
|
||||||
|
/// for generating commands on startup.
|
||||||
|
impl FromWorld for DispatchStorage {
|
||||||
|
fn from_world(_: &mut World) -> Self {
|
||||||
|
let mut dispatch = CommandDispatcher::new();
|
||||||
|
|
||||||
|
// Register dispatcher commands
|
||||||
|
{
|
||||||
|
// Register the "spawn_entity" command
|
||||||
|
dispatch
|
||||||
|
.register(literal("spawn_entity").executes(DispatchStorage::command_spawn_entity));
|
||||||
|
|
||||||
|
// Register the "spawn_entity_num" command
|
||||||
|
dispatch.register(literal("spawn_entity_num").then(
|
||||||
|
argument("entities", integer()).executes(DispatchStorage::command_spawn_entity_num),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
dispatch,
|
||||||
|
world: WorldAccessor::empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DispatchStorage {
|
||||||
|
/// A bevy system called to process commands.
|
||||||
|
fn bevy_process_commands(world: &mut World) {
|
||||||
|
world.resource_scope::<Self, _>(|bevy_world, mut storage| {
|
||||||
|
// NOTE: Initial swap to own bevy's `World`
|
||||||
|
//
|
||||||
|
// This is important, otherwise the dispatcher
|
||||||
|
// will only be able to access it's own empty `World`.
|
||||||
|
storage.world.swap(bevy_world);
|
||||||
|
|
||||||
|
let source = storage.world.clone();
|
||||||
|
|
||||||
|
// Test "spawn_entity"
|
||||||
|
{
|
||||||
|
println!("Testing 'spawn_entity' command");
|
||||||
|
let result = storage.dispatch.execute("spawn_entity", source.clone());
|
||||||
|
|
||||||
|
// Ensure the command was successful
|
||||||
|
assert_eq!(result, Ok(0));
|
||||||
|
|
||||||
|
// Query the World for the spawned entity
|
||||||
|
let mut world = source.lock();
|
||||||
|
let mut query = world.query_filtered::<(), With<SpawnedEntity>>();
|
||||||
|
|
||||||
|
// Ensure only one entity was spawned
|
||||||
|
let count = query.iter(&world).count();
|
||||||
|
println!("Spawned entities: {count}");
|
||||||
|
assert_eq!(count, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test "spawn_entity_num"
|
||||||
|
{
|
||||||
|
println!("Testing 'spawn_entity_num' command");
|
||||||
|
let result = storage
|
||||||
|
.dispatch
|
||||||
|
.execute("spawn_entity_num 3", source.clone());
|
||||||
|
|
||||||
|
// Ensure the command was successful
|
||||||
|
assert_eq!(result, Ok(0));
|
||||||
|
|
||||||
|
// Query the World for spawned entities
|
||||||
|
let mut world = source.lock();
|
||||||
|
let mut query = world.query_filtered::<(), With<SpawnedEntity>>();
|
||||||
|
|
||||||
|
// Ensure three additional entities were spawned
|
||||||
|
let count = query.iter(&world).count();
|
||||||
|
println!("Spawned entities: {count}");
|
||||||
|
assert_eq!(count, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Second swap to give bevy's `World` back
|
||||||
|
//
|
||||||
|
// It's even more important to give the `World` back
|
||||||
|
// after commands are executed, otherwise your app
|
||||||
|
// will be stuck with an empty `World`.
|
||||||
|
storage.world.swap(bevy_world);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A command called from the dispatcher.
|
||||||
|
///
|
||||||
|
/// Spawns an entity with the [`SpawnedEntity`] component.
|
||||||
|
fn command_spawn_entity(context: &CommandContext<WorldAccessor>) -> i32 {
|
||||||
|
context.source.lock().spawn(SpawnedEntity);
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A command called from the dispatcher.
|
||||||
|
///
|
||||||
|
/// Spawns a number of entities with the [`SpawnedEntity`] component.
|
||||||
|
fn command_spawn_entity_num(context: &CommandContext<WorldAccessor>) -> i32 {
|
||||||
|
let num = context.argument("entities").unwrap();
|
||||||
|
let num = *num.downcast_ref::<i32>().unwrap();
|
||||||
|
|
||||||
|
for _ in 0..num {
|
||||||
|
context.source.lock().spawn(SpawnedEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A bevy system called to verify four total entities was spawned.
|
||||||
|
fn verify_spawned_entities(query: Query<(), With<SpawnedEntity>>) {
|
||||||
|
assert_eq!(query.iter().count(), 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A wrapper around a [`World`] that allows for
|
||||||
|
/// access from inside a [`CommandDispatcher`].
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct WorldAccessor {
|
||||||
|
world: Arc<Mutex<World>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WorldAccessor {
|
||||||
|
/// Create a new empty [`WorldAccessor`].
|
||||||
|
fn empty() -> Self {
|
||||||
|
Self {
|
||||||
|
world: Arc::new(Mutex::new(World::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Swap the internal [`World`] with the given one.
|
||||||
|
fn swap(&mut self, world: &mut World) {
|
||||||
|
std::mem::swap(&mut *self.lock(), world);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A marker [`Component`] used to test spawning entities from the dispatcher.
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Component)]
|
||||||
|
struct SpawnedEntity;
|
||||||
|
|
||||||
|
/// Implemented for convenience.
|
||||||
|
impl std::ops::Deref for WorldAccessor {
|
||||||
|
type Target = Arc<Mutex<World>>;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.world
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue