Remove obsolete file

This commit is contained in:
timvisee 2021-11-14 14:07:21 +01:00
parent c9290827be
commit 32317a4c2f
No known key found for this signature in database
GPG Key ID: B8DB720BC383E172

View File

@ -1,953 +0,0 @@
// TODO: remove this before feature release!
#![allow(unused)]
use std::sync::Arc;
use std::time::{Duration, Instant};
use bytes::BytesMut;
use futures::FutureExt;
use minecraft_protocol::data::chat::{Message, Payload};
use minecraft_protocol::data::server_status::*;
use minecraft_protocol::decoder::Decoder;
use minecraft_protocol::encoder::Encoder;
use minecraft_protocol::version::v1_14_4::game::{GameMode, MessagePosition};
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
use minecraft_protocol::version::v1_14_4::login::{LoginDisconnect, LoginStart, LoginSuccess};
use minecraft_protocol::version::v1_14_4::status::StatusResponse;
use minecraft_protocol::version::v1_17_1::game::{
ChunkData, ClientBoundChatMessage, ClientBoundKeepAlive, GameDisconnect, JoinGame,
PlayerPositionAndLook, Respawn, SetTitleSubtitle, SetTitleText, SetTitleTimes, SpawnPosition,
TimeUpdate,
};
use nbt::CompoundTag;
use tokio::io::{self, AsyncWriteExt};
use tokio::net::tcp::{ReadHalf, WriteHalf};
use tokio::net::TcpStream;
use tokio::time;
use uuid::Uuid;
use crate::config::*;
use crate::proto::{self, Client, ClientState, RawPacket};
use crate::proxy;
use crate::server::{self, Server, State};
use crate::service;
// TODO: remove this before releasing feature
pub const USE_LOBBY: bool = true;
pub const DONT_START_SERVER: bool = false;
const STARTING_BANNER: &str = "§2Server is starting";
const STARTING_BANNER_SUB: &str = "§7⌛ Please wait...";
/// Client holding server state poll interval.
const HOLD_POLL_INTERVAL: Duration = Duration::from_secs(1);
// TODO: do not drop error here, return Box<dyn Error>
pub async fn serve(
client: Client,
mut inbound: TcpStream,
config: Arc<Config>,
server: Arc<Server>,
queue: BytesMut,
) -> 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!
// Incoming buffer and packet holding queue
let mut buf = queue;
let mut server_queue = BytesMut::new();
loop {
// Read packet from stream
let (packet, raw) = match proto::read_packet(&mut buf, &mut reader).await {
Ok(Some(packet)) => packet,
Ok(None) => break,
Err(_) => {
error!(target: "lazymc", "Closing connection, error occurred");
break;
}
};
// Grab client state
let client_state = client.state();
// Hijack login start
if client_state == ClientState::Login && packet.id == proto::LOGIN_PACKET_ID_LOGIN_START {
// Try to get login username
let login_start = LoginStart::decode(&mut packet.data.as_slice()).map_err(|_| ())?;
// TODO: remove debug message
debug!(target: "LOBBY", "Login {:?}", login_start.name);
// Respond with login success
let packet = LoginSuccess {
// TODO: use correct username here
uuid: Uuid::new_v3(
&Uuid::new_v3(&Uuid::NAMESPACE_OID, b"OfflinePlayer"),
login_start.name.as_bytes(),
),
username: login_start.name,
// uuid: Uuid::parse_str("35ee313b-d89a-41b8-b25e-d32e8aff0389").unwrap(),
// username: "Username".into(),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::LOGIN_PACKET_ID_LOGIN_SUCCESS, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
// Update client state to play
client.set_state(ClientState::Play);
// TODO: remove debug message
debug!(target: "LOBBY", "Sent login success, moving to play state");
// TODO: handle errors here
play_packets(&mut writer).await;
send_keep_alive(&mut writer).await?;
send_title(&mut writer).await?;
// Wait for server to come online
wait_for_server(config.clone(), server.clone(), &mut writer).await?;
// Connect to server
let (mut outbound, client_queue) = connect_to_server(client, &config, server).await?;
// Wait for join game packet
// TODO: do something with this excess buffer
let (join_game, client_buf) = wait_for_server_join_game(&mut outbound).await?;
dbg!(join_game.entity_id);
send_title_reset(&mut writer).await?;
// Send respawn apcket
send_respawn(&mut writer, join_game.clone()).await?;
send_respawn(&mut writer, join_game).await?;
if !client_buf.is_empty() {
// TODO: handle this
// TODO: remove error message
error!(target: "LOBBY", "Got excess data from server for client! ({} bytes)", client_buf.len());
}
// Drain inbound
// TODO: do not drain, send packets to server, except keep-alive
drain_stream(&mut reader).await?;
// TODO: route any following packets to client
// Wait a little because Notchian servers are slow
// See: https://wiki.vg/Protocol#Login_Success
// TODO: improve this
// debug!(target: "LOBBY", "Waiting 2 sec for server");
// time::sleep(Duration::from_secs(2)).await;
debug!(target: "LOBBY", "Moving client to proxy");
route_proxy_queue(inbound, outbound, config, client_queue, server_queue);
return Ok(());
}
if client_state == ClientState::Play
&& packet.id == proto::packets::play::SERVER_CLIENT_SETTINGS
{
debug!(target: "LOBBY", "Ignoring client settings packet");
continue;
}
if client_state == ClientState::Play
&& packet.id == proto::packets::play::SERVER_PLUGIN_MESSAGE
{
debug!(target: "LOBBY", "Ignoring plugin message packet");
continue;
}
if client_state == ClientState::Play
&& packet.id == proto::packets::play::SERVER_PLAYER_POS_ROT
{
debug!(target: "LOBBY", "Ignoring player pos rot packet");
continue;
}
if client_state == ClientState::Play && packet.id == proto::packets::play::SERVER_PLAYER_POS
{
debug!(target: "LOBBY", "Ignoring player pos packet");
continue;
}
// Show unhandled packet warning
debug!(target: "lazymc", "Received unhandled packet:");
debug!(target: "lazymc", "- State: {:?}", client_state);
debug!(target: "lazymc", "- Packet ID: 0x{:02X} ({})", packet.id, packet.id);
}
// Gracefully close connection
match writer.shutdown().await {
Ok(_) => {}
Err(err) if err.kind() == io::ErrorKind::NotConnected => {}
Err(_) => return Err(()),
}
Ok(())
}
/// Kick client with a message.
///
/// Should close connection afterwards.
async fn kick(msg: &str, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
let packet = LoginDisconnect {
reason: Message::new(Payload::text(msg)),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::LOGIN_PACKET_ID_DISCONNECT, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())
}
async fn play_packets(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
debug!(target: "LOBBY", "Send play packets");
// See: https://wiki.vg/Protocol_FAQ#What.27s_the_normal_login_sequence_for_a_client.3F
// Send game join
send_join_game(writer).await?;
// TODO: send brand plugin message
// After this, we receive:
// - PLAY_PAKCET_ID_CLIENT_SETTINGS
// - PLAY_PAKCET_ID_PLUGIN_MESSAGE
// - PLAY_PAKCET_ID_PLAYER_POS_ROT
// - PLAY_PAKCET_ID_PLAYER_POS ...
// TODO: send Update View Position ?
// TODO: send Update View Distance ?
// Send chunk data
// TODO: send_chunk_data(writer).await?;
// TODO: probably not required
send_spawn_pos(writer).await?;
// Send player location, disables download terrain screen
send_player_pos(writer).await?;
// TODO: send Update View Position
// TODO: send Spawn Position
// TODO: send Position and Look (one more time)
// Send time update
send_time_update(writer).await?;
// Keep sending keep alive packets
send_keep_alive(writer).await?;
Ok(())
}
async fn send_join_game(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// // TODO: use proper values here!
// let packet = JoinGame {
// // entity_id: 0,
// // game_mode: GameMode::Spectator,
// entity_id: 27,
// game_mode: GameMode::Hardcore,
// dimension: 23,
// max_players: 100,
// level_type: String::from("default"),
// view_distance: 10,
// reduced_debug_info: true,
// };
// TODO: use proper values here!
let packet = JoinGame {
// entity_id: 0x6d,
// ID must not collide with anything existing
// entity_id: 1337133700,
entity_id: 0,
hardcore: false,
game_mode: 3,
previous_game_mode: -1i8 as u8, // use -1i8 as u8?
world_names: vec![
"minecraft:overworld".into(),
"minecraft:the_nether".into(),
"minecraft:the_end".into(),
],
dimension_codec: snbt_to_compound_tag(include_str!("../res/dimension_codec.snbt")),
dimension: snbt_to_compound_tag(include_str!("../res/dimension.snbt")),
// TODO: is this ok?
// world_name: "minecraft:overworld".into(),
world_name: "lazymc:lobby".into(),
hashed_seed: 0,
max_players: 20,
view_distance: 10,
reduced_debug_info: true,
enable_respawn_screen: false,
is_debug: false,
is_flat: false,
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_JOIN_GAME, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
Ok(())
}
async fn send_respawn(writer: &mut WriteHalf<'_>, join_game: JoinGame) -> Result<(), ()> {
// TODO: use proper values here!
let packet = Respawn {
dimension: join_game.dimension,
world_name: join_game.world_name,
hashed_seed: join_game.hashed_seed,
game_mode: join_game.game_mode,
previous_game_mode: join_game.previous_game_mode,
is_debug: join_game.is_debug,
is_flat: join_game.is_flat,
copy_metadata: false,
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_RESPAWN, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
Ok(())
}
// // TODO: this is possibly broken?
// async fn send_chunk_data(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// // Send player location, disables download terrain screen
// let packet = ChunkData {
// x: 0,
// z: 0,
// primary_mask: Vec::new(),
// heightmaps: CompoundTag::named("HeightMaps"),
// biomes: Vec::new(),
// data_size: 0,
// data: Vec::new(),
// block_entities_size: 0,
// block_entities: Vec::new(),
// // primary_mask: 65535,
// // heights: CompoundTag::named("HeightMaps"),
// // data: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
// // tiles: vec![CompoundTag::named("TileEntity")],
// };
// let mut data = Vec::new();
// packet.encode(&mut data).map_err(|_| ())?;
// let response = RawPacket::new(proto::CLIENT_CHUNK_DATA, data).encode()?;
// writer.write_all(&response).await.map_err(|_| ())?;
// Ok(())
// }
async fn send_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(())
}
async fn send_player_pos(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// Send player location, disables download terrain screen
let packet = PlayerPositionAndLook {
x: 0.0,
y: 0.0,
z: 0.0,
yaw: 0.0,
pitch: 90.0,
flags: 0b00000000,
teleport_id: 0,
dismount_vehicle: true,
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_PLAYER_POS_LOOK, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
Ok(())
}
async fn send_time_update(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
const MC_TIME_NOON: i64 = 6000;
// Send player location, disables download terrain screen
let packet = TimeUpdate {
world_age: MC_TIME_NOON,
time_of_day: MC_TIME_NOON,
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_TIME_UPDATE, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
Ok(())
}
async fn send_keep_alive(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// Send player location, disables download terrain screen
// TODO: keep picking random ID!
let packet = ClientBoundKeepAlive { id: 0 };
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_KEEP_ALIVE, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
// TODO: require to receive correct keepalive!
Ok(())
}
async fn send_keep_alive_loop(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// TODO: use interval of 10 sec?
let mut poll_interval = time::interval(Duration::from_secs(10));
loop {
// TODO: wait for start signal over channel instead of polling
poll_interval.tick().await;
debug!(target: "LOBBY", "Sending keep-alive to client");
send_keep_alive(writer).await?;
}
Ok(())
}
async fn send_title(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// Set title
let packet = SetTitleText {
text: Message::new(Payload::text(STARTING_BANNER)),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_TEXT, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
// Set subtitle
let packet = SetTitleSubtitle {
text: Message::new(Payload::text(STARTING_BANNER_SUB)),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response =
RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_SUBTITLE, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
// Set times
// TODO: do not make this longer than 2x keep alive packet interval
let packet = SetTitleTimes {
fade_in: 0,
stay: i32::MAX,
fade_out: 0,
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_TIMES, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
Ok(())
}
async fn send_title_reset(writer: &mut WriteHalf<'_>) -> Result<(), ()> {
// Set title
let packet = SetTitleText {
text: Message::new(Payload::text("")),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_TEXT, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
// Set subtitle
let packet = SetTitleSubtitle {
text: Message::new(Payload::text("")),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response =
RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_SUBTITLE, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
// Set times
// TODO: do not make this longer than 2x keep alive packet interval
let packet = SetTitleTimes {
fade_in: 10,
stay: 100,
fade_out: 10,
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let response = RawPacket::new(proto::packets::play::CLIENT_SET_TITLE_TIMES, data).encode()?;
writer.write_all(&response).await.map_err(|_| ())?;
Ok(())
}
// TODO: go through this, use proper error messages
pub async fn wait_for_server<'a>(
config: Arc<Config>,
server: Arc<Server>,
writer: &mut WriteHalf<'a>,
) -> Result<(), ()> {
debug!(target: "lazymc", "Waiting on server...");
// Set up polling interval, get timeout
let mut poll_interval = time::interval(HOLD_POLL_INTERVAL);
let since = Instant::now();
let timeout = config.time.hold_client_for as u64;
loop {
// TODO: wait for start signal over channel instead of polling
poll_interval.tick().await;
trace!("Poloutboundng server state for holding client...");
// TODO: shouldn't do this here
send_keep_alive(writer).await?;
match server.state() {
// Still waiting on server start
State::Starting => {
trace!(target: "lazymc", "Server not ready, holding client for longer");
// TODO: timeout
// // If hold timeout is reached, kick client
// if since.elapsed().as_secs() >= timeout {
// warn!(target: "lazymc", "Held client reached timeout of {}s, disconnecting", timeout);
// kick(&config.messages.login_starting, &mut inbound.split().1).await?;
// return Ok(());
// }
continue;
}
// Server started, start relaying and proxy
State::Started => {
// TODO: drop client if already disconnected
// // Relay client to proxy
// info!(target: "lazymc", "Server ready for held client, relaying to server");
// service::server::route_proxy_queue(inbound, config, hold_queue);
info!(target: "lazymc", "Server ready for lobby client, connecting to server");
return Ok(());
}
// Server stopping, this shouldn't happen, kick
State::Stopping => {
// TODO: kick message
// warn!(target: "lazymc", "Server stopping for held client, disconnecting");
// kick(&config.messages.login_stopping, &mut inbound.split().1).await?;
break;
}
// Server stopped, this shouldn't happen, disconnect
State::Stopped => {
error!(target: "lazymc", "Server stopped for held client, disconnecting");
break;
}
}
}
Err(())
}
pub async fn connect_to_server(
real_client: Client,
config: &Config,
server: Arc<Server>,
) -> Result<(TcpStream, BytesMut), ()> {
// Set up connection to server
// TODO: on connect fail, ping server and redirect to serve_status if offline
let mut outbound = TcpStream::connect(config.server.address)
.await
.map_err(|_| ())?;
let (mut reader, mut writer) = outbound.split();
let tmp_client = Client::default();
tmp_client.set_state(ClientState::Login);
// TODO: use client version
let packet = Handshake {
protocol_version: 755,
server_addr: config.server.address.ip().to_string(),
server_port: config.server.address.port(),
next_state: ClientState::Login.to_id(),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let request = RawPacket::new(proto::HANDSHAKE_PACKET_ID_HANDSHAKE, data).encode()?;
writer.write_all(&request).await.map_err(|_| ())?;
// Request login start
// TODO: use client username
let packet = LoginStart {
name: "timvisee".into(),
};
let mut data = Vec::new();
packet.encode(&mut data).map_err(|_| ())?;
let request = RawPacket::new(proto::LOGIN_PACKET_ID_LOGIN_START, data).encode()?;
writer.write_all(&request).await.map_err(|_| ())?;
// # Wait for server responses
// Incoming buffer and packet holding queue
let mut buf = BytesMut::new();
let mut client_queue = BytesMut::new();
let mut server_queue = BytesMut::new();
loop {
// Read packet from stream
let (packet, raw) = match proto::read_packet(&mut buf, &mut reader).await {
Ok(Some(packet)) => packet,
Ok(None) => break,
Err(_) => {
error!(target: "lazymc", "Closing connection, error occurred");
break;
}
};
// Grab client state
let client_state = tmp_client.state();
// Hijack login success
if client_state == ClientState::Login && packet.id == proto::LOGIN_PACKET_ID_LOGIN_SUCCESS {
debug!(target: "LOBBY", "Login success received");
// TODO: fix reading this packet
// let login_success =
// LoginSuccess::decode(&mut packet.data.as_slice()).map_err(|err| {
// dbg!(err);
// ()
// })?;
// // TODO: remove debug message
// debug!(target: "LOBBY", "Login success: {:?}", login_success.username);
// Switch to play state
tmp_client.set_state(ClientState::Play);
debug!(target: "LOBBY", "Server connection ready");
// TODO: also return buf!
assert!(
buf.is_empty(),
"server incomming buf not empty, will lose data"
);
return Ok((outbound, client_queue));
}
// // Hijack join game
// if client_state == ClientState::Play && packet.id == proto::packets::play::CLIENT_JOIN_GAME
// {
// // We must receive join game packet
// // TODO: also parse packet!
// // TODO: remove debug message
// debug!(target: "LOBBY", "Received join packet, will relay to client");
// // TODO: send join game packet?
// debug!(target: "LOBBY", "client_queue + {} bytes", raw.len());
// client_queue.extend(raw);
// // TODO: real client and tmp_client must match state
// return Ok((outbound, client_queue));
// }
// TODO: // Hijack join game
// TODO: // TODO: client_state == ClientState::Play &&
// TODO: if packet.id == proto::packets::play::CLIENT_JOIN_GAME {
// TODO: let join_game = JoinGame::decode(&mut packet.data.as_slice()).map_err(|_| ())?;
// TODO: // TODO: remove debug message
// TODO: debug!(target: "LOBBY", "GOT JOIN GAME!");
// TODO: continue;
// TODO: }
// // Hijack disconnect message
// // TODO: remove this?
// if packet.id == proto::packets::play::CLIENT_DISCONNECT {
// let disconnect = GameDisconnect::decode(&mut packet.data.as_slice()).map_err(|_| ())?;
// debug!(target: "LOBBY", "DISCONNECT REASON: {:?}", disconnect.reason);
// continue;
// }
// // Grab client state
// let client_state = client.state();
// // Hijack login start
// if client_state == ClientState::Login && packet.id == proto::LOGIN_PACKET_ID_LOGIN_START {
// // Try to get login username
// let login_start = LoginStart::decode(&mut packet.data.as_slice()).map_err(|_| ())?;
// // TODO: remove debug message
// debug!(target: "LOBBY", "Login {:?}", login_start.name);
// // Respond with login success
// let packet = LoginSuccess {
// // TODO: use correct username here
// uuid: Uuid::new_v3(
// &Uuid::new_v3(&Uuid::NAMESPACE_OID, b"OfflinePlayer"),
// login_start.name.as_bytes(),
// ),
// username: login_start.name,
// // uuid: Uuid::parse_str("35ee313b-d89a-41b8-b25e-d32e8aff0389").unwrap(),
// // username: "Username".into(),
// };
// let mut data = Vec::new();
// packet.encode(&mut data).map_err(|_| ())?;
// let response = RawPacket::new(proto::LOGIN_PACKET_ID_LOGIN_SUCCESS, data).encode()?;
// writer.write_all(&response).await.map_err(|_| ())?;
// // Update client state to play
// client.set_state(ClientState::Play);
// // TODO: remove debug message
// debug!(target: "LOBBY", "Sent login success, moving to play state");
// // TODO: handle errors here
// play_packets(&mut writer).await;
// send_keep_alive(&mut writer).await?;
// // Wait for server to come online
// wait_for_server(config.clone(), server.clone()).await?;
// // Connect to server
// connect_to_server(client, config, server).await?;
// // Keep sending keep alive packets
// debug!(target: "LOBBY", "Keep sending keep-alive now...");
// send_keep_alive_loop(&mut writer).await?;
// debug!(target: "LOBBY", "Done with playing packets, client disconnect?");
// return Ok(());
// }
// if client_state == ClientState::Play
// && packet.id == proto::packets::play::SERVER_CLIENT_SETTINGS
// {
// debug!(target: "LOBBY", "Ignoring client settings packet");
// continue;
// }
// if client_state == ClientState::Play
// && packet.id == proto::packets::play::SERVER_PLUGIN_MESSAGE
// {
// debug!(target: "LOBBY", "Ignoring plugin message packet");
// continue;
// }
// if client_state == ClientState::Play
// && packet.id == proto::packets::play::SERVER_PLAYER_POS_ROT
// {
// debug!(target: "LOBBY", "Ignoring player pos rot packet");
// continue;
// }
// if client_state == ClientState::Play && packet.id == proto::packets::play::SERVER_PLAYER_POS
// {
// debug!(target: "LOBBY", "Ignoring player pos packet");
// continue;
// }
// Show unhandled packet warning
debug!(target: "lazymc", "Received unhandled packet:");
debug!(target: "lazymc", "- State: {:?}", client_state);
debug!(target: "lazymc", "- Packet ID: 0x{:02X} ({})", packet.id, packet.id);
}
// TODO: we should receive login success from server
// TODO: we should receive join game packet, relay this to client
// // Gracefully close connection
// match writer.shutdown().await {
// Ok(_) => {}
// Err(err) if err.kind() == io::ErrorKind::NotConnected => {}
// Err(_) => return Err(()),
// }
// We only reach this on errors
// TODO: do we actually ever reach this?
Err(())
}
// TODO: remove unused fields
// TODO: do not drop error here, return Box<dyn Error>
// TODO: add timeout
pub async fn wait_for_server_join_game(
// client: Client,
mut outbound: &mut TcpStream,
// config: Arc<Config>,
// server: Arc<Server>,
// queue: BytesMut,
) -> Result<(JoinGame, BytesMut), ()> {
let (mut reader, mut writer) = outbound.split();
// TODO: note this assumes the first receiving packet (over queue) is login start
// TODO: assert client is in login mode!
// Incoming buffer and packet holding queue
let mut buf = BytesMut::new();
loop {
// Read packet from stream
let (packet, raw) = match proto::read_packet(&mut buf, &mut reader).await {
Ok(Some(packet)) => packet,
Ok(None) => break,
Err(_) => {
error!(target: "lazymc", "Closing connection, error occurred");
break;
}
};
// Hijack login start
if packet.id == proto::packets::play::CLIENT_JOIN_GAME {
let join_game = JoinGame::decode(&mut packet.data.as_slice()).map_err(|err| {
// TODO: remove this debug
dbg!(err);
()
})?;
// TODO: remove debug message
debug!(target: "LOBBY", "GOT JOIN FROM SERVER");
return Ok((join_game, buf));
}
// Show unhandled packet warning
debug!(target: "lazymc", "Received unhandled packet:");
// debug!(target: "lazymc", "- State: {:?}", client_state);
debug!(target: "lazymc", "- Packet ID: 0x{:02X} ({})", packet.id, packet.id);
}
// Gracefully close connection
match writer.shutdown().await {
Ok(_) => {}
Err(err) if err.kind() == io::ErrorKind::NotConnected => {}
Err(_) => return Err(()),
}
// TODO: will we ever reach this?
Err(())
}
// TODO: update name and description
/// Route inbound TCP stream to proxy with queued data, spawning a new task.
#[inline]
pub fn route_proxy_queue(
inbound: TcpStream,
outbound: TcpStream,
config: Arc<Config>,
client_queue: BytesMut,
server_queue: BytesMut,
) {
// When server is online, proxy all
let service = async move {
proxy::proxy_inbound_outbound_with_queue(inbound, outbound, &client_queue, &server_queue)
.map(|r| {
if let Err(err) = r {
warn!(target: "lazymc", "Failed to proxy: {}", err);
}
// TODO: remove after debug
debug!(target: "LOBBY", "Done with playing packets, client disconnect?");
})
.await
};
tokio::spawn(service);
}
// TODO: go through this, use proper error messages
pub async fn drain_stream<'a>(reader: &mut ReadHalf<'a>) -> Result<(), ()> {
// TODO: remove after debug
debug!(target: "lazymc", "Draining stream...");
// TODO: use other size?
let mut drain_buf = [0; 1024];
loop {
match reader.try_read(&mut drain_buf) {
// TODO: stop if read < drain_buf.len() ?
Ok(read) if read == 0 => return Ok(()),
Ok(read) => continue,
Err(err) => {
// TODO: remove after debug
dbg!("drain err", err);
return Ok(());
}
}
}
}
/// Read NBT CompoundTag from SNBT.
fn snbt_to_compound_tag(data: &str) -> CompoundTag {
use nbt::decode::read_compound_tag;
use quartz_nbt::io::{self, Flavor};
use quartz_nbt::snbt;
use std::io::Cursor;
// Parse SNBT data
let compound = snbt::parse(data).expect("failed to parse SNBT");
// Encode to binary
let mut binary = Vec::new();
io::write_nbt(&mut binary, None, &compound, Flavor::Uncompressed);
// Parse binary with usable NBT create
read_compound_tag(&mut &*binary).unwrap()
}