From e81b6fa1fa9aa0f693619bcf22a7877512ff1fb4 Mon Sep 17 00:00:00 2001 From: Michael Bradley Date: Sun, 25 May 2025 12:28:32 -0400 Subject: [PATCH] Redo netcode error handling No more panics :) --- src/net.rs | 122 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 77 insertions(+), 45 deletions(-) diff --git a/src/net.rs b/src/net.rs index 1055e92..3645944 100644 --- a/src/net.rs +++ b/src/net.rs @@ -1,18 +1,67 @@ use std::{ net::{Ipv6Addr, SocketAddr, UdpSocket}, - thread, + thread::{JoinHandle, spawn}, }; -use bevy::prelude::*; +use bevy::{prelude::*, tasks::futures_lite::io}; use crossbeam_channel::{Receiver, Sender, unbounded}; use crate::game::seed::Seed; -#[derive(Resource)] -pub struct NetworkSend(Sender<(u64, SocketAddr)>); +fn configure_socket(socket: &UdpSocket) -> io::Result<()> { + socket.set_read_timeout(None)?; + socket.set_write_timeout(None)?; + Ok(()) +} + +type NetworkMessage = (u64, SocketAddr); + +fn start_network_thread( + network_loop: fn(M, UdpSocket) -> Result, + messages: M, + socket: UdpSocket, +) -> JoinHandle { + spawn(move || { + let result = network_loop(messages, socket); + match result { + Ok(()) => error!("Network thread: Loop returned without error?"), + Err(ref err) => error!("Network thread: {}", err), + }; + result + }) +} + +fn network_send_loop(messages: Receiver, socket: UdpSocket) -> Result { + loop { + let (message, address) = messages.recv()?; + socket.send_to(&message.to_le_bytes(), address)?; + } +} + +fn network_receive_loop(messages: Sender, socket: UdpSocket) -> Result { + loop { + let mut message = [0u8; 8]; + let (len, address) = socket.recv_from(&mut message)?; + info!("Network thread: Received {len} bytes"); + messages.try_send((u64::from_le_bytes(message), address))?; + } +} + +fn setup_socket(port: u16) -> Result<(Sender, Receiver)> { + let socket = UdpSocket::bind((Ipv6Addr::LOCALHOST, port))?; + configure_socket(&socket)?; + let (send_inbound, receive_inbound) = unbounded(); + start_network_thread(network_receive_loop, send_inbound, socket.try_clone()?); + let (send_outbound, receive_outbound) = unbounded(); + start_network_thread(network_send_loop, receive_outbound, socket); + Ok((send_outbound, receive_inbound)) +} #[derive(Resource)] -pub struct NetworkReceive(Receiver<(u64, SocketAddr)>); +pub struct NetworkSend(Sender); + +#[derive(Resource)] +pub struct NetworkReceive(Receiver); fn handle_network_io( receive: Res, @@ -31,6 +80,13 @@ fn handle_network_io( Ok(()) } +#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)] +enum NetworkState { + #[default] + SinglePlayer, + MultiPlayer, +} + pub struct NetIOPlugin { listen: u16, peer: Option, @@ -44,51 +100,27 @@ impl NetIOPlugin { impl Plugin for NetIOPlugin { fn build(&self, app: &mut App) { - app.add_systems(FixedUpdate, handle_network_io); + app.init_state::().add_systems( + FixedUpdate, + handle_network_io.run_if(in_state(NetworkState::MultiPlayer)), + ); - let (send, receive) = match UdpSocket::bind((Ipv6Addr::LOCALHOST, self.listen)) { - Ok(socket) => { - socket.set_read_timeout(None).unwrap(); - socket.set_write_timeout(None).unwrap(); - let (send_outbound, receive_outbound) = unbounded::<(u64, SocketAddr)>(); - let send_socket = socket.try_clone().unwrap(); - thread::spawn(move || { - loop { - match receive_outbound.recv() { - Ok((message, address)) => send_socket - .send_to(&message.to_le_bytes(), address) - .unwrap(), - Err(err) => { - error!("{err}"); - break; - } - }; + match setup_socket(self.listen) { + Ok((send, receive)) => { + if let Some(socket) = self.peer { + if let Err(err) = send.try_send((0, socket)) { + warn!("Failed to send to peer: {err}"); + return; } - }); - let (send_inbound, receive_inbound) = unbounded::<(u64, SocketAddr)>(); - thread::spawn(move || { - loop { - let mut message = [0u8; 8]; - let (len, address) = socket.recv_from(&mut message).unwrap(); - info!("Received {len} bytes"); - send_inbound - .try_send((u64::from_le_bytes(message), address)) - .unwrap(); - } - }); - (send_outbound, receive_inbound) + } + + app.insert_state(NetworkState::MultiPlayer) + .insert_resource(NetworkSend(send)) + .insert_resource(NetworkReceive(receive)); } Err(err) => { - error!("Could not bind socket: {err}"); - todo!("bounded(0) is apparently meaningful so find another solution") + warn!("Failed to set up networking: {err}"); } }; - - if let Some(socket) = self.peer { - send.try_send((0, socket)).unwrap(); - } - - app.insert_resource(NetworkSend(send)); - app.insert_resource(NetworkReceive(receive)); } }