Skip to content

Commit

Permalink
Support for wasm targets, closes jkb0o#4
Browse files Browse the repository at this point in the history
  • Loading branch information
jkb0o committed Mar 13, 2023
1 parent a024489 commit 23df401
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 16 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[target.wasm32-unknown-unknown]
runner = "wasm-server-runner"
95 changes: 84 additions & 11 deletions crates/pecs_http/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,72 @@
//! Make `http` requests asyncroniusly via [`ehttp`](https://docs.rs/ehttp/)
use bevy::prelude::*;
use bevy::tasks::{AsyncComputeTaskPool, Task};
use bevy::tasks::Task;
use bevy::utils::HashMap;
pub use ehttp::Response;
use futures_lite::future;
use pecs_core::{AsynOps, Promise, PromiseCommand, PromiseId, PromiseLikeBase, PromiseResult};

#[cfg(not(target_arch = "wasm32"))]
use bevy::tasks::AsyncComputeTaskPool;
#[cfg(target_arch = "wasm32")]
use bevy::utils::HashSet;
#[cfg(target_arch = "wasm32")]
use std::cell::Cell;
#[cfg(target_arch = "wasm32")]
use std::rc::Rc;
#[cfg(target_arch = "wasm32")]
use pecs_core::promise_resolve;

pub struct PromiseHttpPlugin;
impl Plugin for PromiseHttpPlugin {
fn build(&self, app: &mut App) {
#[cfg(not(target_arch = "wasm32"))]
app.init_resource::<Requests>();
#[cfg(not(target_arch = "wasm32"))]
app.add_system(process_requests);
#[cfg(target_arch = "wasm32")]
app.init_resource::<WasmRequests>();
}
}

#[cfg(target_arch = "wasm32")]
#[derive(Clone)]
pub struct WasmResolver {
id: PromiseId,
world: Rc<Cell<*mut World>>
}

#[cfg(target_arch = "wasm32")]
impl WasmResolver {
pub fn new(world: &mut World, id: PromiseId) -> Self {
Self {
id,
world: Rc::new(Cell::new(world as *mut World))
}
}
pub fn resolve<T: 'static>(&self, value: T) {
let world = unsafe { self.world.get().as_mut().unwrap() };
{
let Some(requests) = world.get_resource::<WasmRequests>() else {
return
};
if requests.contains(&self.id) {
promise_resolve(world, self.id, (), value)
}

}
world.resource_mut::<WasmRequests>().remove(&self.id);
}
}
#[cfg(target_arch = "wasm32")]
unsafe impl Send for WasmResolver { }
#[cfg(target_arch = "wasm32")]
unsafe impl Sync for WasmResolver { }
#[cfg(target_arch = "wasm32")]
#[derive(Resource, Deref, DerefMut, Default)]
pub struct WasmRequests(HashSet<PromiseId>);

pub struct Request(ehttp::Request);
impl Request {
pub(crate) fn new() -> Self {
Expand All @@ -36,15 +89,36 @@ impl Request {
self
}
pub fn send(self) -> Promise<(), Result<Response, String>> {
Promise::register(
|world, id| {
let task = AsyncComputeTaskPool::get().spawn(async move { ehttp::fetch_blocking(&self.0) });
world.resource_mut::<Requests>().insert(id, task);
},
|world, id| {
world.resource_mut::<Requests>().remove(&id);
},
)
#[cfg(target_arch = "wasm32")]
{
Promise::register(
|world, id| {
world.resource_mut::<WasmRequests>().insert(id);
let resolver = WasmResolver::new(world, id);
ehttp::fetch(self.0, move |result| {
resolver.resolve(result);
});
// let task = AsyncComputeTaskPool::get().spawn(async move { ehttp::fetch_blocking(&self.0) });
// world.resource_mut::<Requests>().insert(id, task);
},
|world, id| {
// world.resource_mut::<Requests>().remove(&id);
world.resource_mut::<WasmRequests>().remove(&id);
},
)
}
#[cfg(not(target_arch = "wasm32"))]
{
Promise::register(
|world, id| {
let task = AsyncComputeTaskPool::get().spawn(async move { ehttp::fetch_blocking(&self.0) });
world.resource_mut::<Requests>().insert(id, task);
},
|world, id| {
world.resource_mut::<Requests>().remove(&id);
},
)
}
}
}

Expand Down Expand Up @@ -96,7 +170,6 @@ impl<S> HttpOpsExtension<S> for AsynOps<S> {
Http(self.0)
}
}

#[derive(Resource, Deref, DerefMut, Default)]
pub struct Requests(HashMap<PromiseId, Task<Result<Response, String>>>);

Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,5 @@ cargo run --example system_state
This example shows how promises keep the state of Bevy's system params.
We create 16 buttons and asyn loop single promise every second.
Inside the promise we log buttons with changed for the previous second
`Interaction` component by querying with Changed<Interaction> filter.
`Interaction` component by querying with `Changed<Interaction>` filter.
![System State](../docs/system-state.gif)
8 changes: 4 additions & 4 deletions examples/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ fn main() {

fn setup(mut commands: Commands, time: Res<Time>) {
let start = time.elapsed_seconds();
// create PromiseLike chainable commands with the current time as state
// create PromiseLike chainable commands with the current time as the state
commands
.promise(|| start)
// will be executed right after current stage
.then(asyn!(state => {
info!("Wait a second..");
state.asyn().timeout(1.0)
}))
// will be executed after in a second after previous call
// will be executed in a second after the previous call
.then(asyn!(state => {
info!("How large is is the Bevy main web page?");
state.asyn().http().get("https://bevyengine.org")
}))
// will be executed after request completes
// will be executed after we get the response/error
.then(asyn!(state, result => {
match result {
Ok(response) => info!("It is {} bytes!", response.bytes.len()),
Expand All @@ -40,4 +40,4 @@ fn setup(mut commands: Commands, time: Res<Time>) {
info!("Exiting now");
asyn::app::exit()
}));
}
}

0 comments on commit 23df401

Please sign in to comment.