m32chat/src/main.rs
2023-10-03 22:11:42 +02:00

184 lines
5.2 KiB
Rust

use chrono::{DateTime, Duration, Local};
use std::{collections::HashMap, net::UdpSocket};
use log::debug;
const MAX_CLIENTS: usize = 10;
const CLIENT_TIMEOUT: u32 = 300;
const MY_SPEED: u8 = 30;
fn strip_header(msg: &[u8]) -> Vec<u8> {
//print_msg(msg);
let mut stripped = Vec::new();
stripped.push(msg[1] & 3);
stripped.append(msg[2..].to_vec().as_mut());
//print_msg(&stripped);
stripped.to_owned()
}
fn mopp(speed: u8, data: &[u8]) -> Vec<u8> {
let serial = 1u8;
let morse = HashMap::from([
(b'0', "-----"),
(b'1', ".----"),
(b'2', "..---"),
(b'3', "...--"),
(b'4', "....-"),
(b'5', "....."),
(b'6', "-...."),
(b'7', "--..."),
(b'8', "---.."),
(b'9', "----."),
(b'a', ".-"),
(b'b', "-..."),
(b'c', "-.-."),
(b'd', "-.."),
(b'e', "."),
(b'f', "..-."),
(b'g', "--."),
(b'h', "...."),
(b'i', ".."),
(b'j', ".---"),
(b'k', "-.-"),
(b'l', ".-.."),
(b'm', "--"),
(b'n', "-."),
(b'o', "---"),
(b'p', ".--."),
(b'q', "--.-"),
(b'r', ".-."),
(b's', "..."),
(b't', "-"),
(b'u', "..-"),
(b'v', "...-"),
(b'w', ".--"),
(b'x', "-..-"),
(b'y', "-.--"),
(b'z', "--.."),
(b'=', "-...-"),
(b'/', "-..-."),
(b'+', ".-.-."),
(b'-', "-....-"),
(b'.', ".-.-.-"),
(b',', "--..--"),
(b'?', "..--.."),
(b':', "---..."),
(b'!', "-.-.--"),
(b'\'', ".----."),
]);
let mut m = "01".to_owned(); // protocol version
m.push_str(&format!("{:06b}", serial));
m.push_str(&format!("{:06b}", speed));
for &char in data {
if char == b' ' {
continue;
}
for b in morse.get(&char).unwrap().as_bytes() {
if *b == b'.' {
m.push_str("01");
} else {
m.push_str("10");
}
}
m.push_str("00"); // EOC
}
m.replace_range(m.len() - 2.., "11"); // EOW
m = format!(
"{1:0<0$}",
(8 as f32 * (m.len() as f32 / 8 as f32).ceil()) as usize,
m
);
let mut res = Vec::<u8>::new();
for i in (0..m.len()).step_by(8) {
let value = u8::from_str_radix(&m[i..i + 8], 2).unwrap();
res.push(value);
}
res.to_owned()
}
fn broadcast(
socket: &UdpSocket,
receivers: &HashMap<String, DateTime<Local>>,
client: &str,
data: &[u8],
) {
for rec in receivers {
// Do not broadcast to origin
if rec.0 == client {
continue;
}
socket.send_to(&data, &rec.0).unwrap();
}
}
fn main() -> std::io::Result<()> {
env_logger::init();
let socket = UdpSocket::bind("0.0.0.0:7373")?;
socket
.set_read_timeout(None)
.expect("Could not set read timeout");
debug!("Morserino chat server started.");
let mut receivers2: HashMap<String, DateTime<Local>> = HashMap::new();
loop {
let mut buf = [0; 64];
let (number_of_bytes, src_addr) = socket.recv_from(&mut buf).expect("Didn't receive data");
let client_addr = src_addr.to_string();
let speed = buf[1] >> 2;
let data = &buf[0..number_of_bytes];
if receivers2.contains_key(&client_addr) {
debug!("Client known: {client_addr}");
if strip_header(data) == strip_header(mopp(speed, b":bye").as_slice()) {
debug!("Removing client {client_addr} as requested.");
socket
.send_to(mopp(speed, b":bye").as_slice(), &client_addr)
.unwrap();
receivers2.remove(&client_addr);
} else {
broadcast(&socket, &receivers2, &client_addr, data);
receivers2.insert(client_addr.to_owned(), Local::now());
}
} else if strip_header(data) == strip_header(mopp(speed, b"hi").as_slice()) {
debug!("Adding new client {client_addr}");
if receivers2.len() < MAX_CLIENTS {
receivers2.insert(client_addr.to_owned(), Local::now());
socket
.send_to(
mopp(speed, format!("{}{}", ":hi ", receivers2.len()).as_bytes())
.as_slice(),
&client_addr,
)
.unwrap();
} else {
debug!("There are already too many clients connected.");
socket
.send_to(mopp(speed, b":qrl").as_slice(), &client_addr)
.unwrap();
}
} else {
debug!("Ignoring unknown client {client_addr}");
}
let timestamp = Local::now();
for cl in &receivers2 {
if *cl.1 + Duration::seconds(CLIENT_TIMEOUT as i64) < timestamp {
debug!("Removing outdated client {}", cl.0);
socket
.send_to(mopp(MY_SPEED, b":bye").as_slice(), &client_addr)
.unwrap();
}
}
receivers2.retain(|_, val| *val + Duration::seconds(CLIENT_TIMEOUT as i64) >= timestamp);
}
}