Open miketwenty1 opened 1 year ago
It would be nice to see an example where the bevy app calls out to a webserver
There are two "difficulties" with an http request example, though I agree having this example would be great:
As an extra, having it work in both wasm and native could make it harder
We avoid dependencies just for tests that are not present in the rest of the engine, that takes out all dependencies that could perfom an http query
So this means Bevy wouldn't post an example using reqwest? If I understand this correctly? Would it be acceptable to use a more primitive-y method via https://rustwasm.github.io/wasm-bindgen/examples/fetch.html ?
We would need a stable external api that we're sure enough won't change without a notice, won't fail due to server error,
I think a good example resource to pull would be data from like from crates.io / the bevy website / github.com/bevyengine or something similar.. just so it's in the spirit of a web resource related to bevy.
As an extra, having it work in both wasm and native could make it harder
Agreed, but the main concern I have with this statement isn't the hardness of it.. it's whether or not the tokio runtime would be acceptable for the non wasm build.. but that kinda goes back to your first point. that's an external dependency. Not sure if bevy already has something.
It should be noted that reqwest
depends on hyper
which depends on tokio
which is incompatible with our current async executor since tokio does not work on WASM.
@james7132 the example gist on the original post above works with target WASM. test for yourself.
Edit: added Cargo.toml
to the gist to help people run if they doubt the code.
you need to run cargo run --target wasm32-unknown-unknown
will need to point to a valid api endpoint though.
Will need
[target.wasm32-unknown-unknown]
runner = "wasm-server-runner"
in .cargo/config.toml as well.
So this means Bevy wouldn't post an example using reqwest?
Yeah, reqwest is everything but light...
Would it be acceptable to use a more primitive-y method via https://rustwasm.github.io/wasm-bindgen/examples/fetch.html ?
Most of those are already dependencies in Bevy, but then that would work only in wasm.
it's whether or not the tokio runtime would be acceptable for the non wasm build..
No. Aside from the dependency issue, having tokio means having two tasks ecosystem handling their threads at the same time, not the best to show in an example.
@mockersf would there be consideration for wasm only target examples? or does every bevy example HAVE to support multiple targets?
I would rather avoid having wasm only examples... I'm not a fan of https://crates.io/crates/ehttp API but it's the crate with the smallest dependency set (that I know of) that handles both native and WASM. Maybe an example with it could be added...
@mockersf ehttp uses ureq on native, which has blocking I/O underneath and requires spawning a new thread for each request. Which isn't usually a problem but it's about the same overhead as a single threaded Tokio executor (but, okay, with a smaller dependency footprint).
I think that, in general, having an example showing integration with Tokio or other executor would be very nice, even if running things on Tokio shouldn't be the first option. For example, relm4 (a GUI framework based on GTK4) features its own executor (which runs tasks on GTK's event loop) but has examples showing that integration with Tokio is possible.
Hopefully the async ecosystem in Rust will evolve to be more executor agnostic. As of today, the only executor agnostic http client I can find is isahc which besides not working in wasm, also spawns a thread
Yeah ehhtp
/ureq
is not perfect either...
having an example showing integration with Tokio or other executor would be very nice
Yes! but... maybe not in the main Bevy repo. Maybe in the cheat book? @inodentry would that be something interesting for you? Or somewhere else where we could host more complex examples.
Why not in the main Bevy repo?
@mockersf would a feature for bevy_http
or bevy_fetch
make more sense.. to not increase the build time?
I don't think so, the example would still need to have it enabled.
You could make a third party plugin to handle that nicely though (maybe with a less generic name)
@mockersf I don't understand this response
I don't think so, the example would still need to have it enabled.
What would be the issue with a bevy example utilizing a feature flag?
If the example is requiring a feature flag and we don't enable it, it will not be updated with breaking changes and quickly fall out of sync.
I'm still not following. All examples are subject to falling out of sync with bevy updates, correct?
If a feature isn't maintained for some reason, I imagine the examples with that feature should be be removed. What is the thinking around a specific feature example not being maintained with the rest bevy examples?
All examples are subject to falling out of sync with bevy updates, correct?
In our CI, we enforce that examples compile (and run) properly so that they're always in sync with potential breaking changes to the public API.
I'm guessing you would need multiple CI runs with different features enabled against the various example sets.
FYI @miketwenty1 I did an example of this back in October using the ehttp crate with Bevy: https://github.com/wanderrful/yet-another-bevy-project/blob/experiment-6/src/services/reqres.rs
In the /ui/experiment-6
folder you can see how it's used to call the API and use the response to populate a UI (the concerns of the UI are separated into different module files, so it's not all-in-one as most examples usually are).
There is promises implementation for bevy: https://github.com/jkb0o/pecs
It supports http requests via ehttp
out of the box:
commands.add(
Promise::start(asyn!(_ => {
info!("How large is is the Bevy main web page?");
asyn::http::get("https://bevyengine.org")
}))
.then(asyn!(_, result => {
match result {
Ok(response) => info!("It is {} bytes!", response.bytes.len()),
Err(err) => info!("Ahhh... something goes wrong: {err}")
}
Promise::pass()
}))
);
Output:
41.627 INFO simple: How large is is the Bevy main web page?
41.715 INFO simple: It is 17759 bytes!
Doesn't solve the issue itself, but looks related to me.
ehttp merged support for cross platform async, I think it's a strong candidate (https://github.com/emilk/ehttp/pull/25)
I struggled with this initially, so have created as simple of an example as I could. See gist linked below:
https://gist.github.com/dimvoly/3b41e864b44b4e2af3938f9f608a0665
Is uses the blocking variety of reqwest
but at least in my use case I'm sending a very limited amount of requests so there's no need for me to initialize a runtime.
If you search around you can find that people have setup tokio runtimes/contexts for the non-blocking version if that's what you need.
I don't believe this example would work on WASM, but should get most people started with basic http stuff for non-WASM.
Hopefully helps someone.
@dimvoly the example in the original post has a working example with wasm
@miketwenty1 yes the original example is super useful in that regard
@dimvoly thanks for your Bevy 0.11 example!
It's a bit frustrating that one would write async move { .. }
but then use reqwest::blocking
inside that async block. Didn't reqwest::get("https://httpbin.org/get").await
work?
@dlight Bit beyond my current understanding how to use the non-blocking variety (reqwest::get("https://httpbin.org/get").await
) inside an IoTaskPool
task. You'd need a tokio
context which you'd have to spawn manually in your app start up somewhere. However, I suspect that isn't strictly compatible with IoTaskPool
at present - see this issue for further discussion on .await
inside TaskPool.
So from my understanding, for the example in my gist, the thread is blocked, but it's the thread in IoTaskPool
which is not the main thread so your app doesn't freeze.
Is there any chance that in the near future we will have at least basic web request support in Bevy without using third party plugins? For now, I feel if I would use 3 plugins each one will implement request on their own leading to multiple possible source of bugs.
There is a nice and small third party plugin which implements requests using ehttp
mentioned before: https://github.com/foxzool/bevy_http_client
I even update it a little and make it possible to have TypedResponse
which make it easy to parse results into structs, example here: https://github.com/Leinnan/bevy_http_client/blob/master/examples/typed.rs
How can Bevy's documentation be improved?
I'm suggesting something that may be incorrect! But it seems that an async IoTaskPool example would be ideal for helping teach people how to bring in/send out external data to/from a webserver. This would be in the case where using sockets would be overkill.
As far as I understand the main example documentation around IoTaskPool is the test within this lib.rs. (which for myself, a bevy and rust noob, was too sparse). https://github.com/bevyengine/bevy/blob/main/crates/bevy_core/src/lib.rs#L145
Also the AsyncComputeTaskPool example is too specific to be useful for understanding channels/http requests. https://github.com/bevyengine/bevy/blob/main/examples/async_tasks/async_compute.rs
It would be nice to see an example where the bevy app calls out to a webserver using some sort of user input, and then the webserver returns data that is rendered in the bevy game. Using something like reqwest (if applicable). I believe channels would also be needed for this example? This would really paint the full picture on how to currently handle these sort of network io tasks for a bevy game. I'm guessing many more people interested in WASM bevy development would be able to utilize this example.
Ohh did I mention the motivation for this example is for WASM targets? :)
Example
I have managed to create a PoC for this, but it's quite barbaric and I'm guessing someone could make a really slick example of this idea and put it up as an official example. https://gist.github.com/miketwenty1/baa1634fe558186e606c02932b8f37c8
Also note, this seems to work fine with either
IoTaskPool
orAsyncComputeTaskPool
So I'm sort of just guessing IoTaskPool is more appropriate in this case.