Implement the Bevy Remote Protocol (BRP).
This commit implements most of the Bevy Remote Protocol proposal by @coreh in the form of an HTTP server that's activated when RemotePlugin is added to the App. Requests and responses are sent via JSON, using serde and the reflection capability as appropriate. The server is implemented using Tokio and hyper.
All of the proposal has been implemented, with the exception of the POLL request, as it's not needed for debuggers to function and I'm uncertain as to its design.
In addition to the verbs in the proposal, an additional verb has been added, LIST. It allows the client to enumerate all components attached to an entity, or, if called without an entity, all components that are registered to the App.
Two new examples have been added, server and client. The server example spawns a test scene and allows connections. The client example offers a simple command-line interface to BRP queries. I considered having client be more extensive, but it struck me as out of scope for this PR, which is already rather large.
Note that this PR is missing some documentation, so I'm leaving it as a draft for now.
Changelog
Added
- The new Bevy Remote Protocol allows debuggers to connect to your app over HTTP and inspect and modify the ECS. To use it, add the
RemotePluginto your app.
You added a new feature but didn't update the readme. Please run cargo run -p build-templated-pages -- update features to update it, and commit the file change.
I think the smol+hyper stack is perfectly viable, maybe run the server directly on one of bevy's task pools using smol_hyper?
🙌 @pcwalton Thanks for carrying the torch on this, as I haven't been super available for the last couple of months due to various IRL duties/activities. I'll review this PR as soon as I have some bandwidth.
Just out of curiosity, how viable would it be to implement Reflect for https://github.com/bevyengine/bevy/blob/main/crates/bevy_ecs/src/world/mod.rs#L77 and use them. But don't know if there is too much overlap, especially with Get and Query logically.
@mintlu8 Do you have a minimal example of how that would look?
Note that I'm treating "make request JSON convenient to write by hand" as more or less a non-goal. My hope is that this will kickstart the development of community debugging GUIs, and those will be what nearly everyone uses. This doesn't mean that the JSON should be harder to write than necessary, but it does mean that clarity wins over convenience. Conveniences like abbreviations for components, verbs, etc. belong at the level of the debugging UI.
Compressed JSON is surprisingly competitive and is also easy to inspect and debug. I'd prefer to just stick with it. We can always add more formats later.
Since my last two comments sound kind of contradictory, let me clarify: Easy exploration and testing of the API via curl is a goal of my implementation of the BRP, but normal day-to-day use of curl isn't. It should be easy to experiment with the API via curl or similar tools. But you should be using a dedicated client to do most of your real work in it. I suspect most users will want to use GUIs, which could be based on sickle, egui, Web, native, etc. There could also be other UIs, such as CLIs or TUIs.
The inclusion of LIST enables clients to implement abbreviation of component names. A client could start with LIST to retrieve the full names of all components registered to the app. Then it could use that information to resolve short names (e.g. Transform) when they're unambiguous and to report an error when they're ambiguous. A client is in a better position to do this than the server, because it can report errors without a round-trip and can also implement nice things like autocomplete and menu drop-down lists of components.
I made the BRP extensible by adding a RemoteVerbs resource that you can add things to.
@valaphee I don't know how this is applicable; commands seem completely orthogonal. Commands are for granting delayed access to a world within an otherwise-parallel system. But the BRP is already running with exclusive world access, so it has no need for commands.
Compressed JSON is surprisingly competitive and is also easy to inspect and debug. I'd prefer to just stick with it. We can always add more formats later.
Makes sense. (Sorry if you were responding to a comment I deleted--I misunderstood that writing by hand is a non-goal, but reading commands might still be in scope.)
@mintlu8 Do you have a minimal example of how that would look?
This one? https://github.com/smol-rs/smol/blob/master/examples/hyper-server.rs
smol hasn't had any commits for 2 months. Do we still want to rely on it?
smolhasn't had any commits for 2 months. Do we still want to rely on it?
Bevy pretty much uses the entire smol stack under the hood for its async stuff, I don't see why not.
async_task, async_io, futures_lite and async_channel are all smol crates.
Ok, this is now using smol instead of Tokio.
I'm putting this as "needs review" because I'm waiting on @coreh to look at it before doing more.
There actually aren't as many components as you might think -- no more than 50 IIRC. I don't think pagination is necessary. Maybe in the future.
Is there any way to implement this as a protocol agnostic tower service instead? AFAIK hyper is built on tower so much of that work could be reused for an additional http example.
@LegNeato Can you elaborate as to what you mean?
@LegNeato Can you elaborate as to what you mean?
Looking at the patch closer, I think it would best be as a separate project. The thought was to write BRP aupport as a tower service and then wrap it in bevy on the server side and let other non-bevy tools consume the tower service directly on the client side.
i haven't looked super closely at this pr. but i think it could be useful to add code gen for swagger/openapi. this could be in another pr also nice work looks amazing
@LegNeato Can you elaborate as to what you mean?
Looking at the patch closer, I think it would best be as a separate project. The thought was to write BRP aupport as a tower service and then wrap it in bevy on the server side and let other non-bevy tools consume the tower service directly on the client side.
Another benefit of implementing as a tower service is the service can be exposed through other transport mechanisms besides TCP/QUIC such as Unix domain sockets (UDS works on Windows too!)
Any idea of whether a brp server would work on the web? The server example is marked as wasm so I would assume so but I can't think of way for the server to be accessed if running on the web. https://github.com/bevyengine/bevy/blob/a56805a3b2df2248fe6ee584bd8344983bd04393/Cargo.toml#L3271-L3275
For context I'd like to use this for the 3rd example use case in the BRP proposal.
JS/HTML-based UI interacting with an embedded Bevy application running in a browser;
Adopted in #14880: please continue conversation there!