Redo netcode error handling
All checks were successful
CI / Formatting (push) Successful in 1m5s

No more panics :)
This commit is contained in:
Michael Bradley 2025-05-25 12:28:32 -04:00
parent dad37262a5
commit e81b6fa1fa
Signed by: MichaelBradley
SSH key fingerprint: SHA256:o/aaeYtRubILK7OYYjYP12DmU7BsPUhKji1AgaQ+ge4

View file

@ -1,18 +1,67 @@
use std::{ use std::{
net::{Ipv6Addr, SocketAddr, UdpSocket}, 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 crossbeam_channel::{Receiver, Sender, unbounded};
use crate::game::seed::Seed; use crate::game::seed::Seed;
#[derive(Resource)] fn configure_socket(socket: &UdpSocket) -> io::Result<()> {
pub struct NetworkSend(Sender<(u64, SocketAddr)>); socket.set_read_timeout(None)?;
socket.set_write_timeout(None)?;
Ok(())
}
type NetworkMessage = (u64, SocketAddr);
fn start_network_thread<M: Send + 'static>(
network_loop: fn(M, UdpSocket) -> Result,
messages: M,
socket: UdpSocket,
) -> JoinHandle<Result> {
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<NetworkMessage>, socket: UdpSocket) -> Result {
loop {
let (message, address) = messages.recv()?;
socket.send_to(&message.to_le_bytes(), address)?;
}
}
fn network_receive_loop(messages: Sender<NetworkMessage>, 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<NetworkMessage>, Receiver<NetworkMessage>)> {
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)] #[derive(Resource)]
pub struct NetworkReceive(Receiver<(u64, SocketAddr)>); pub struct NetworkSend(Sender<NetworkMessage>);
#[derive(Resource)]
pub struct NetworkReceive(Receiver<NetworkMessage>);
fn handle_network_io( fn handle_network_io(
receive: Res<NetworkReceive>, receive: Res<NetworkReceive>,
@ -31,6 +80,13 @@ fn handle_network_io(
Ok(()) Ok(())
} }
#[derive(States, Default, Debug, Clone, PartialEq, Eq, Hash)]
enum NetworkState {
#[default]
SinglePlayer,
MultiPlayer,
}
pub struct NetIOPlugin { pub struct NetIOPlugin {
listen: u16, listen: u16,
peer: Option<SocketAddr>, peer: Option<SocketAddr>,
@ -44,51 +100,27 @@ impl NetIOPlugin {
impl Plugin for NetIOPlugin { impl Plugin for NetIOPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(FixedUpdate, handle_network_io); app.init_state::<NetworkState>().add_systems(
FixedUpdate,
handle_network_io.run_if(in_state(NetworkState::MultiPlayer)),
);
let (send, receive) = match UdpSocket::bind((Ipv6Addr::LOCALHOST, self.listen)) { match setup_socket(self.listen) {
Ok(socket) => { Ok((send, receive)) => {
socket.set_read_timeout(None).unwrap(); if let Some(socket) = self.peer {
socket.set_write_timeout(None).unwrap(); if let Err(err) = send.try_send((0, socket)) {
let (send_outbound, receive_outbound) = unbounded::<(u64, SocketAddr)>(); warn!("Failed to send to peer: {err}");
let send_socket = socket.try_clone().unwrap(); return;
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;
}
};
} }
}); }
let (send_inbound, receive_inbound) = unbounded::<(u64, SocketAddr)>();
thread::spawn(move || { app.insert_state(NetworkState::MultiPlayer)
loop { .insert_resource(NetworkSend(send))
let mut message = [0u8; 8]; .insert_resource(NetworkReceive(receive));
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)
} }
Err(err) => { Err(err) => {
error!("Could not bind socket: {err}"); warn!("Failed to set up networking: {err}");
todo!("bounded(0) is apparently meaningful so find another solution")
} }
}; };
if let Some(socket) = self.peer {
send.try_send((0, socket)).unwrap();
}
app.insert_resource(NetworkSend(send));
app.insert_resource(NetworkReceive(receive));
} }
} }