Drop obsolete lobby packet, add lobby server warmup, some fixes

This commit is contained in:
timvisee 2021-11-14 14:48:02 +01:00
parent 802fd2990a
commit 2cc64b29e0
No known key found for this signature in database
GPG Key ID: B8DB720BC383E172
2 changed files with 34 additions and 43 deletions

View File

@ -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<dyn Error>
// 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,
}
};

View File

@ -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;