/*
Hurry Curry! - a game about cooking
Copyright 2024 metamuffin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, version 3 of the License only.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
use anyhow::Result;
use hurrycurry_protocol::{PacketC, PacketS, BINCODE_CONFIG};
use log::warn;
use std::{collections::VecDeque, net::TcpStream};
use tungstenite::{
client::{uri_mode, IntoClientRequest},
client_tls_with_config,
handshake::client::Request,
stream::{MaybeTlsStream, Mode},
Message, WebSocket,
};
pub struct Network {
sock: WebSocket>,
pub queue_in: VecDeque,
pub queue_out: VecDeque,
}
impl Network {
pub fn connect(addr: &str) -> Result {
let (parts, _) = addr.into_client_request().unwrap().into_parts();
let mut builder = Request::builder()
.uri(parts.uri.clone().clone())
.method(parts.method.clone())
.version(parts.version);
*builder.headers_mut().unwrap() = parts.headers.clone();
let request = builder.body(()).unwrap();
let host = request.uri().host().unwrap();
let host = if host.starts_with('[') {
&host[1..host.len() - 1]
} else {
host
};
let port = request
.uri()
.port_u16()
.unwrap_or(match uri_mode(request.uri())? {
Mode::Plain => 27032,
Mode::Tls => 443,
});
let stream = TcpStream::connect((host, port))?;
stream.set_nodelay(true).unwrap();
let (mut sock, _) = client_tls_with_config(request, stream, None, None).unwrap();
match sock.get_mut() {
MaybeTlsStream::Plain(s) => s.set_nonblocking(true).unwrap(),
MaybeTlsStream::Rustls(s) => s.sock.set_nonblocking(true).unwrap(),
_ => todo!(),
};
Ok(Self {
sock,
queue_in: VecDeque::new(),
queue_out: VecDeque::new(),
})
}
pub fn poll(&mut self) {
self.queue_in.extend(match self.sock.read() {
Ok(Message::Text(packet)) => match serde_json::from_str(&packet) {
Ok(p) => Some(p),
Err(e) => {
warn!("invalid json packet: {e:?}");
None
}
},
Ok(Message::Binary(packet)) => {
match bincode::decode_from_slice(&packet, BINCODE_CONFIG) {
Ok((p, _)) => Some(p),
Err(e) => {
warn!("invalid bincode packet: {e:?}");
None
}
}
}
Ok(_) => None,
Err(e) => {
warn!("{e:?}");
None
}
});
for packet in self.queue_out.drain(..) {
self.sock
.write(Message::Text(serde_json::to_string(&packet).unwrap()))
.unwrap();
}
}
}