diff --git a/pumpkin-protocol/src/client/play/c_keep_alive.rs b/pumpkin-protocol/src/client/play/c_keep_alive.rs new file mode 100644 index 000000000..7e9ff9a4c --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_keep_alive.rs @@ -0,0 +1,14 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +#[derive(Serialize)] +#[packet(0x26)] +pub struct CKeepAlive { + id: i64, +} + +impl CKeepAlive { + pub fn new(id: i64) -> Self { + Self { id } + } +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index 90a405280..79b588ead 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -13,6 +13,7 @@ mod c_entity_velocity; mod c_game_event; mod c_head_rot; mod c_hurt_animation; +mod c_keep_alive; mod c_login; mod c_open_screen; mod c_particle; @@ -55,6 +56,7 @@ pub use c_entity_velocity::*; pub use c_game_event::*; pub use c_head_rot::*; pub use c_hurt_animation::*; +pub use c_keep_alive::*; pub use c_login::*; pub use c_open_screen::*; pub use c_particle::*; diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 8b9d8ef82..dda5278c3 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -6,9 +6,12 @@ compile_error!("Compiling for WASI targets is not supported!"); use mio::net::TcpListener; use mio::{Events, Interest, Poll, Token}; +use pumpkin_protocol::client::play::CKeepAlive; +use tokio::time::sleep; use std::collections::HashMap; use std::io::{self}; +use std::time::Duration; use client::{interrupted, Client}; use commands::handle_command; @@ -81,7 +84,10 @@ fn main() -> io::Result<()> { let rcon = ADVANCED_CONFIG.rcon.clone(); let mut clients: HashMap = HashMap::new(); - let mut players: HashMap, Arc>> = HashMap::new(); + let players = Arc::new(tokio::sync::RwLock::new(HashMap::< + Arc, + Arc>, + >::new())); let server = Arc::new(tokio::sync::Mutex::new(Server::new())); log::info!("Started Server took {}ms", time.elapsed().as_millis()); @@ -110,6 +116,28 @@ fn main() -> io::Result<()> { RCONServer::new(&rcon, server).await.unwrap(); }); } + + { + let players = players.clone(); + // Broadcast keep alive packets + // TODO: random id & validate response + tokio::spawn(async move { + loop { + { + let players = players.read().await; + for player in players.values() { + let mut player = player.lock().unwrap(); + if let Err(e) = player.client.try_send_packet(&CKeepAlive::new(0)) { + log::warn!("Failed to send keep alive ({e})"); + } + } + } + // Let lock out of scope so we dont continuously hold it + sleep(Duration::from_secs(15)).await; + } + }); + } + loop { if let Err(err) = poll.poll(&mut events, None) { if interrupted(&err) { @@ -157,11 +185,15 @@ fn main() -> io::Result<()> { token => { // Poll Players + + let mut players = players.write().await; let done = if let Some(player) = players.get_mut(&token) { let mut player = player.lock().unwrap(); player.client.poll(event).await; if !player.client.closed { let mut server = server.lock().await; + + log::debug!("Processing packets"); player.process_packets(&mut server).await; } player.client.closed @@ -172,6 +204,8 @@ fn main() -> io::Result<()> { if done { if let Some(player) = players.remove(&token) { let mut player = player.lock().unwrap(); + let name = &player.gameprofile.name; + log::debug!("Removing player {name}"); player.remove().await; poll.registry().deregister(&mut player.client.connection)?; } @@ -195,10 +229,16 @@ fn main() -> io::Result<()> { if done { poll.registry().deregister(&mut client.connection)?; } else if make_player { + let name = match client.gameprofile.as_ref() { + Some(profile) => &profile.name, + None => "[Unknown]", + }; + log::debug!("Adding player {name}"); let token = client.token.clone(); let mut server = server.lock().await; let (player, world) = server.add_player(token.clone(), client).await; + players.insert(token, player.clone()); let mut world = world.lock().await; world.spawn_player(&BASIC_CONFIG, player).await;