-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
bevy_xwt #136
Comments
I see the On the API, I'd prefer having separate crates for client and server instead of features. I'm very much appreciate the help, but at the same time I'd like to setup things the certain way, but I'm currently working on a more lower level things and am not yet at the stage when I'm ready to build an full featured bevy integration. I have a few things in mind for the actual bevy networking libraries, but most of them are kind of transport agnostic to a degree, and would use To elaborate on my networking thoughs, I'm thinking about building a couple different implementations with deep integration:
All of those could be built on top of xwt - but they don't really have to, as I am confident that we can build an abtraction layer and power it with an open ecosystem of crates, like So, maybe you'd be interested in starting that work instead? It will still be some time before I get to work on the |
Going to do my best to elaborate here, but I think we may have similar needs. I'm making an MMO so fine-tuned control is important. A few things:
I think the best way to go here is actually the original proposal: re-exporting the xwt types with bevy ecosystem integration (e.g. commands, system parameters, plugins). I also think The reason is control. We shouldn't give up control. By having a very simple Here is the tree of
In my case, I use If we translated this to the
The idea here is In an ideal world, here's what I want: Protocol (shared dependency): #[derive(XwtProcol)] // Ensures it's Serializable, Deserializable, etc.
pub struct PingPayload; Client: fn main() {
App::new()
.add_plugins(XwtClientPlugin {
encoding: Encoding::Json,
validate_certs: false,
... // more configuration
})
.add_protocol::<PingPayload>()
.add_systems(
Startup,
|mut commands: Commands| {
commands.connect_xwt_server("127.0.0.1:3536");
},
)
.add_systems(
Update,
{
|mut transport: XwtTransport<PingPayload>| {
transport.reliable_to_host(PingPayload);
for _pong in client.read() {
info!("...Received pong!");
}
}
}
)
.run();
} Server: fn main() {
App::new()
.add_plugins(XwtServerPlugin {
encoding: Encoding::Json,
port: 3536,
cert: Certs::SelfSigned,
... // more configuration
})
.add_protocol::<PingPayload>()
.add_systems(
Startup,
|mut commands: Commands| {
commands.start_listening(); // Start server
},
)
.add_systems(
Update,
{
|mut events: EventReader<XwtServerEvent>, mut server: XwtServer| {
for ev in events.read() {
if let XwtServerEvent::ConnectionRequest(session) = ev {
server.accept(session);
}
}
}
}
)
.add_systems(
Update,
{
|mut transport: XwtTransport<PingPayload>| {
for (session, _ping) in transport.read() {
transport.reliable_to_(session, PingPayload);
}
}
}
)
.run();
} |
Oh, I may have confused you: I feel like all my networking ideas should explicitly not be This means if I'd were to build the more advanced stuff I'd myself pick a different name for that - and probably something with a separate banding from the For those high level things, the protocol encoding layer is in their domain, because there are differences in how we'd want to model the encoding of the data. Meaning that Of course, monorepos would be the go - but we might be go with more than one. As in, one for the networking abstraction layer - that one would also contain the "low-level" bevy bindings; good candidates for drivers there are Btw, there are already a lot of crates that would benefit from the abstracted transport crate:
All those have already shown interest in or integrated with That said, I feel like transport layer should actually be fully transparent, and not hide any of its dependencies. What I means is it shouldn't even provide concrete type - just a family of traits, similar to what An example of that would look something like this: type Transport = bevy_net_client::Client<bevy_net_xwt::Client<xwt_web_sys::Client>>;
fn main() {
let mut transport: Transport = bevy_net_client::Client::new();
transport.spawn_ioloops().unwrap();
let net_plugin: bevy_opt1_net::Plugin<Transport, mygame::Protocol> = bevy_opt1_net::client::Plugin {
transport,
// ... more configuration
};
App::new()
.add_plugins(net_plugin)
.add_systems(
Startup,
|mut commands: Commands| {
// `bevy_net_client` command that takes the `bevy_net_xwt`-specific (i.e. driver specific) params
// This would need to be tied together via generics, so maybe use a different query
// than the raw `Commands` here...
commands.bevy_net_client_connect(bevy_net_xwt::ConnectionParams { url: "127.0.0.1:3536", });
},
)
.add_systems(
Update,
{
|frame_idx: bevy_opt1_net::ScheduleFrame, mut replication_tree: bevy_opt1_net::ReplicationTree<mygame::Protocol>| {
replication_tree.insert(frame_idx, PingPayload);
// The application never sees the network layer, as it only has to interact with deterministic state
// which is replicated automatically...
}
}
)
.add_systems(
Update,
{
|frame_idx: bevy_opt1_net::ScheduleFrame, mut replication_tree: bevy_opt1_net::ReplicationTree<mygame::Protocol>| {
replication_tree.insert(frame_idx, mygame::PingPayload);
// The application never sees the network layer, as it only has to interact with deterministic state
// which is replicated automatically...
}
}
)
.add_systems(
Update,
{
|mut transport: bevy_opt1_net::TransportResource<Transport>| {
// Here's an example of a low-level access to the transport if the need be.
// At this point, code knows what concrete transport type is being used, and
// allows us to directly invoke the underlying APIs if we need to.
let _: &bevy_net_client::Client<bevy_net_xwt::Client<xwt_web_sys::Client>> = transport.as_ref();
// For instance, we can access a `xwt_web_sys`-specific function right through the types composition:
let driver: &xwt_web_sys::Client = transport.driver();
// Call something that's available in the Web WebTransport API driver only
// https://w3c.github.io/webtransport/#dom-webtransport-anticipatedconcurrentincomingunidirectionalstreams
println!("{}", driver.anticipated_concurrent_incoming_unidirectional_streams);
}
}
)
.run();
} This should demonstrate my approach to hiding the types and reexports - for this particular system, at a lot of places I'd pick a transparent the composition of generics instead. I understand the tradeoffs between this and the alternative very well, and I think this, with a lot of care and caution in the API design, is the way to go. Oh, yeah, note how I did't specify how the |
Or, btw, I want to change the xwt crate such that it doesn't directly import the drivers, so it's dependencies would look something like this:
And the app that uses
and in the code the app will be responsible for selecting the driver. In the #[cfg(wasm)]
type Client = xwt_web_sys::Client;
#[cfg(native)]
type Client = xwt_wtransport::Client; You might also want to be able to switch between the drivers if there are more than one available for a given platform: #[cfg(native)]
enum Client {
// https://docs.rs/wtransport
WTransport(xwt_wtransport::Client),
// https://docs.rs/webtransport-quinn, if a driver for it was implemented
WebTransportuinn(xwt_webtransport_quinn::Client),
} While most of the code can be generic around a driver type: struct MyState<Client: xwt_core::Client> {
client: Client,
// ...
}
impl<Client: xwt_core::Client> MyState<Client> {
// We can do anything we want from what `xwt_core::Client` offers with the client now.
} So, after my planned refactor one wouldn't be able to just use an Current approach the |
Closing as pot planned because it is currently out of scope for this crate - it needs more fundamental work on the interfaces and standards support before it would be a good time to focus on explicit bevy integration. |
Hey,
When doing my exploration into #132 - I found that I would indeed benefit from an API akin to bevy_matchbox
I want to start building
bevy_xwt
, at a high level:bevy_xwt
: 2 features (client, server)commands.start_session(url: &str)
commands.close_session()
ClientSession
, derefs toSession
enum ClientEvent { IdAssigned(SessionId), Connected, Disconnected }
commands.start_server(port: u16)
commands.stop_server()
ServerSessions
, derefs toHashMap<SessionId, Session>
enum ServerEvent { ClientJoined(SessionId), ClientLeft(SessionId) }
That's basically it, extremely simple to start.
Since you mentioned you want to reserve the name
bevy_xwt
, do you mind starting the seed repo, and adding me as a contributor so I can continue to build it under your repo?The text was updated successfully, but these errors were encountered: