Send client protocol & username to server from lobby, send server brand
This commit is contained in:
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -684,7 +684,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "minecraft-protocol"
|
name = "minecraft-protocol"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?branch=lazymc-v1_17_1#a4fc2bcf7bced11fa255c371d8d5c780157ecbb4"
|
source = "git+https://github.com/timvisee/rust-minecraft-protocol?branch=lazymc-v1_17_1#bef4fa8c009857e3753d85d8e2df1f71c3ae08f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"minecraft-protocol-derive",
|
"minecraft-protocol-derive",
|
||||||
@@ -697,7 +697,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "minecraft-protocol-derive"
|
name = "minecraft-protocol-derive"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?branch=lazymc-v1_17_1#a4fc2bcf7bced11fa255c371d8d5c780157ecbb4"
|
source = "git+https://github.com/timvisee/rust-minecraft-protocol?branch=lazymc-v1_17_1#bef4fa8c009857e3753d85d8e2df1f71c3ae08f6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
78
src/lobby.rs
78
src/lobby.rs
@@ -17,8 +17,8 @@ use minecraft_protocol::version::v1_14_4::login::{LoginDisconnect, LoginStart, L
|
|||||||
use minecraft_protocol::version::v1_14_4::status::StatusResponse;
|
use minecraft_protocol::version::v1_14_4::status::StatusResponse;
|
||||||
use minecraft_protocol::version::v1_17_1::game::{
|
use minecraft_protocol::version::v1_17_1::game::{
|
||||||
ChunkData, ClientBoundChatMessage, ClientBoundKeepAlive, GameDisconnect, JoinGame,
|
ChunkData, ClientBoundChatMessage, ClientBoundKeepAlive, GameDisconnect, JoinGame,
|
||||||
PlayerPositionAndLook, Respawn, SetTitleSubtitle, SetTitleText, SetTitleTimes, SpawnPosition,
|
PlayerPositionAndLook, PluginMessage, Respawn, SetTitleSubtitle, SetTitleText, SetTitleTimes,
|
||||||
TimeUpdate,
|
SpawnPosition, TimeUpdate,
|
||||||
};
|
};
|
||||||
use nbt::CompoundTag;
|
use nbt::CompoundTag;
|
||||||
use tokio::io::{self, AsyncWriteExt};
|
use tokio::io::{self, AsyncWriteExt};
|
||||||
@@ -29,7 +29,7 @@ use tokio::time;
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use crate::proto::{self, Client, ClientState, RawPacket};
|
use crate::proto::{self, Client, ClientInfo, ClientState, RawPacket};
|
||||||
use crate::proxy;
|
use crate::proxy;
|
||||||
use crate::server::{self, Server, State};
|
use crate::server::{self, Server, State};
|
||||||
use crate::service;
|
use crate::service;
|
||||||
@@ -53,6 +53,7 @@ const TICKS_PER_SECOND: u32 = 20;
|
|||||||
// TODO: on error, nicely kick client with message
|
// TODO: on error, nicely kick client with message
|
||||||
pub async fn serve(
|
pub async fn serve(
|
||||||
client: Client,
|
client: Client,
|
||||||
|
client_info: ClientInfo,
|
||||||
mut inbound: TcpStream,
|
mut inbound: TcpStream,
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
server: Arc<Server>,
|
server: Arc<Server>,
|
||||||
@@ -63,6 +64,12 @@ pub async fn serve(
|
|||||||
// TODO: note this assumes the first receiving packet (over queue) is login start
|
// TODO: note this assumes the first receiving packet (over queue) is login start
|
||||||
// TODO: assert client is in login mode!
|
// TODO: assert client is in login mode!
|
||||||
|
|
||||||
|
// We must have useful client info
|
||||||
|
if client_info.username.is_none() {
|
||||||
|
error!(target: "lazymc::lobby", "Client username is unknown, closing connection");
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
// Incoming buffer and packet holding queue
|
// Incoming buffer and packet holding queue
|
||||||
let mut inbound_buf = queue;
|
let mut inbound_buf = queue;
|
||||||
let mut server_queue = BytesMut::new();
|
let mut server_queue = BytesMut::new();
|
||||||
@@ -99,7 +106,8 @@ pub async fn serve(
|
|||||||
|
|
||||||
// Wait for server to come online, then set up new connection to it
|
// Wait for server to come online, then set up new connection to it
|
||||||
stage_wait(config.clone(), server.clone(), &mut writer).await?;
|
stage_wait(config.clone(), server.clone(), &mut writer).await?;
|
||||||
let (mut outbound, mut server_buf) = connect_to_server(client, &config, server).await?;
|
let (mut outbound, mut server_buf) =
|
||||||
|
connect_to_server(client, client_info, &config, server).await?;
|
||||||
|
|
||||||
// Grab join game packet from server
|
// Grab join game packet from server
|
||||||
let join_game = wait_for_server_join_game(&mut outbound, &mut server_buf).await?;
|
let join_game = wait_for_server_join_game(&mut outbound, &mut server_buf).await?;
|
||||||
@@ -137,36 +145,8 @@ pub async fn serve(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this ever called?
|
// TODO: when receiving Login Plugin Request, respond with empty payload
|
||||||
if client_state == ClientState::Play
|
// See: https://wiki.vg/Protocol#Login_Plugin_Request
|
||||||
&& packet.id == proto::packets::play::SERVER_CLIENT_SETTINGS
|
|
||||||
{
|
|
||||||
debug!(target: "lazymc::lobby", "Ignoring client settings packet");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this ever called?
|
|
||||||
if client_state == ClientState::Play
|
|
||||||
&& packet.id == proto::packets::play::SERVER_PLUGIN_MESSAGE
|
|
||||||
{
|
|
||||||
debug!(target: "lazymc::lobby", "Ignoring plugin message packet");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this ever called?
|
|
||||||
if client_state == ClientState::Play
|
|
||||||
&& packet.id == proto::packets::play::SERVER_PLAYER_POS_ROT
|
|
||||||
{
|
|
||||||
debug!(target: "lazymc::lobby", "Ignoring player pos rot packet");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: is this ever called?
|
|
||||||
if client_state == ClientState::Play && packet.id == proto::packets::play::SERVER_PLAYER_POS
|
|
||||||
{
|
|
||||||
debug!(target: "lazymc::lobby", "Ignoring player pos packet");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show unhandled packet warning
|
// Show unhandled packet warning
|
||||||
debug!(target: "lazymc", "Received unhandled packet:");
|
debug!(target: "lazymc", "Received unhandled packet:");
|
||||||
@@ -232,7 +212,9 @@ async fn send_lobby_play_packets(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
|||||||
// Send initial game join
|
// Send initial game join
|
||||||
send_lobby_join_game(writer).await?;
|
send_lobby_join_game(writer).await?;
|
||||||
|
|
||||||
// TODO: send plugin message for brand
|
// Send server brand
|
||||||
|
// TODO: does client ever receive real brand after this?
|
||||||
|
send_lobby_brand(writer).await?;
|
||||||
|
|
||||||
// Send spawn and player position, disables 'download terrain' screen
|
// Send spawn and player position, disables 'download terrain' screen
|
||||||
// TODO: is sending spawn this required?
|
// TODO: is sending spawn this required?
|
||||||
@@ -286,6 +268,24 @@ async fn send_lobby_join_game(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data = Vec::new();
|
||||||
|
packet.encode(&mut data).map_err(|_| ())?;
|
||||||
|
|
||||||
|
let response = RawPacket::new(proto::packets::play::CLIENT_PLUGIN_MESSAGE, data).encode()?;
|
||||||
|
writer.write_all(&response).await.map_err(|_| ())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Send lobby spawn position to client.
|
/// Send lobby spawn position to client.
|
||||||
async fn send_lobby_spawn_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
async fn send_lobby_spawn_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||||
let packet = SpawnPosition {
|
let packet = SpawnPosition {
|
||||||
@@ -537,6 +537,7 @@ async fn wait_for_server<'a>(config: Arc<Config>, server: Arc<Server>) -> Result
|
|||||||
// TODO: clean this up
|
// TODO: clean this up
|
||||||
async fn connect_to_server(
|
async fn connect_to_server(
|
||||||
real_client: Client,
|
real_client: Client,
|
||||||
|
client_info: ClientInfo,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
server: Arc<Server>,
|
server: Arc<Server>,
|
||||||
) -> Result<(TcpStream, BytesMut), ()> {
|
) -> Result<(TcpStream, BytesMut), ()> {
|
||||||
@@ -551,9 +552,9 @@ async fn connect_to_server(
|
|||||||
let tmp_client = Client::default();
|
let tmp_client = Client::default();
|
||||||
tmp_client.set_state(ClientState::Login);
|
tmp_client.set_state(ClientState::Login);
|
||||||
|
|
||||||
// TODO: use client version
|
// Handshake packet
|
||||||
let packet = Handshake {
|
let packet = Handshake {
|
||||||
protocol_version: 755,
|
protocol_version: client_info.protocol_version.unwrap(),
|
||||||
server_addr: config.server.address.ip().to_string(),
|
server_addr: config.server.address.ip().to_string(),
|
||||||
server_port: config.server.address.port(),
|
server_port: config.server.address.port(),
|
||||||
next_state: ClientState::Login.to_id(),
|
next_state: ClientState::Login.to_id(),
|
||||||
@@ -566,9 +567,8 @@ async fn connect_to_server(
|
|||||||
writer.write_all(&request).await.map_err(|_| ())?;
|
writer.write_all(&request).await.map_err(|_| ())?;
|
||||||
|
|
||||||
// Request login start
|
// Request login start
|
||||||
// TODO: use client username
|
|
||||||
let packet = LoginStart {
|
let packet = LoginStart {
|
||||||
name: "timvisee".into(),
|
name: client_info.username.ok_or(())?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut data = Vec::new();
|
let mut data = Vec::new();
|
||||||
|
17
src/proto.rs
17
src/proto.rs
@@ -53,6 +53,7 @@ pub mod packets {
|
|||||||
pub const CLIENT_CHAT_MSG: i32 = 0x0F;
|
pub const CLIENT_CHAT_MSG: i32 = 0x0F;
|
||||||
pub const CLIENT_SPAWN_POS: i32 = 0x4B;
|
pub const CLIENT_SPAWN_POS: i32 = 0x4B;
|
||||||
pub const CLIENT_DISCONNECT: i32 = 0x1A;
|
pub const CLIENT_DISCONNECT: i32 = 0x1A;
|
||||||
|
pub const CLIENT_PLUGIN_MESSAGE: i32 = 0x18;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +126,22 @@ impl Default for ClientState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Client info, useful during connection handling.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct ClientInfo {
|
||||||
|
/// Client protocol version.
|
||||||
|
pub protocol_version: Option<i32>,
|
||||||
|
|
||||||
|
/// Client username.
|
||||||
|
pub username: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientInfo {
|
||||||
|
pub fn empty() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Raw Minecraft packet.
|
/// Raw Minecraft packet.
|
||||||
///
|
///
|
||||||
/// Having a packet ID and a raw data byte array.
|
/// Having a packet ID and a raw data byte array.
|
||||||
|
@@ -17,7 +17,7 @@ use tokio::time;
|
|||||||
|
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use crate::lobby;
|
use crate::lobby;
|
||||||
use crate::proto::{self, Client, ClientState, RawPacket};
|
use crate::proto::{self, Client, ClientInfo, ClientState, RawPacket};
|
||||||
use crate::server::{self, Server, State};
|
use crate::server::{self, Server, State};
|
||||||
use crate::service;
|
use crate::service;
|
||||||
|
|
||||||
@@ -39,6 +39,8 @@ pub async fn serve(
|
|||||||
|| config.join.methods.contains(&Method::Forward);
|
|| config.join.methods.contains(&Method::Forward);
|
||||||
let mut inbound_history = BytesMut::new();
|
let mut inbound_history = BytesMut::new();
|
||||||
|
|
||||||
|
let mut client_info = ClientInfo::empty();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Read packet from stream
|
// Read packet from stream
|
||||||
let (packet, raw) = match proto::read_packet(&mut buf, &mut reader).await {
|
let (packet, raw) = match proto::read_packet(&mut buf, &mut reader).await {
|
||||||
@@ -55,22 +57,28 @@ pub async fn serve(
|
|||||||
|
|
||||||
// Hijack handshake
|
// Hijack handshake
|
||||||
if client_state == ClientState::Handshake && packet.id == proto::STATUS_PACKET_ID_STATUS {
|
if client_state == ClientState::Handshake && packet.id == proto::STATUS_PACKET_ID_STATUS {
|
||||||
// Parse handshake, grab new state
|
// Parse handshake
|
||||||
let new_state = match Handshake::decode(&mut packet.data.as_slice()) {
|
let handshake = match Handshake::decode(&mut packet.data.as_slice()) {
|
||||||
Ok(handshake) => match ClientState::from_id(handshake.next_state) {
|
Ok(handshake) => handshake,
|
||||||
Some(state) => state,
|
|
||||||
None => {
|
|
||||||
error!(target: "lazymc", "Client tried to switch into unknown protcol state ({}), disconnecting", handshake.next_state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
debug!(target: "lazymc", "Got malformed handshake from client, disconnecting");
|
debug!(target: "lazymc", "Got malformed handshake from client, disconnecting");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update client state
|
// Parse new state
|
||||||
|
let new_state = match ClientState::from_id(handshake.next_state) {
|
||||||
|
Some(state) => state,
|
||||||
|
None => {
|
||||||
|
error!(target: "lazymc", "Client tried to switch into unknown protcol state ({}), disconnecting", handshake.next_state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update client info and client state
|
||||||
|
client_info
|
||||||
|
.protocol_version
|
||||||
|
.replace(handshake.protocol_version);
|
||||||
client.set_state(new_state);
|
client.set_state(new_state);
|
||||||
|
|
||||||
// If login handshake and holding is enabled, hold packets
|
// If login handshake and holding is enabled, hold packets
|
||||||
@@ -103,10 +111,12 @@ pub async fn serve(
|
|||||||
|
|
||||||
// Hijack login start
|
// Hijack login start
|
||||||
if client_state == ClientState::Login && packet.id == proto::LOGIN_PACKET_ID_LOGIN_START {
|
if client_state == ClientState::Login && packet.id == proto::LOGIN_PACKET_ID_LOGIN_START {
|
||||||
// Try to get login username
|
// Try to get login username, update client info
|
||||||
|
// TODO: we should always parse this packet successfully
|
||||||
let username = LoginStart::decode(&mut packet.data.as_slice())
|
let username = LoginStart::decode(&mut packet.data.as_slice())
|
||||||
.ok()
|
.ok()
|
||||||
.map(|p| p.name);
|
.map(|p| p.name);
|
||||||
|
client_info.username = username.clone();
|
||||||
|
|
||||||
// Kick if lockout is enabled
|
// Kick if lockout is enabled
|
||||||
if config.lockout.enabled {
|
if config.lockout.enabled {
|
||||||
@@ -137,7 +147,7 @@ pub async fn serve(
|
|||||||
queue.extend(buf.split_off(0));
|
queue.extend(buf.split_off(0));
|
||||||
|
|
||||||
// Start lobby
|
// Start lobby
|
||||||
lobby::serve(client, inbound, config, server, queue).await?;
|
lobby::serve(client, client_info, inbound, config, server, queue).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user