johanhelsing / matchbox

Painless peer-to-peer WebRTC networking for rust wasm (and native!)
Apache License 2.0
868 stars 70 forks source link

PeerState::Disconnected received before PeerState::Connected #353

Open ndev-rs opened 10 months ago

ndev-rs commented 10 months ago

When a player joins a room, if the rest of the players quickly leave and rejoin to create a new session together, often the new player will see some or all of these disconnect and reconnects out of order.

    for (id, state) in socket.update_peers() {
        match state {
            PeerState::Connected => {
                info!("matchbox: peer connected: {:?}", id);
            }
            PeerState::Disconnected => {
                info!("matchbox: peer disconnected: {:?}", id);
            }
        }
    }

Output:

2023-10-27T18:33:02.740393Z  INFO *::network: matchbox: peer connected: PeerId(10e5cfbd-5f95-48df-a5b4-998951f56e88)
2023-10-27T18:33:02.740461Z  INFO *::network: matchbox: peer disconnected: PeerId(10e5cfbd-5f95-48df-a5b4-998951f56e88)
2023-10-27T18:33:02.740473Z  INFO *::network: matchbox: peer disconnected: PeerId(5004a446-0a23-4417-acd1-d6b18fee1455)
2023-10-27T18:33:03.007778Z  INFO *::network: matchbox: peer connected: PeerId(b3c558f9-50eb-4cb4-98f8-cb39713bf5bf)
2023-10-27T18:33:03.007956Z  INFO *::network: matchbox: peer connected: PeerId(78b5ff17-32a7-421c-955e-16a33f8cdfc4)
2023-10-27T18:33:03.820027Z  INFO *::network: matchbox: peer connected: PeerId(5004a446-0a23-4417-acd1-d6b18fee1455)
Full code
pub fn connect(
...
) {
    if socket.id().is_none() { return };

    let now = SystemTime::now();
    let mut msgs_to_send: Vec<(PeerId, Box<[u8]>)> = Vec::new();

    // update peers
    let peer_events = socket.update_peers();
    let ids: HashSet<_> = socket.connected_peers().collect();
    room_state.add_connected_peers(&ids);
    room_state.remove_disconnected_peers(&ids);

    // on connect
    if room_state.room_time.is_none() {
        room_state.room_time = Some(now);
        room_state.add_local(&now);
    } else if !peer_events.is_empty() {
        // update timeout window
        room_state.room_time = Some(now);
    }

    // broadcast join time to peers
    for (id, state) in &peer_events {
        match state {
            PeerState::Connected => {
                info!("matchbox: peer connected: {:?}", id);
                let packet = encode(TIME, room_state.get_local().join_time);
                msgs_to_send.push((*id, packet));
            }
            PeerState::Disconnected => {
                info!("matchbox: peer disconnected: {:?}", id);
            }
        }
    }

    // recv msgs and update player state
    recv_messages(&mut commands, &assets, &mut socket,
        &mut room_state, &mut level, &mut msgs_to_send,
    );

    // send msgs
    for (id, packet) in msgs_to_send {
        socket.channel(1).send(packet, id);
    }

    // when all ready checks are recvd, start the game
    if room_state.players.values().all(|player_state| player_state.ready) {
        let players: Vec<_> = room_state.players.iter()
            .map(|(key, _)| key.clone())
            .collect();

        create_ggrs_session(
            commands,
            players,
            socket,
            next_state
        );
    }
}
bevy = { version = "0.11", features = ["dynamic_linking"] }
bevy_ggrs = { version = "0.13", features = ["wasm-bindgen"] }
bevy_matchbox = { version = "0.7", features=["ggrs"] }

Default matchbox_server, tested locally on native Linux.

simbleau commented 10 months ago

Ah, so the issue is that a disconnection can be reported before the connection, assuming the events happen close together

johanhelsing commented 10 months ago

I noticed yesterday that it's also possible to get only a Disconnected by itself if a peer tries to connect, but ice gathering fails.

simbleau commented 10 months ago

I noticed yesterday that it's also possible to get only a Disconnected by itself if a peer tries to connect, but ice gathering fails.

Are we tracking this in a separate issue?