diff --git a/Cargo.toml b/Cargo.toml index f2b92861..b75cdec3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,6 +154,7 @@ bzip2 = "0.4.1" # CLI clap = "4.5.20" indicatif = "0.17.8" +colored = "2.1.0" # Misc deepsize = "0.2.0" diff --git a/src/bin/src/packet_handlers/login_process.rs b/src/bin/src/packet_handlers/login_process.rs index 437c3fcb..d44638fa 100644 --- a/src/bin/src/packet_handlers/login_process.rs +++ b/src/bin/src/packet_handlers/login_process.rs @@ -8,9 +8,8 @@ use ferrumc_net::packets::incoming::login_acknowledged::LoginAcknowledgedEvent; use ferrumc_net::packets::incoming::login_start::LoginStartEvent; use ferrumc_net::packets::incoming::server_bound_known_packs::ServerBoundKnownPacksEvent; use ferrumc_net::packets::outgoing::client_bound_known_packs::ClientBoundKnownPacksPacket; -use ferrumc_net::packets::outgoing::finish_configuration::FinishConfigurationPacket; use ferrumc_net::packets::outgoing::game_event::GameEventPacket; -use ferrumc_net::packets::outgoing::keep_alive::OutgoingKeepAlivePacket; +use ferrumc_net::packets::outgoing::keep_alive::{KeepAlive, KeepAlivePacket}; use ferrumc_net::packets::outgoing::login_play::LoginPlayPacket; use ferrumc_net::packets::outgoing::login_success::LoginSuccessPacket; use ferrumc_net::packets::outgoing::registry_data::get_registry_packets; @@ -19,6 +18,7 @@ use ferrumc_net::packets::outgoing::synchronize_player_position::SynchronizePlay use ferrumc_net::GlobalState; use ferrumc_net_codec::encode::NetEncodeOpts; use tracing::{debug, trace}; +use ferrumc_net::packets::outgoing::finish_configuration::FinishConfigurationPacket; #[event_handler] async fn handle_login_start( @@ -31,23 +31,19 @@ async fn handle_login_start( let username = login_start_event.login_start_packet.username.as_str(); debug!("Received login start from user with username {}", username); + // Add the player identity component to the ECS for the entity. state.universe.add_component::( login_start_event.conn_id, PlayerIdentity::new(username.to_string(), uuid), )?; - + //Send a Login Success Response to further the login sequence let mut writer = state .universe .get_mut::(login_start_event.conn_id)?; - writer - .send_packet( - &LoginSuccessPacket::new(uuid, username), - &NetEncodeOpts::WithLength, - ) - .await?; + writer.send_packet(&LoginSuccessPacket::new(uuid, username), &NetEncodeOpts::WithLength).await?; Ok(login_start_event) } @@ -66,6 +62,7 @@ async fn handle_login_acknowledged( *connection_state = ConnectionState::Configuration; + // Send packets packet let client_bound_known_packs = ClientBoundKnownPacksPacket::new(); @@ -73,9 +70,7 @@ async fn handle_login_acknowledged( .universe .get_mut::(login_acknowledged_event.conn_id)?; - writer - .send_packet(&client_bound_known_packs, &NetEncodeOpts::WithLength) - .await?; + writer.send_packet(&client_bound_known_packs, &NetEncodeOpts::WithLength).await?; Ok(login_acknowledged_event) } @@ -92,17 +87,10 @@ async fn handle_server_bound_known_packs( .get_mut::(server_bound_known_packs_event.conn_id)?; let registry_packets = get_registry_packets(); - writer - .send_packet(®istry_packets, &NetEncodeOpts::None) - .await?; - - writer - .send_packet( - &FinishConfigurationPacket::new(), - &NetEncodeOpts::WithLength, - ) - .await?; - + writer.send_packet(®istry_packets, &NetEncodeOpts::None).await?; + + writer.send_packet(&FinishConfigurationPacket::new(), &NetEncodeOpts::WithLength).await?; + Ok(server_bound_known_packs_event) } @@ -115,51 +103,34 @@ async fn handle_ack_finish_configuration( let conn_id = ack_finish_configuration_event.conn_id; - let mut conn_state = state.universe.get_mut::(conn_id)?; + let mut conn_state = state + .universe + .get_mut::(conn_id)?; *conn_state = ConnectionState::Play; - let mut writer = state.universe.get_mut::(conn_id)?; - - writer - .send_packet(&LoginPlayPacket::new(conn_id), &NetEncodeOpts::WithLength) - .await?; - writer - .send_packet( - &SetDefaultSpawnPositionPacket::default(), - &NetEncodeOpts::WithLength, - ) - .await?; - writer - .send_packet( - &SynchronizePlayerPositionPacket::default(), - &NetEncodeOpts::WithLength, - ) - .await?; - writer - .send_packet( - &GameEventPacket::start_waiting_for_level_chunks(), - &NetEncodeOpts::WithLength, - ) - .await?; + let mut writer = state + .universe + .get_mut::(conn_id)?; + + writer.send_packet(&LoginPlayPacket::new(conn_id), &NetEncodeOpts::WithLength).await?; + writer.send_packet(&SetDefaultSpawnPositionPacket::default(), &NetEncodeOpts::WithLength).await?; + writer.send_packet(&SynchronizePlayerPositionPacket::default(), &NetEncodeOpts::WithLength).await?; + writer.send_packet(&GameEventPacket::start_waiting_for_level_chunks(), &NetEncodeOpts::WithLength).await?; send_keep_alive(conn_id, state, &mut writer).await?; + Ok(ack_finish_configuration_event) } -async fn send_keep_alive( - conn_id: usize, - state: GlobalState, - writer: &mut ComponentRefMut<'_, StreamWriter>, -) -> Result<(), NetError> { - let keep_alive_packet = OutgoingKeepAlivePacket::default(); - writer - .send_packet(&keep_alive_packet, &NetEncodeOpts::WithLength) - .await?; - - state - .universe - .add_component::(conn_id, keep_alive_packet)?; +async fn send_keep_alive(conn_id: usize, state: GlobalState, writer: &mut ComponentRefMut<'_, StreamWriter>) -> Result<(), NetError> { + let keep_alive_packet = KeepAlivePacket::default(); + writer.send_packet(&keep_alive_packet, &NetEncodeOpts::WithLength).await?; + + let id = keep_alive_packet.id; + + state.universe.add_component::(conn_id, id)?; + Ok(()) -} +} \ No newline at end of file diff --git a/src/bin/src/systems/keep_alive_system.rs b/src/bin/src/systems/keep_alive_system.rs index 4183a1b8..18eafdf9 100644 --- a/src/bin/src/systems/keep_alive_system.rs +++ b/src/bin/src/systems/keep_alive_system.rs @@ -2,8 +2,7 @@ use crate::systems::definition::System; use async_trait::async_trait; use ferrumc_core::identity::player_identity::PlayerIdentity; use ferrumc_net::connection::{ConnectionState, StreamWriter}; -use ferrumc_net::packets::incoming::keep_alive::IncomingKeepAlivePacket; -use ferrumc_net::packets::outgoing::keep_alive::OutgoingKeepAlivePacket; +use ferrumc_net::packets::outgoing::keep_alive::{KeepAlive, KeepAlivePacket}; use ferrumc_net::utils::broadcast::{BroadcastOptions, BroadcastToAll}; use ferrumc_net::GlobalState; use std::sync::atomic::{AtomicBool, Ordering}; @@ -21,7 +20,6 @@ impl KeepAliveSystem { } } } -const FIFTEEN_SECONDS_MS: i64 = 15000; // 15 seconds in milliseconds #[async_trait] impl System for KeepAliveSystem { @@ -31,7 +29,11 @@ impl System for KeepAliveSystem { .duration_since(std::time::UNIX_EPOCH) .expect("Time went backwards") .as_millis() as i64; - while !self.shutdown.load(Ordering::Relaxed) { + loop { + if self.shutdown.load(Ordering::Relaxed) { + break; + } + let online_players = state.universe.query::<&PlayerIdentity>(); let current_time = std::time::SystemTime::now() @@ -44,24 +46,19 @@ impl System for KeepAliveSystem { last_time = current_time; } + let fifteen_seconds_ms = 15000; // 15 seconds in milliseconds + let entities = state .universe - .query::<( - &mut StreamWriter, - &ConnectionState, - &IncomingKeepAlivePacket, - )>() + .query::<(&mut StreamWriter, &ConnectionState, &KeepAlive)>() .into_entities() .into_iter() .filter_map(|entity| { let conn_state = state.universe.get::(entity).ok()?; - let keep_alive = state - .universe - .get_mut::(entity) - .ok()?; + let keep_alive = state.universe.get_mut::(entity).ok()?; if matches!(*conn_state, ConnectionState::Play) - && (current_time - keep_alive.id) >= FIFTEEN_SECONDS_MS + && (current_time - keep_alive.id) >= fifteen_seconds_ms { Some(entity) } else { @@ -71,29 +68,24 @@ impl System for KeepAliveSystem { .collect::>(); if !entities.is_empty() { trace!("there are {:?} players to keep alive", entities.len()); + } - let packet = OutgoingKeepAlivePacket { id: current_time }; + let packet = KeepAlivePacket::default(); - let broadcast_opts = BroadcastOptions::default() - .only(entities) - .with_sync_callback(move |entity, state| { - let Ok(mut outgoing_keep_alive) = - state.universe.get_mut::(entity) - else { - warn!( - "Failed to get component for entity {}", - entity - ); - return; - }; + let broadcast_opts = BroadcastOptions::default() + .only(entities) + .with_sync_callback(move |entity, state| { + let Ok(mut keep_alive) = state.universe.get_mut::(entity) else { + warn!("Failed to get component for entity {}", entity); + return; + }; - *outgoing_keep_alive = OutgoingKeepAlivePacket { id: current_time }; - }); + *keep_alive = KeepAlive::from(current_time); + }); - if let Err(e) = state.broadcast(&packet, broadcast_opts).await { - error!("Error sending keep alive packet: {}", e); - }; - } + if let Err(e) = state.broadcast(&packet, broadcast_opts).await { + error!("Error sending keep alive packet: {}", e); + }; // TODO, this should be configurable as some people may have bad network so the clients may end up disconnecting from the server moments before the keep alive is sent tokio::time::sleep(tokio::time::Duration::from_secs(30)).await; } diff --git a/src/lib/derive_macros/Cargo.toml b/src/lib/derive_macros/Cargo.toml index 9a30f5ba..fb0a2c61 100644 --- a/src/lib/derive_macros/Cargo.toml +++ b/src/lib/derive_macros/Cargo.toml @@ -3,10 +3,15 @@ name = "ferrumc-macros" version = "0.1.0" edition = "2021" +[features] +default = ["colors"] +colors = [] + [lib] proc-macro = true [dependencies] +colored = { workspace = true } quote = { workspace = true } syn = { workspace = true, features = ["full"] } thiserror = { workspace = true } diff --git a/src/lib/derive_macros/src/net/packets/mod.rs b/src/lib/derive_macros/src/net/packets/mod.rs index b0e39c33..548ed5f1 100644 --- a/src/lib/derive_macros/src/net/packets/mod.rs +++ b/src/lib/derive_macros/src/net/packets/mod.rs @@ -3,10 +3,14 @@ use quote::quote; use std::env; use std::ops::Add; use syn::{parse_macro_input, LitInt, LitStr}; +use colored::Colorize; /// Essentially, this just reads all the files in the directory and generates a match arm for each packet. /// (packet_id, state) => { ... } pub fn bake_registry(input: TokenStream) -> TokenStream { + #[cfg(feature = "colors")] + colored::control::set_override(true); + let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); let module_path = parse_macro_input!(input as syn::LitStr).value(); @@ -18,7 +22,11 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let base_path = module_path.split("\\").collect::>()[2..].join("::"); let base_path = format!("crate::{}", base_path); - println!("[FERRUMC_MACROS] Parsing packets in {}", dir_path.display()); + println!( + " {} {}", + "[FERRUMC_MACROS]".blue().bold(), + format!("Parsing packets in {}", dir_path.display()).white().bold() + ); if !std::fs::metadata(dir_path).unwrap().is_dir() { return TokenStream::from(quote! { @@ -90,17 +98,20 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { let struct_name = &item_struct.ident; println!( - "[FERRUMC_MACROS] Found Packet (ID: 0x{:02X}, State: {}, Struct Name: {})", - packet_id, state, struct_name + " {} {} (ID: {}, State: {}, Struct Name: {})", + "[FERRUMC_MACROS]".bold().blue(), + "Found Packet".white().bold(), + format!("0x{:02X}", packet_id).cyan(), + state.green(), + struct_name.to_string().yellow() ); - + let path = format!( // "crate::net::packets::incoming::{}", "{}::{}", base_path, file_name.to_string_lossy().replace(".rs", "") ); - let struct_path = format!("{}::{}", path, struct_name); let struct_path = syn::parse_str::(&struct_path).expect("parse_str failed"); @@ -119,10 +130,15 @@ pub fn bake_registry(input: TokenStream) -> TokenStream { } let elapsed = start.elapsed(); - println!("[FERRUMC_MACROS] Found {} packets", match_arms.len()); println!( - "[FERRUMC_MACROS] It took: {:?} to parse all the files and generate the packet registry", - elapsed + " {} {}", + "[FERRUMC_MACROS]".bold().blue(), + format!("Found {} packets", match_arms.len()).purple().bold() + ); + println!( + " {} {}", + "[FERRUMC_MACROS]".bold().blue(), + format!("It took: {:?} to parse all the files and generate the packet registry", elapsed).red().bold() ); let match_arms = match_arms.into_iter(); diff --git a/src/lib/net/src/packets/incoming/keep_alive.rs b/src/lib/net/src/packets/incoming/keep_alive.rs index 6f4c9491..44d33746 100644 --- a/src/lib/net/src/packets/incoming/keep_alive.rs +++ b/src/lib/net/src/packets/incoming/keep_alive.rs @@ -1,11 +1,9 @@ -use crate::packets::outgoing::keep_alive::OutgoingKeepAlivePacket; +use crate::packets::outgoing::keep_alive::KeepAlive; use crate::packets::IncomingPacket; use crate::{NetResult, ServerState}; -use ferrumc_ecs::components::storage::ComponentRefMut; -use ferrumc_ecs::errors::ECSError; use ferrumc_macros::{packet, NetDecode}; use std::sync::Arc; -use tracing::{debug, warn}; +use tracing::debug; #[derive(NetDecode)] #[packet(packet_id = 0x18, state = "play")] @@ -15,17 +13,12 @@ pub struct IncomingKeepAlivePacket { impl IncomingPacket for IncomingKeepAlivePacket { async fn handle(self, conn_id: usize, state: Arc) -> NetResult<()> { - // TODO handle errors. - let last_keep_alive = state.universe.get_mut::(conn_id)?; - + let mut last_keep_alive = state.universe.get_mut::(conn_id)?; if self.id != last_keep_alive.id { debug!( - "Invalid keep alive packet received from entity {:?} with id {:?} (expected {:?})", + "Invalid keep alive packet received from {:?} with id {:?} (expected {:?})", conn_id, self.id, last_keep_alive.id ); - return NetResult::Err(crate::errors::NetError::Packet( - crate::errors::PacketError::InvalidState(0x18), - )); // TODO Kick player } @@ -50,11 +43,9 @@ impl IncomingPacket for IncomingKeepAlivePacket { ); return Err(crate::errors::NetError::ECSError(result.err().unwrap())); } - } else { - let mut last_received_keep_alive: ComponentRefMut<'_, IncomingKeepAlivePacket> = - result.unwrap(); - *last_received_keep_alive = self; + } else { + *last_keep_alive = KeepAlive::from(self.id); } Ok(()) diff --git a/src/lib/net/src/packets/outgoing/keep_alive.rs b/src/lib/net/src/packets/outgoing/keep_alive.rs index 41b0aa81..23a36b77 100644 --- a/src/lib/net/src/packets/outgoing/keep_alive.rs +++ b/src/lib/net/src/packets/outgoing/keep_alive.rs @@ -1,18 +1,39 @@ use ferrumc_macros::{packet, NetEncode}; use std::io::Write; +#[derive(Debug, NetEncode)] +pub struct KeepAlive { + pub id: i64, +} + +mod adapters { + impl From for super::KeepAlive { + fn from(id: i64) -> Self { + Self { id } + } + } +} + #[derive(NetEncode)] #[packet(packet_id = 0x26)] -pub struct OutgoingKeepAlivePacket { - pub id: i64, +pub struct KeepAlivePacket { + pub id: KeepAlive, } -impl Default for OutgoingKeepAlivePacket { +impl Default for KeepAlivePacket { fn default() -> Self { let current_ms = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("Time went backwards?? LMAO") .as_millis() as i64; - Self { id: current_ms } + Self::new(current_ms) + } +} + +impl KeepAlivePacket { + pub fn new(id: i64) -> Self { + Self { + id: KeepAlive::from(id), + } } }