use std::{collections::HashMap, time::Duration, net::ToSocketAddrs}; use event::ServerEvent; use log::{info, error}; use tokio::{net::TcpListener, sync::mpsc::{Sender, self}, select}; use uuid::Uuid; use crate::{client::run_client, event::ClientEvent}; mod event; mod protocol; mod ser; mod varint; mod client; pub type JsonValue = serde_json::Value; #[derive(Clone, Debug)] pub struct ClientInfo { addr: String, port: u16, proto_version: i32, proto_name: &'static str, } #[derive(Debug)] pub struct Player { name: String, uuid: Uuid, } struct Client { tx: Sender, keepalive: Option, info: Option, player: Option, } #[tokio::main] async fn main() -> Result<(), Box> { env_logger::init(); let socket_addr = "0.0.0.0:25565".to_socket_addrs()?.next().expect("invalid server address"); let listener = TcpListener::bind(socket_addr).await?; let mut clients = HashMap::new(); let mut next_id = 0; let (c_tx, mut c_rx) = mpsc::channel(16); let mut keepalive = tokio::time::interval(Duration::from_secs(15)); info!("listening on {socket_addr}"); loop { select! { conn = listener.accept() => { let c_tx = c_tx.clone(); let (stream, addr) = conn?; let id = next_id; next_id += 1; let (s_tx, s_rx) = mpsc::channel(16); clients.insert(id, Client { tx: s_tx, keepalive: None, info: None, player: None, }); info!("#{id} connected from {addr}"); tokio::spawn(async move { let c_tx2 = c_tx.clone(); match run_client(id, stream, c_tx, s_rx).await { Ok(_) => info!("client {id} disconnected"), Err(e) => error!("client {id} error: {e}"), } c_tx2.send((id, ClientEvent::Disconnect)).await.unwrap(); }); }, _ = keepalive.tick() => { for (_, client) in &mut clients { if client.keepalive.is_some() { client.tx.send(ServerEvent::Disconnect(Some("Failed to respond to keep alives".to_owned()))).await?; } else if client.player.is_some() { let data = 1; client.keepalive = Some(data); client.tx.send(ServerEvent::KeepAlive(data)).await?; } } }, ev = c_rx.recv() => { let Some(ev) = ev else { return Err("reciever closed".into()); }; let id = ev.0; let client = clients.get_mut(&id).unwrap(); match ev.1 { ClientEvent::Handshake(info) => client.info = Some(info), ClientEvent::Join(player) => clients.get_mut(&id).unwrap().player = Some(player), ClientEvent::Disconnect => { clients.remove(&id); }, ClientEvent::KeepAlive(data) => { let client = clients.get_mut(&id).unwrap(); if client.keepalive == Some(data) { client.keepalive = None; } else { client.keepalive = Some(0); } } } } } } }