From 2cc64b29e08290bac095ae2c128ac1b62c997222 Mon Sep 17 00:00:00 2001 From: timvisee Date: Sun, 14 Nov 2021 14:48:02 +0100 Subject: [PATCH] Drop obsolete lobby packet, add lobby server warmup, some fixes --- src/lobby.rs | 74 +++++++++++++++++++++------------------------------ src/mc/mod.rs | 3 +++ 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/lobby.rs b/src/lobby.rs index 595ff38..72db440 100644 --- a/src/lobby.rs +++ b/src/lobby.rs @@ -11,7 +11,7 @@ use minecraft_protocol::version::v1_14_4::handshake::Handshake; use minecraft_protocol::version::v1_14_4::login::{LoginStart, LoginSuccess}; use minecraft_protocol::version::v1_17_1::game::{ ClientBoundKeepAlive, JoinGame, PlayerPositionAndLook, PluginMessage, Respawn, - SetTitleSubtitle, SetTitleText, SetTitleTimes, SpawnPosition, TimeUpdate, + SetTitleSubtitle, SetTitleText, SetTitleTimes, TimeUpdate, }; use nbt::CompoundTag; use tokio::io::{self, AsyncWriteExt}; @@ -22,6 +22,7 @@ use tokio::time; use uuid::Uuid; use crate::config::*; +use crate::mc; use crate::proto::{self, Client, ClientInfo, ClientState, RawPacket}; use crate::proxy; use crate::server::{Server, State}; @@ -37,9 +38,21 @@ const SERVER_POLL_INTERVAL: Duration = Duration::from_millis(500); /// Interval to send keep-alive packets at. const KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(10); -/// Minecraft ticks per second. -const TICKS_PER_SECOND: u32 = 20; +/// Time to wait before responding to newly connected server. +/// +/// Notchian servers are slow, we must wait a little before sending play packets, because the +/// server needs time to transition the client into this state. +/// See warning at: https://wiki.vg/Protocol#Login_Success +const SERVER_WARMUP: Duration = Duration::from_secs(1); +/// Server brand to send to client in lobby world. +/// +/// Shown in F3 menu. Updated once client is relayed to real server. +const SERVER_BRAND: &[u8] = b"lazymc"; + +/// Serve lobby service for given client connection. +/// +/// The client must be in the login state, or this will error. // TODO: do not drop error here, return Box // TODO: on error, nicely kick client with message pub async fn serve( @@ -52,8 +65,11 @@ pub async fn serve( ) -> Result<(), ()> { let (mut reader, mut writer) = inbound.split(); - // TODO: note this assumes the first receiving packet (over queue) is login start - // TODO: assert client is in login mode! + // Client must be in login state + if client.state() != ClientState::Login { + error!(target: "lazymc::lobby", "Client reached lobby service with invalid state: {:?}", client.state()); + return Err(()); + } // We must have useful client info if client_info.username.is_none() { @@ -103,12 +119,13 @@ pub async fn serve( // Grab join game packet from server let join_game = wait_for_server_join_game(&mut outbound, &mut server_buf).await?; - // Reset our lobby title - send_lobby_title(&mut writer, "").await?; + // Wait a second because Notchian servers are slow + // See: https://wiki.vg/Protocol#Login_Success + trace!(target: "lazymc::lobby", "Waiting a second before relaying client connection..."); + time::sleep(SERVER_WARMUP).await; - // Send respawn packet, initiates teleport to real server world - // TODO: should we just send one packet? - send_respawn_from_join(&mut writer, join_game.clone()).await?; + // Reset our lobby title, send respawn packet to teleport to real server world + send_lobby_title(&mut writer, "").await?; send_respawn_from_join(&mut writer, join_game).await?; // Drain inbound connection so we don't confuse the server @@ -116,14 +133,8 @@ pub async fn serve( // for some blacklisted ones drain_stream(&mut reader).await?; - // TODO: should we wait a little? - // Wait a little because Notchian servers are slow - // See: https://wiki.vg/Protocol#Login_Success - // trace!(target: "lazymc::lobby", "Waiting a second before relaying client connection..."); - // time::sleep(Duration::from_secs(1)).await; - // Client and server connection ready now, move client to proxy - debug!(target: "lazymc::lobby", "Server connection ready, moving client to proxy"); + debug!(target: "lazymc::lobby", "Server connection ready, relaying lobby client to proxy"); route_proxy(inbound, outbound, server_buf); return Ok(()); @@ -156,13 +167,10 @@ async fn respond_login_success( ) -> Result<(), ()> { let packet = LoginSuccess { uuid: Uuid::new_v3( - // TODO: use Uuid::null() here as namespace? - &Uuid::new_v3(&Uuid::NAMESPACE_OID, b"OfflinePlayer"), + &Uuid::new_v3(&Uuid::nil(), b"OfflinePlayer"), login_start.name.as_bytes(), ), username: login_start.name.clone(), - // uuid: Uuid::parse_str("35ee313b-d89a-41b8-b25e-d32e8aff0389").unwrap(), - // username: "Username".into(), }; let mut data = Vec::new(); @@ -185,8 +193,6 @@ async fn send_lobby_play_packets(writer: &mut WriteHalf<'_>) -> Result<(), ()> { send_lobby_brand(writer).await?; // Send spawn and player position, disables 'download terrain' screen - // TODO: is sending spawn this required? - send_lobby_spawn_pos(writer).await?; send_lobby_player_pos(writer).await?; // Notify client of world time, required once before keep-alive packets @@ -238,11 +244,9 @@ async fn send_lobby_join_game(writer: &mut WriteHalf<'_>) -> Result<(), ()> { /// Send lobby brand to client. async fn send_lobby_brand(writer: &mut WriteHalf<'_>) -> Result<(), ()> { - let brand = b"lazymc".to_vec(); - let packet = PluginMessage { channel: "minecraft:brand".into(), - data: brand, + data: SERVER_BRAND.into(), }; let mut data = Vec::new(); @@ -254,22 +258,6 @@ async fn send_lobby_brand(writer: &mut WriteHalf<'_>) -> Result<(), ()> { Ok(()) } -/// Send lobby spawn position to client. -async fn send_lobby_spawn_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> { - let packet = SpawnPosition { - position: 0, - angle: 0.0, - }; - - let mut data = Vec::new(); - packet.encode(&mut data).map_err(|_| ())?; - - let response = RawPacket::new(proto::packets::play::CLIENT_SPAWN_POS, data).encode()?; - writer.write_all(&response).await.map_err(|_| ())?; - - Ok(()) -} - /// Send lobby player position to client. async fn send_lobby_player_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> { // Send player location, disables download terrain screen @@ -374,7 +362,7 @@ async fn send_lobby_title(writer: &mut WriteHalf<'_>, text: &str) -> Result<(), } else { SetTitleTimes { fade_in: 0, - stay: KEEP_ALIVE_INTERVAL.as_secs() as i32 * TICKS_PER_SECOND as i32 * 2, + stay: KEEP_ALIVE_INTERVAL.as_secs() as i32 * mc::TICKS_PER_SECOND as i32 * 2, fade_out: 0, } }; diff --git a/src/mc/mod.rs b/src/mc/mod.rs index a54afeb..c528f81 100644 --- a/src/mc/mod.rs +++ b/src/mc/mod.rs @@ -1,3 +1,6 @@ #[cfg(feature = "rcon")] pub mod rcon; pub mod server_properties; + +/// Minecraft ticks per second. +pub const TICKS_PER_SECOND: u32 = 20;