Henauxg / bevy_quinnet

A Client/Server game networking plugin using QUIC, for the Bevy game engine.
Apache License 2.0
244 stars 14 forks source link

Gracefully disconnect connections and trigger events #6

Closed zheilbron closed 1 year ago

zheilbron commented 1 year ago

Two somewhat related fixes in this PR:

  1. The client and server will now attempt to process pending messages before closing. Previously, the following pattern would cause a race.
    send_message(...);
    close_connection(...);
  2. A separate wait task is spawned to wait on closed connections in order to signal the ConnectionLostEvents.
Henauxg commented 1 year ago

I did indeed leave the disconnection/close behavior into a quick and dirty grey territory until now. It's good that you looked into it and it made me took a closer look too. Here are my thoughts about it:

Desired behaviors :

We are still missing a few bits so I proposed additional changes to your PR to fulfill those:

Thanks a lot for your work ! Feel free to review the additional changes I propose. I'll wait for your opinion to merge those.

Minors:

Possible future tweaks:

zheilbron commented 1 year ago

Thanks for taking a holistic look at the issue and fixing up the PR. I agree with the desired behaviors.

client: In close_waiter, we could rename conn_err to close_reason and log as: info!("Connection closed: {}", close_reason); since "normally" closing locally of by the peer is not really considered as an "error".

I agree. This is how quinn names it though: https://docs.rs/quinn/latest/quinn/struct.Connection.html#method.closed

I think that we don't need to call flush() yet, since send() does a flush every time. But since replacing send() with non flushing feed() or send_all() is on the roadmap, we might as well leave the flush() calls.

I was debating this, but couldn't convince myself whether or not it was needed. Thanks for pointing out that send does indeed flush.

One question I'm still unsure about: What is the lifecycle of a connection, particularly one that has reached the Disconnected state? Can it ever be reconnected or is it essentially garbage at that point? And, if it's garbage, should it be collected automatically once it's no longer needed?

I noticed that this is problematic if one relies on the default connection. If the default connection ever becomes disconnected, it remains a defunct connection until manually cleared with client::close_connection.

Henauxg commented 1 year ago

As of now, connections are indeed kind of a dead weight once Disconnected. This is partly why I made connection::disconnect private, and only used through the public client::close_connection which properly dispose of the connection. But you are right that even so we can't close them ourselves, connections can still become Disconnected due to other external factors (errors, server closing, ...) which is an annoyance when it happens to the default connection.

I was thinking that I might add a reconnect to re-use the same ConnectionConfiguration and ConnectionId. It seems to offer better ergonomics than a new open_connection call, allowing for thinks like "auto-reconnect", or just the simplicity of keeping the same default connection.