use azalea_protocol::{
connect::Connection,
packets::{
ClientIntention,
PROTOCOL_VERSION,
handshake::{
ClientboundHandshakePacket,
ServerboundHandshakePacket,
},
login::{
ServerboundLoginPacket,
c_login_finished::ClientboundLoginFinished,
},
status::{
ServerboundStatusPacket,
c_status_response::{ClientboundStatusResponse, Players, Version},
c_pong_response::ClientboundPongResponse,
},
config::{
ClientboundConfigPacket, ServerboundConfigPacket,
},
},
};
use azalea_auth::game_profile::GameProfile;
use azalea_chat::FormattedText;
use eyre::Result;
use tokio::net::{TcpListener, TcpStream};
use std::sync::Arc;
pub struct LocalServerConfig {
pub bind: String, }
pub struct LocalClient {
pub username: String,
pub uuid: uuid::Uuid,
pub connection: Connection<ServerboundConfigPacket, ClientboundConfigPacket>,
}
pub async fn listen(cfg: &LocalServerConfig) -> Result<TcpListener> {
let listener = TcpListener::bind(&cfg.bind).await?;
tracing::info!("listening for the bot on {}", cfg.bind);
Ok(listener)
}
pub async fn accept_login(stream: TcpStream) -> Result<LocalClient> {
let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
Connection::wrap(stream);
let packet = conn.read().await?;
let intention = match packet {
ServerboundHandshakePacket::Intention(p) => p,
};
if intention.protocol_version != PROTOCOL_VERSION as i32 {
return Err(eyre::eyre!(
"Protocol version mismatch: client has {}, proxy expects {}",
intention.protocol_version,
PROTOCOL_VERSION
));
}
match intention.intention {
ClientIntention::Status => {
let mut conn = conn.status();
let packet = conn.read().await?;
match packet {
ServerboundStatusPacket::StatusRequest(_) => {
conn.write(ClientboundStatusResponse {
description: FormattedText::from("Azalea Reflection Proxy"),
favicon: None,
players: Players {
max: 1,
online: 0,
sample: vec![],
},
version: Version {
name: "1.21.3".to_string(),
protocol: PROTOCOL_VERSION as i32,
},
enforces_secure_chat: None,
}).await?;
}
_ => {}
}
let packet = conn.read().await?;
if let ServerboundStatusPacket::PingRequest(p) = packet {
conn.write(ClientboundPongResponse {
time: p.time,
}).await?;
}
return Err(eyre::eyre!("Client performed status ping and disconnected"));
}
ClientIntention::Login => {
let mut conn = conn.login();
let packet = conn.read().await?;
let (username, profile_id) = match packet {
ServerboundLoginPacket::Hello(p) => (p.name, p.profile_id),
_ => return Err(eyre::eyre!("Expected Hello packet, got {:?}", packet)),
};
let uuid = profile_id;
conn.write(ClientboundLoginFinished {
game_profile: GameProfile {
uuid,
name: username.clone(),
properties: Arc::new(Default::default()),
},
}).await?;
let packet = conn.read().await?;
match packet {
ServerboundLoginPacket::LoginAcknowledged(_) => {
tracing::debug!("Client acknowledged login");
}
_ => return Err(eyre::eyre!("Expected LoginAcknowledged, got {:?}", packet)),
}
let config_conn = conn.config();
Ok(LocalClient {
username,
uuid,
connection: config_conn,
})
}
ClientIntention::Transfer => {
Err(eyre::eyre!("Transfer intention not supported"))
}
}
}