add leave event to make entering a client more reliable (#50)

Instead of relying on release events not getting lost, every event now signals the opponent
to release its pointer grab.

There is one case that requires a Leave event:

Consider a Sending client A and receiving Client B.

If B enters the dead-zone of client A, it will send an enter event towards A but before
A receives the Release event, it may still send additional events towards B that should not cause
B to immediately revert to Receiving state again.

Therefore B puts itself into AwaitingLeave state until it receives a Leave event coming from A.
A responds to the Enter event coming from B with a leave event, to signify that it will no longer
send any events and releases it's pointer.

To guard against packet loss of the leave events, B sends additional enter events while it is in AwaitingLeave
mode until it receives a Leave event at some point.

This is still not resilient against possible packet reordering in UDP but in the (rare) case where a leave event arrives before some other event coming from A, the user would simply need to move the pointer into the dead-zone again.
This commit is contained in:
Ferdinand Schober
2023-12-17 17:38:06 +01:00
committed by GitHub
parent 02d1b33e45
commit 735434438f
4 changed files with 87 additions and 52 deletions

View File

@@ -36,6 +36,7 @@ use crate::{
enum State {
Sending,
Receiving,
AwaitingLeave,
}
pub struct Server {
@@ -308,63 +309,91 @@ impl Server {
state.client.active_addr = Some(addr);
match (event, addr) {
(Event::Pong(), _) => {} // ignore pong events
(Event::Pong(), _) => { /* ignore pong events */ }
(Event::Ping(), addr) => {
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
log::error!("udp send: {}", e);
}
}
(event, addr) => {
// device is sending events => release pointer if captured
if self.state == State::Sending {
// Even in sending state, we can still receive events
// from the client we entered until (our) release event
// arrives there.
//
// Therefore we must wait until we receive
// another release event to actually release the pointer.
if let Event::Release() = event {
log::debug!("releasing pointer ...");
self.producer.release();
self.state = State::Receiving;
}
return;
}
// consume event
self.consumer.consume(event, handle).await;
log::trace!("{event:?} => consumer");
// let the server know we are still alive once every second
let last_replied = state.last_replied;
if last_replied.is_none()
|| last_replied.is_some()
&& last_replied.unwrap().elapsed() > Duration::from_secs(1)
{
state.last_replied = Some(Instant::now());
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
// tell clients that we are ready to receive events
if let Event::Enter() = event {
if let Err(e) = send_event(&self.socket, Event::Leave(), addr).await {
log::error!("udp send: {}", e);
}
}
match self.state {
State::Sending => {
if let Event::Leave() = event {
// ignore additional leave events that may
// have been sent for redundancy
} else {
// upon receiving any event, we go back to receiving mode
self.producer.release();
self.state = State::Receiving;
}
},
State::Receiving => {
// consume event
self.consumer.consume(event, handle).await;
log::trace!("{event:?} => consumer");
},
State::AwaitingLeave => {
// we just entered the deadzone of a client, so
// we need to ignore events that may still
// be on the way until a leave event occurs
// telling us the client registered the enter
if let Event::Leave() = event {
self.state = State::Sending;
}
// entering a client that is waiting for a leave
// event should still be possible
if let Event::Enter() = event {
self.state = State::Receiving;
}
},
}
},
}
// let the server know we are still alive once every second
if state.last_replied.is_none()
|| state.last_replied.is_some()
&& state.last_replied.unwrap().elapsed() > Duration::from_secs(1)
{
state.last_replied = Some(Instant::now());
if let Err(e) = send_event(&self.socket, Event::Pong(), addr).await {
log::error!("udp send: {}", e);
}
}
}
async fn handle_producer_event(&mut self, c: ClientHandle, e: Event) {
log::trace!("producer: ({c}) {e:?}");
// we are sending events
self.state = State::Sending;
// get client state for handle
let state = match self.client_manager.get_mut(c) {
Some(state) => state,
None => {
// should not happen
log::warn!("unknown client!");
self.producer.release();
self.state = State::Receiving;
return;
}
};
// if we just entered the client we want to send additional enter events until
// we get a leave event
if let State::Receiving | State::AwaitingLeave = self.state {
self.state = State::AwaitingLeave;
if let Some(addr) = state.client.active_addr {
if let Err(e) = send_event(&self.socket, Event::Enter(), addr).await {
log::error!("udp send: {}", e);
}
}
}
// otherwise we should have an address to
// transmit events to the corrensponding client
if let Some(addr) = state.client.active_addr {
@@ -414,16 +443,6 @@ impl Server {
log::error!("udp send: {}", e);
}
}
// we can not assume the client is in receiving mode because a
// client can always change mode from receiving to sending
// by restarting.
// To prevent both clients from being in sending mode,
// we send a release event.
if let Err(e) = send_event(&self.socket, Event::Release(), *addr).await {
if e.kind() != ErrorKind::WouldBlock {
log::error!("udp send: {}", e);
}
}
}
}