1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use crate::operation::{
	AsyncOperation, BoxedCommand, OperationReceiver, OperationSender, ResourceOperation,
};
use crate::{AsyncEntity, AsyncIOSystem, AsyncResource, AsyncSystem};
use bevy_core::Name;
use bevy_ecs::prelude::*;
use bevy_ecs::system::Command;
use bevy_reflect::prelude::*;
use std::any::TypeId;
use std::borrow::Cow;

/// Exposes asynchronous access to the Bevy ECS `World`.
///
/// # Commands
/// Apply any `Command` asynchronously with `AsyncWorld::apply_command`.
///
/// # Systems
/// Just like their synchronous variants, asynchronous `System`s must be registered
/// before they can be used. Systems can optionally accept and return values asynchronously
/// if they are registered with `AsyncWorld::register_io_system`.
///
/// # Entities
/// Spawn entities with the `AsyncWorld::spawn_*` family.
///
/// # Resources
/// Insert, remove, and wait for resources to exist.
#[derive(Clone, Debug)]
pub struct AsyncWorld(OperationSender);

impl AsyncWorld {
	/// Applies the given `Command` to the world.
	pub async fn apply_command<C: Command>(&self, command: C) {
		self.0.send(BoxedCommand::new(command)).await;
	}

	/// Applies the given `Operation` to the world.
	pub async fn apply_operation(&self, operation: AsyncOperation) {
		self.0.send(operation).await;
	}

	/// Returns a copy of the underlying `OperationSender`.
	pub fn sender(&self) -> OperationSender {
		self.0.clone()
	}

	/// Registers a `System` and returns an `AsyncSystem` that can be used to run the system on demand.
	pub async fn register_system<M>(&self, system: impl IntoSystem<(), (), M>) -> AsyncSystem {
		let system = Box::new(IntoSystem::into_system(system));
		AsyncSystem::new(system, self.0.clone()).await
	}

	/// Registers a `System` and returns an `AsyncIOSystem` that can be used to run the system on demand
	/// while supplying an input value and receiving an output value.
	pub async fn register_io_system<I: Send + 'static, O: Send + 'static, M>(
		&self,
		system: impl IntoSystem<I, O, M>,
	) -> AsyncIOSystem<I, O> {
		AsyncIOSystem::new(system, self.0.clone()).await
	}

	/// Constructs an `AsyncEntity` for the given `Entity`. If the entity does not exist, any operation
	/// performed on it will panic.
	pub fn entity(&self, id: Entity) -> AsyncEntity {
		AsyncEntity::new(id, self.0.clone())
	}

	/// Spawns a new `Entity` and returns an `AsyncEntity` that represents it, which can be used
	/// to further manipulate the entity.
	pub async fn spawn_empty(&self) -> AsyncEntity {
		AsyncEntity::new_empty(self.0.clone()).await
	}

	/// Spawns a new `Entity` and returns an `AsyncEntity` that represents it, which can be used
	/// to further manipulate the entity. This function attaches a bevy `Name` component with the given
	/// value.
	pub async fn spawn_named(&self, name: impl Into<Cow<'static, str>>) -> AsyncEntity {
		AsyncEntity::new_named(name.into(), self.0.clone()).await
	}

	/// Spawns a new `Entity` with the given `Bundle` and returns an `AsyncEntity` that represents it,
	/// which can be used to further manipulate the entity.
	pub async fn spawn<B: Bundle + Reflect>(&self, bundle: B) -> AsyncEntity {
		AsyncEntity::new_bundle(Box::new(bundle), self.0.clone()).await
	}

	/// Inserts a new resource or updates an existing resource with the given value.
	pub async fn insert_resource<R: Resource + Reflect>(&self, resource: R) {
		let operation = ResourceOperation::Insert(Box::new(resource));
		self.0.send(operation).await;
	}

	/// Removes the resource of a given type, if it exists.
	pub async fn remove_resource<R: Resource + Reflect>(&self) {
		let operation = ResourceOperation::Remove(TypeId::of::<R>());
		self.0.send(operation).await;
	}

	/// Start waiting for the `Resource` of a given type. Returns an `AsyncResource` which can be further
	/// waited to receive the value of the resource.
	///
	/// `AsyncWorld::wait_for_resource().await` is equivalent to
	/// `AsyncWorld::start_waiting_for_resource().await.wait().await`.
	pub async fn start_waiting_for_resource<R: Resource + FromReflect>(&self) -> AsyncResource<R> {
		let (sender, receiver) = async_channel::bounded(1);
		let operation = ResourceOperation::WaitFor(TypeId::of::<R>(), sender);
		self.0.send(operation).await;
		AsyncResource::new(receiver)
	}

	/// Wait for the `Resource` of a given type. Returns the value of the resource, once it exists.
	///
	/// `AsyncWorld::wait_for_resource().await` is equivalent to
	/// `AsyncWorld::start_waiting_for_resource().await.wait().await`.
	pub async fn wait_for_resource<R: Resource + FromReflect>(&self) -> R {
		self.start_waiting_for_resource().await.wait().await
	}
}

impl From<OperationSender> for AsyncWorld {
	fn from(sender: OperationSender) -> Self {
		Self(sender)
	}
}

impl FromWorld for AsyncWorld {
	fn from_world(world: &mut World) -> Self {
		let (sender, receiver) = async_channel::unbounded();
		world.spawn((
			OperationReceiver::from(receiver),
			Name::new("OperationReceiver"),
		));
		OperationSender::from(sender).into()
	}
}