pub struct Connection<R: ProtocolPacket, W: ProtocolPacket> {
pub reader: ReadConnection<R>,
pub writer: WriteConnection<W>,
}Expand description
A connection that can read and write packets.
§Examples
Join an offline-mode server and go through the handshake.
use azalea_protocol::{
connect::Connection,
packets::{
self, ClientIntention, PROTOCOL_VERSION,
handshake::ServerboundIntention,
login::{ClientboundLoginPacket, ServerboundHello, ServerboundKey},
},
resolver,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let resolved_address = resolver::resolve_address(&"localhost".try_into().unwrap()).await?;
let mut conn = Connection::new(&resolved_address).await?;
// handshake
conn.write(ServerboundIntention {
protocol_version: PROTOCOL_VERSION,
hostname: resolved_address.ip().to_string(),
port: resolved_address.port(),
intention: ClientIntention::Login,
})
.await?;
let mut conn = conn.login();
// login
conn.write(ServerboundHello {
name: "bot".to_owned(),
profile_id: uuid::Uuid::nil(),
})
.await?;
let (conn, game_profile) = loop {
let packet = conn.read().await?;
match packet {
ClientboundLoginPacket::Hello(p) => {
let e = azalea_crypto::encrypt(&p.public_key, &p.challenge).unwrap();
conn.write(ServerboundKey {
key_bytes: e.encrypted_public_key,
encrypted_challenge: e.encrypted_challenge,
})
.await?;
conn.set_encryption_key(e.secret_key);
}
ClientboundLoginPacket::LoginCompression(p) => {
conn.set_compression_threshold(p.compression_threshold);
}
ClientboundLoginPacket::LoginFinished(p) => {
break (conn.config(), p.game_profile);
}
ClientboundLoginPacket::LoginDisconnect(p) => {
eprintln!("login disconnect: {}", p.reason);
return Err("login disconnect".into());
}
ClientboundLoginPacket::CustomQuery(p) => {}
ClientboundLoginPacket::CookieRequest(p) => {
conn.write(packets::login::ServerboundCookieResponse {
key: p.key,
payload: None,
})
.await?;
}
}
};
Ok(())
}Fields§
§reader: ReadConnection<R>§writer: WriteConnection<W>Implementations§
Source§impl<R, W> Connection<R, W>
impl<R, W> Connection<R, W>
Sourcepub async fn read(&mut self) -> Result<R, Box<ReadPacketError>>
pub async fn read(&mut self) -> Result<R, Box<ReadPacketError>>
Read a packet from the other side of the connection.
Examples found in repository?
67async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
68 stream.set_nodelay(true)?;
69 let ip = stream.peer_addr()?;
70 let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
71 Connection::wrap(stream);
72
73 // The first packet sent from a client is the intent packet.
74 // This specifies whether the client is pinging
75 // the server or is going to join the game.
76 let intent = match conn.read().await {
77 Ok(packet) => match packet {
78 ServerboundHandshakePacket::Intention(packet) => {
79 info!(
80 "New connection from {}, hostname {:?}:{}, version {}, {:?}",
81 ip.ip(),
82 packet.hostname,
83 packet.port,
84 packet.protocol_version,
85 packet.intention
86 );
87 packet
88 }
89 },
90 Err(e) => {
91 let e = e.into();
92 warn!("Error during intent: {e}");
93 return Err(e);
94 }
95 };
96
97 match intent.intention {
98 // If the client is pinging the proxy,
99 // reply with the information below.
100 ClientIntention::Status => {
101 let mut conn = conn.status();
102 loop {
103 match conn.read().await {
104 Ok(p) => match p {
105 ServerboundStatusPacket::StatusRequest(_) => {
106 conn.write(ClientboundStatusResponse {
107 description: PROXY_DESC.into(),
108 favicon: PROXY_FAVICON.clone(),
109 players: PROXY_PLAYERS.clone(),
110 version: PROXY_VERSION.clone(),
111 enforces_secure_chat: PROXY_SECURE_CHAT,
112 })
113 .await?;
114 }
115 ServerboundStatusPacket::PingRequest(p) => {
116 conn.write(ClientboundPongResponse { time: p.time }).await?;
117 break;
118 }
119 },
120 Err(e) => match *e {
121 ReadPacketError::ConnectionClosed => {
122 break;
123 }
124 e => {
125 warn!("Error during status: {e}");
126 return Err(e.into());
127 }
128 },
129 }
130 }
131 }
132 // If the client intends to join the proxy,
133 // wait for them to send the `Hello` packet to
134 // log their username and uuid, then forward the
135 // connection along to the proxy target.
136 ClientIntention::Login => {
137 let mut conn = conn.login();
138 loop {
139 match conn.read().await {
140 Ok(p) => {
141 if let ServerboundLoginPacket::Hello(hello) = p {
142 info!(
143 "Player \'{0}\' from {1} logging in with uuid: {2}",
144 hello.name,
145 ip.ip(),
146 hello.profile_id.to_string()
147 );
148
149 tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
150 if let Err(e) = r {
151 error!("Failed to proxy: {e}");
152 }
153 }));
154
155 break;
156 }
157 }
158 Err(e) => match *e {
159 ReadPacketError::ConnectionClosed => {
160 break;
161 }
162 e => {
163 warn!("Error during login: {e}");
164 return Err(e.into());
165 }
166 },
167 }
168 }
169 }
170 ClientIntention::Transfer => {
171 warn!("Client attempted to join via transfer")
172 }
173 }
174
175 Ok(())
176}Sourcepub fn try_read(&mut self) -> Result<Option<R>, Box<ReadPacketError>>
pub fn try_read(&mut self) -> Result<Option<R>, Box<ReadPacketError>>
Try to read a packet from the other side of the connection, or return Ok(None) if there’s no packet to read.
Sourcepub async fn write(&mut self, packet: impl Packet<W>) -> Result<()>
pub async fn write(&mut self, packet: impl Packet<W>) -> Result<()>
Write a packet to the other side of the connection.
Examples found in repository?
67async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
68 stream.set_nodelay(true)?;
69 let ip = stream.peer_addr()?;
70 let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
71 Connection::wrap(stream);
72
73 // The first packet sent from a client is the intent packet.
74 // This specifies whether the client is pinging
75 // the server or is going to join the game.
76 let intent = match conn.read().await {
77 Ok(packet) => match packet {
78 ServerboundHandshakePacket::Intention(packet) => {
79 info!(
80 "New connection from {}, hostname {:?}:{}, version {}, {:?}",
81 ip.ip(),
82 packet.hostname,
83 packet.port,
84 packet.protocol_version,
85 packet.intention
86 );
87 packet
88 }
89 },
90 Err(e) => {
91 let e = e.into();
92 warn!("Error during intent: {e}");
93 return Err(e);
94 }
95 };
96
97 match intent.intention {
98 // If the client is pinging the proxy,
99 // reply with the information below.
100 ClientIntention::Status => {
101 let mut conn = conn.status();
102 loop {
103 match conn.read().await {
104 Ok(p) => match p {
105 ServerboundStatusPacket::StatusRequest(_) => {
106 conn.write(ClientboundStatusResponse {
107 description: PROXY_DESC.into(),
108 favicon: PROXY_FAVICON.clone(),
109 players: PROXY_PLAYERS.clone(),
110 version: PROXY_VERSION.clone(),
111 enforces_secure_chat: PROXY_SECURE_CHAT,
112 })
113 .await?;
114 }
115 ServerboundStatusPacket::PingRequest(p) => {
116 conn.write(ClientboundPongResponse { time: p.time }).await?;
117 break;
118 }
119 },
120 Err(e) => match *e {
121 ReadPacketError::ConnectionClosed => {
122 break;
123 }
124 e => {
125 warn!("Error during status: {e}");
126 return Err(e.into());
127 }
128 },
129 }
130 }
131 }
132 // If the client intends to join the proxy,
133 // wait for them to send the `Hello` packet to
134 // log their username and uuid, then forward the
135 // connection along to the proxy target.
136 ClientIntention::Login => {
137 let mut conn = conn.login();
138 loop {
139 match conn.read().await {
140 Ok(p) => {
141 if let ServerboundLoginPacket::Hello(hello) = p {
142 info!(
143 "Player \'{0}\' from {1} logging in with uuid: {2}",
144 hello.name,
145 ip.ip(),
146 hello.profile_id.to_string()
147 );
148
149 tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
150 if let Err(e) = r {
151 error!("Failed to proxy: {e}");
152 }
153 }));
154
155 break;
156 }
157 }
158 Err(e) => match *e {
159 ReadPacketError::ConnectionClosed => {
160 break;
161 }
162 e => {
163 warn!("Error during login: {e}");
164 return Err(e.into());
165 }
166 },
167 }
168 }
169 }
170 ClientIntention::Transfer => {
171 warn!("Client attempted to join via transfer")
172 }
173 }
174
175 Ok(())
176}
177
178async fn transfer(
179 mut inbound: TcpStream,
180 intent: ServerboundIntention,
181 hello: ServerboundHello,
182) -> Result<(), Box<dyn Error>> {
183 let outbound = TcpStream::connect(PROXY_ADDR).await?;
184 let name = hello.name.clone();
185 outbound.set_nodelay(true)?;
186
187 // Repeat the intent and hello packet
188 // received earlier to the proxy target
189 let mut outbound_conn: Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> =
190 Connection::wrap(outbound);
191 outbound_conn.write(intent).await?;
192
193 let mut outbound_conn = outbound_conn.login();
194 outbound_conn.write(hello).await?;
195
196 let mut outbound = outbound_conn.unwrap()?;
197
198 // Split the incoming and outgoing connections in
199 // halves and handle each pair on separate threads.
200 let (mut ri, mut wi) = inbound.split();
201 let (mut ro, mut wo) = outbound.split();
202
203 let client_to_server = async {
204 io::copy(&mut ri, &mut wo).await?;
205 wo.shutdown().await
206 };
207
208 let server_to_client = async {
209 io::copy(&mut ro, &mut wi).await?;
210 wi.shutdown().await
211 };
212
213 tokio::try_join!(client_to_server, server_to_client)?;
214 info!("Player \'{name}\' left the game");
215
216 Ok(())
217}Sourcepub fn into_split(self) -> (ReadConnection<R>, WriteConnection<W>)
pub fn into_split(self) -> (ReadConnection<R>, WriteConnection<W>)
Split the reader and writer into two objects.
This doesn’t allocate.
Sourcepub fn into_split_raw(self) -> (RawReadConnection, RawWriteConnection)
pub fn into_split_raw(self) -> (RawReadConnection, RawWriteConnection)
Split the reader and writer into the state-agnostic
RawReadConnection and RawWriteConnection types.
This is meant to help with some types of proxies.
Source§impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket>
impl Connection<ClientboundHandshakePacket, ServerboundHandshakePacket>
Sourcepub async fn new(address: &SocketAddr) -> Result<Self, ConnectionError>
pub async fn new(address: &SocketAddr) -> Result<Self, ConnectionError>
Create a new connection to the given address.
Sourcepub async fn new_with_proxy(
address: &SocketAddr,
proxy: Proxy,
) -> Result<Self, ConnectionError>
pub async fn new_with_proxy( address: &SocketAddr, proxy: Proxy, ) -> Result<Self, ConnectionError>
Create a new connection to the given address and SOCKS5 proxy.
If you’re not using a proxy, use Self::new instead.
Sourcepub async fn new_from_stream(stream: TcpStream) -> Result<Self, ConnectionError>
pub async fn new_from_stream(stream: TcpStream) -> Result<Self, ConnectionError>
Create a new connection from an existing stream.
Useful if you want to set custom options on the stream. Otherwise, just
use Self::new.
Sourcepub fn login(self) -> Connection<ClientboundLoginPacket, ServerboundLoginPacket>
pub fn login(self) -> Connection<ClientboundLoginPacket, ServerboundLoginPacket>
Change our state from handshake to login.
This is the state that is used for logging in.
Examples found in repository?
178async fn transfer(
179 mut inbound: TcpStream,
180 intent: ServerboundIntention,
181 hello: ServerboundHello,
182) -> Result<(), Box<dyn Error>> {
183 let outbound = TcpStream::connect(PROXY_ADDR).await?;
184 let name = hello.name.clone();
185 outbound.set_nodelay(true)?;
186
187 // Repeat the intent and hello packet
188 // received earlier to the proxy target
189 let mut outbound_conn: Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> =
190 Connection::wrap(outbound);
191 outbound_conn.write(intent).await?;
192
193 let mut outbound_conn = outbound_conn.login();
194 outbound_conn.write(hello).await?;
195
196 let mut outbound = outbound_conn.unwrap()?;
197
198 // Split the incoming and outgoing connections in
199 // halves and handle each pair on separate threads.
200 let (mut ri, mut wi) = inbound.split();
201 let (mut ro, mut wo) = outbound.split();
202
203 let client_to_server = async {
204 io::copy(&mut ri, &mut wo).await?;
205 wo.shutdown().await
206 };
207
208 let server_to_client = async {
209 io::copy(&mut ro, &mut wi).await?;
210 wi.shutdown().await
211 };
212
213 tokio::try_join!(client_to_server, server_to_client)?;
214 info!("Player \'{name}\' left the game");
215
216 Ok(())
217}Sourcepub fn status(
self,
) -> Connection<ClientboundStatusPacket, ServerboundStatusPacket>
pub fn status( self, ) -> Connection<ClientboundStatusPacket, ServerboundStatusPacket>
Change our state from handshake to status.
This is the state that is used for pinging the server.
Source§impl Connection<ClientboundLoginPacket, ServerboundLoginPacket>
impl Connection<ClientboundLoginPacket, ServerboundLoginPacket>
Sourcepub fn set_compression_threshold(&mut self, threshold: i32)
pub fn set_compression_threshold(&mut self, threshold: i32)
Set our compression threshold, i.e. the maximum size that a packet is allowed to be without getting compressed.
Setting it to 0 means every packet will be compressed. If you set it to less than 0 then compression is disabled.
Sourcepub fn set_encryption_key(&mut self, key: [u8; 16])
pub fn set_encryption_key(&mut self, key: [u8; 16])
Set the encryption key that is used to encrypt and decrypt packets.
It’s the same for both reading and writing.
Sourcepub fn config(
self,
) -> Connection<ClientboundConfigPacket, ServerboundConfigPacket>
pub fn config( self, ) -> Connection<ClientboundConfigPacket, ServerboundConfigPacket>
Change our state from login to configuration.
This is the state where the server sends us the registries and the resource pack.
Sourcepub async fn authenticate(
&self,
access_token: &str,
uuid: &Uuid,
private_key: [u8; 16],
packet: &ClientboundHello,
sessionserver_proxy: Option<Proxy>,
) -> Result<(), ClientSessionServerError>
pub async fn authenticate( &self, access_token: &str, uuid: &Uuid, private_key: [u8; 16], packet: &ClientboundHello, sessionserver_proxy: Option<Proxy>, ) -> Result<(), ClientSessionServerError>
Authenticate with Minecraft’s servers, which is required to join online-mode servers.
This must happen when you get a ClientboundLoginPacket::Hello packet.
§Examples
use azalea_auth::AuthResult;
use azalea_protocol::{
connect::Connection,
packets::login::{ClientboundLoginPacket, ServerboundKey},
};
use uuid::Uuid;
let AuthResult {
access_token,
profile,
} = azalea_auth::auth("[email protected]", azalea_auth::AuthOpts::default())
.await
.expect("Couldn't authenticate");
let mut conn = Connection::new(&resolved_address).await?;
// transition to the login state, in a real program we would have done a handshake first
let mut conn = conn.login();
match conn.read().await? {
ClientboundLoginPacket::Hello(p) => {
// tell Mojang we're joining the server & enable encryption
let e = azalea_crypto::encrypt(&p.public_key, &p.challenge).unwrap();
conn.authenticate(&access_token, &profile.id, e.secret_key, &p, None)
.await?;
conn.write(ServerboundKey {
key_bytes: e.encrypted_public_key,
encrypted_challenge: e.encrypted_challenge,
})
.await?;
conn.set_encryption_key(e.secret_key);
}
_ => {}
}Source§impl Connection<ServerboundHandshakePacket, ClientboundHandshakePacket>
impl Connection<ServerboundHandshakePacket, ClientboundHandshakePacket>
Sourcepub fn login(self) -> Connection<ServerboundLoginPacket, ClientboundLoginPacket>
pub fn login(self) -> Connection<ServerboundLoginPacket, ClientboundLoginPacket>
Change our state from handshake to login.
This is the state that is used while negotiating encryption and authenticating with Mojang.
Examples found in repository?
67async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
68 stream.set_nodelay(true)?;
69 let ip = stream.peer_addr()?;
70 let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
71 Connection::wrap(stream);
72
73 // The first packet sent from a client is the intent packet.
74 // This specifies whether the client is pinging
75 // the server or is going to join the game.
76 let intent = match conn.read().await {
77 Ok(packet) => match packet {
78 ServerboundHandshakePacket::Intention(packet) => {
79 info!(
80 "New connection from {}, hostname {:?}:{}, version {}, {:?}",
81 ip.ip(),
82 packet.hostname,
83 packet.port,
84 packet.protocol_version,
85 packet.intention
86 );
87 packet
88 }
89 },
90 Err(e) => {
91 let e = e.into();
92 warn!("Error during intent: {e}");
93 return Err(e);
94 }
95 };
96
97 match intent.intention {
98 // If the client is pinging the proxy,
99 // reply with the information below.
100 ClientIntention::Status => {
101 let mut conn = conn.status();
102 loop {
103 match conn.read().await {
104 Ok(p) => match p {
105 ServerboundStatusPacket::StatusRequest(_) => {
106 conn.write(ClientboundStatusResponse {
107 description: PROXY_DESC.into(),
108 favicon: PROXY_FAVICON.clone(),
109 players: PROXY_PLAYERS.clone(),
110 version: PROXY_VERSION.clone(),
111 enforces_secure_chat: PROXY_SECURE_CHAT,
112 })
113 .await?;
114 }
115 ServerboundStatusPacket::PingRequest(p) => {
116 conn.write(ClientboundPongResponse { time: p.time }).await?;
117 break;
118 }
119 },
120 Err(e) => match *e {
121 ReadPacketError::ConnectionClosed => {
122 break;
123 }
124 e => {
125 warn!("Error during status: {e}");
126 return Err(e.into());
127 }
128 },
129 }
130 }
131 }
132 // If the client intends to join the proxy,
133 // wait for them to send the `Hello` packet to
134 // log their username and uuid, then forward the
135 // connection along to the proxy target.
136 ClientIntention::Login => {
137 let mut conn = conn.login();
138 loop {
139 match conn.read().await {
140 Ok(p) => {
141 if let ServerboundLoginPacket::Hello(hello) = p {
142 info!(
143 "Player \'{0}\' from {1} logging in with uuid: {2}",
144 hello.name,
145 ip.ip(),
146 hello.profile_id.to_string()
147 );
148
149 tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
150 if let Err(e) = r {
151 error!("Failed to proxy: {e}");
152 }
153 }));
154
155 break;
156 }
157 }
158 Err(e) => match *e {
159 ReadPacketError::ConnectionClosed => {
160 break;
161 }
162 e => {
163 warn!("Error during login: {e}");
164 return Err(e.into());
165 }
166 },
167 }
168 }
169 }
170 ClientIntention::Transfer => {
171 warn!("Client attempted to join via transfer")
172 }
173 }
174
175 Ok(())
176}Sourcepub fn status(
self,
) -> Connection<ServerboundStatusPacket, ClientboundStatusPacket>
pub fn status( self, ) -> Connection<ServerboundStatusPacket, ClientboundStatusPacket>
Change our state from handshake to status.
This is the state that is used for pinging the server.
Examples found in repository?
67async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
68 stream.set_nodelay(true)?;
69 let ip = stream.peer_addr()?;
70 let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
71 Connection::wrap(stream);
72
73 // The first packet sent from a client is the intent packet.
74 // This specifies whether the client is pinging
75 // the server or is going to join the game.
76 let intent = match conn.read().await {
77 Ok(packet) => match packet {
78 ServerboundHandshakePacket::Intention(packet) => {
79 info!(
80 "New connection from {}, hostname {:?}:{}, version {}, {:?}",
81 ip.ip(),
82 packet.hostname,
83 packet.port,
84 packet.protocol_version,
85 packet.intention
86 );
87 packet
88 }
89 },
90 Err(e) => {
91 let e = e.into();
92 warn!("Error during intent: {e}");
93 return Err(e);
94 }
95 };
96
97 match intent.intention {
98 // If the client is pinging the proxy,
99 // reply with the information below.
100 ClientIntention::Status => {
101 let mut conn = conn.status();
102 loop {
103 match conn.read().await {
104 Ok(p) => match p {
105 ServerboundStatusPacket::StatusRequest(_) => {
106 conn.write(ClientboundStatusResponse {
107 description: PROXY_DESC.into(),
108 favicon: PROXY_FAVICON.clone(),
109 players: PROXY_PLAYERS.clone(),
110 version: PROXY_VERSION.clone(),
111 enforces_secure_chat: PROXY_SECURE_CHAT,
112 })
113 .await?;
114 }
115 ServerboundStatusPacket::PingRequest(p) => {
116 conn.write(ClientboundPongResponse { time: p.time }).await?;
117 break;
118 }
119 },
120 Err(e) => match *e {
121 ReadPacketError::ConnectionClosed => {
122 break;
123 }
124 e => {
125 warn!("Error during status: {e}");
126 return Err(e.into());
127 }
128 },
129 }
130 }
131 }
132 // If the client intends to join the proxy,
133 // wait for them to send the `Hello` packet to
134 // log their username and uuid, then forward the
135 // connection along to the proxy target.
136 ClientIntention::Login => {
137 let mut conn = conn.login();
138 loop {
139 match conn.read().await {
140 Ok(p) => {
141 if let ServerboundLoginPacket::Hello(hello) = p {
142 info!(
143 "Player \'{0}\' from {1} logging in with uuid: {2}",
144 hello.name,
145 ip.ip(),
146 hello.profile_id.to_string()
147 );
148
149 tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
150 if let Err(e) = r {
151 error!("Failed to proxy: {e}");
152 }
153 }));
154
155 break;
156 }
157 }
158 Err(e) => match *e {
159 ReadPacketError::ConnectionClosed => {
160 break;
161 }
162 e => {
163 warn!("Error during login: {e}");
164 return Err(e.into());
165 }
166 },
167 }
168 }
169 }
170 ClientIntention::Transfer => {
171 warn!("Client attempted to join via transfer")
172 }
173 }
174
175 Ok(())
176}Source§impl Connection<ServerboundLoginPacket, ClientboundLoginPacket>
impl Connection<ServerboundLoginPacket, ClientboundLoginPacket>
Sourcepub fn set_compression_threshold(&mut self, threshold: i32)
pub fn set_compression_threshold(&mut self, threshold: i32)
Set our compression threshold, i.e. the maximum size that a packet is allowed to be without getting compressed.
If you set it to less than 0 then compression gets disabled.
Sourcepub fn set_encryption_key(&mut self, key: [u8; 16])
pub fn set_encryption_key(&mut self, key: [u8; 16])
Set the encryption key that is used to encrypt and decrypt packets.
It’s the same for both reading and writing.
Sourcepub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket>
pub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket>
Change our state from login to game.
This is the state that’s used when the client is actually in the game.
Sourcepub async fn authenticate(
&self,
username: &str,
public_key: &[u8],
private_key: &[u8; 16],
ip: Option<&str>,
) -> Result<GameProfile, ServerSessionServerError>
pub async fn authenticate( &self, username: &str, public_key: &[u8], private_key: &[u8; 16], ip: Option<&str>, ) -> Result<GameProfile, ServerSessionServerError>
Verify connecting clients have authenticated with Minecraft’s servers.
This must happen after the client sends a ServerboundLoginPacket::Key
packet.
Sourcepub fn config(
self,
) -> Connection<ServerboundConfigPacket, ClientboundConfigPacket>
pub fn config( self, ) -> Connection<ServerboundConfigPacket, ClientboundConfigPacket>
Change our state back to configuration.
Source§impl Connection<ServerboundConfigPacket, ClientboundConfigPacket>
impl Connection<ServerboundConfigPacket, ClientboundConfigPacket>
Sourcepub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket>
pub fn game(self) -> Connection<ServerboundGamePacket, ClientboundGamePacket>
Change our state from configuration to game.
This is the state that’s used when the client is actually in the world.
Source§impl Connection<ClientboundConfigPacket, ServerboundConfigPacket>
impl Connection<ClientboundConfigPacket, ServerboundConfigPacket>
Sourcepub fn game(self) -> Connection<ClientboundGamePacket, ServerboundGamePacket>
pub fn game(self) -> Connection<ClientboundGamePacket, ServerboundGamePacket>
Change our state from configuration to game.
This is the state that’s used when the client is actually in the world.
Source§impl Connection<ClientboundGamePacket, ServerboundGamePacket>
impl Connection<ClientboundGamePacket, ServerboundGamePacket>
Sourcepub fn config(
self,
) -> Connection<ClientboundConfigPacket, ServerboundConfigPacket>
pub fn config( self, ) -> Connection<ClientboundConfigPacket, ServerboundConfigPacket>
Change our state back to configuration.
Source§impl Connection<ServerboundGamePacket, ClientboundGamePacket>
impl Connection<ServerboundGamePacket, ClientboundGamePacket>
Sourcepub fn config(
self,
) -> Connection<ServerboundConfigPacket, ClientboundConfigPacket>
pub fn config( self, ) -> Connection<ServerboundConfigPacket, ClientboundConfigPacket>
Change our state back to configuration.
Source§impl<R1, W1> Connection<R1, W1>
impl<R1, W1> Connection<R1, W1>
Sourcepub fn from<R2, W2>(connection: Connection<R1, W1>) -> Connection<R2, W2>
pub fn from<R2, W2>(connection: Connection<R1, W1>) -> Connection<R2, W2>
Creates a Connection of a type from a Connection of another type.
Useful for servers or custom packets.
Sourcepub fn wrap(stream: TcpStream) -> Connection<R1, W1>
pub fn wrap(stream: TcpStream) -> Connection<R1, W1>
Convert an existing TcpStream into a Connection. Useful for servers.
Examples found in repository?
67async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
68 stream.set_nodelay(true)?;
69 let ip = stream.peer_addr()?;
70 let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
71 Connection::wrap(stream);
72
73 // The first packet sent from a client is the intent packet.
74 // This specifies whether the client is pinging
75 // the server or is going to join the game.
76 let intent = match conn.read().await {
77 Ok(packet) => match packet {
78 ServerboundHandshakePacket::Intention(packet) => {
79 info!(
80 "New connection from {}, hostname {:?}:{}, version {}, {:?}",
81 ip.ip(),
82 packet.hostname,
83 packet.port,
84 packet.protocol_version,
85 packet.intention
86 );
87 packet
88 }
89 },
90 Err(e) => {
91 let e = e.into();
92 warn!("Error during intent: {e}");
93 return Err(e);
94 }
95 };
96
97 match intent.intention {
98 // If the client is pinging the proxy,
99 // reply with the information below.
100 ClientIntention::Status => {
101 let mut conn = conn.status();
102 loop {
103 match conn.read().await {
104 Ok(p) => match p {
105 ServerboundStatusPacket::StatusRequest(_) => {
106 conn.write(ClientboundStatusResponse {
107 description: PROXY_DESC.into(),
108 favicon: PROXY_FAVICON.clone(),
109 players: PROXY_PLAYERS.clone(),
110 version: PROXY_VERSION.clone(),
111 enforces_secure_chat: PROXY_SECURE_CHAT,
112 })
113 .await?;
114 }
115 ServerboundStatusPacket::PingRequest(p) => {
116 conn.write(ClientboundPongResponse { time: p.time }).await?;
117 break;
118 }
119 },
120 Err(e) => match *e {
121 ReadPacketError::ConnectionClosed => {
122 break;
123 }
124 e => {
125 warn!("Error during status: {e}");
126 return Err(e.into());
127 }
128 },
129 }
130 }
131 }
132 // If the client intends to join the proxy,
133 // wait for them to send the `Hello` packet to
134 // log their username and uuid, then forward the
135 // connection along to the proxy target.
136 ClientIntention::Login => {
137 let mut conn = conn.login();
138 loop {
139 match conn.read().await {
140 Ok(p) => {
141 if let ServerboundLoginPacket::Hello(hello) = p {
142 info!(
143 "Player \'{0}\' from {1} logging in with uuid: {2}",
144 hello.name,
145 ip.ip(),
146 hello.profile_id.to_string()
147 );
148
149 tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
150 if let Err(e) = r {
151 error!("Failed to proxy: {e}");
152 }
153 }));
154
155 break;
156 }
157 }
158 Err(e) => match *e {
159 ReadPacketError::ConnectionClosed => {
160 break;
161 }
162 e => {
163 warn!("Error during login: {e}");
164 return Err(e.into());
165 }
166 },
167 }
168 }
169 }
170 ClientIntention::Transfer => {
171 warn!("Client attempted to join via transfer")
172 }
173 }
174
175 Ok(())
176}
177
178async fn transfer(
179 mut inbound: TcpStream,
180 intent: ServerboundIntention,
181 hello: ServerboundHello,
182) -> Result<(), Box<dyn Error>> {
183 let outbound = TcpStream::connect(PROXY_ADDR).await?;
184 let name = hello.name.clone();
185 outbound.set_nodelay(true)?;
186
187 // Repeat the intent and hello packet
188 // received earlier to the proxy target
189 let mut outbound_conn: Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> =
190 Connection::wrap(outbound);
191 outbound_conn.write(intent).await?;
192
193 let mut outbound_conn = outbound_conn.login();
194 outbound_conn.write(hello).await?;
195
196 let mut outbound = outbound_conn.unwrap()?;
197
198 // Split the incoming and outgoing connections in
199 // halves and handle each pair on separate threads.
200 let (mut ri, mut wi) = inbound.split();
201 let (mut ro, mut wo) = outbound.split();
202
203 let client_to_server = async {
204 io::copy(&mut ri, &mut wo).await?;
205 wo.shutdown().await
206 };
207
208 let server_to_client = async {
209 io::copy(&mut ro, &mut wi).await?;
210 wi.shutdown().await
211 };
212
213 tokio::try_join!(client_to_server, server_to_client)?;
214 info!("Player \'{name}\' left the game");
215
216 Ok(())
217}Sourcepub fn unwrap(self) -> Result<TcpStream, ReuniteError>
pub fn unwrap(self) -> Result<TcpStream, ReuniteError>
Convert from a Connection into a TcpStream. Useful for servers.
Examples found in repository?
67async fn handle_connection(stream: TcpStream) -> anyhow::Result<()> {
68 stream.set_nodelay(true)?;
69 let ip = stream.peer_addr()?;
70 let mut conn: Connection<ServerboundHandshakePacket, ClientboundHandshakePacket> =
71 Connection::wrap(stream);
72
73 // The first packet sent from a client is the intent packet.
74 // This specifies whether the client is pinging
75 // the server or is going to join the game.
76 let intent = match conn.read().await {
77 Ok(packet) => match packet {
78 ServerboundHandshakePacket::Intention(packet) => {
79 info!(
80 "New connection from {}, hostname {:?}:{}, version {}, {:?}",
81 ip.ip(),
82 packet.hostname,
83 packet.port,
84 packet.protocol_version,
85 packet.intention
86 );
87 packet
88 }
89 },
90 Err(e) => {
91 let e = e.into();
92 warn!("Error during intent: {e}");
93 return Err(e);
94 }
95 };
96
97 match intent.intention {
98 // If the client is pinging the proxy,
99 // reply with the information below.
100 ClientIntention::Status => {
101 let mut conn = conn.status();
102 loop {
103 match conn.read().await {
104 Ok(p) => match p {
105 ServerboundStatusPacket::StatusRequest(_) => {
106 conn.write(ClientboundStatusResponse {
107 description: PROXY_DESC.into(),
108 favicon: PROXY_FAVICON.clone(),
109 players: PROXY_PLAYERS.clone(),
110 version: PROXY_VERSION.clone(),
111 enforces_secure_chat: PROXY_SECURE_CHAT,
112 })
113 .await?;
114 }
115 ServerboundStatusPacket::PingRequest(p) => {
116 conn.write(ClientboundPongResponse { time: p.time }).await?;
117 break;
118 }
119 },
120 Err(e) => match *e {
121 ReadPacketError::ConnectionClosed => {
122 break;
123 }
124 e => {
125 warn!("Error during status: {e}");
126 return Err(e.into());
127 }
128 },
129 }
130 }
131 }
132 // If the client intends to join the proxy,
133 // wait for them to send the `Hello` packet to
134 // log their username and uuid, then forward the
135 // connection along to the proxy target.
136 ClientIntention::Login => {
137 let mut conn = conn.login();
138 loop {
139 match conn.read().await {
140 Ok(p) => {
141 if let ServerboundLoginPacket::Hello(hello) = p {
142 info!(
143 "Player \'{0}\' from {1} logging in with uuid: {2}",
144 hello.name,
145 ip.ip(),
146 hello.profile_id.to_string()
147 );
148
149 tokio::spawn(transfer(conn.unwrap()?, intent, hello).map(|r| {
150 if let Err(e) = r {
151 error!("Failed to proxy: {e}");
152 }
153 }));
154
155 break;
156 }
157 }
158 Err(e) => match *e {
159 ReadPacketError::ConnectionClosed => {
160 break;
161 }
162 e => {
163 warn!("Error during login: {e}");
164 return Err(e.into());
165 }
166 },
167 }
168 }
169 }
170 ClientIntention::Transfer => {
171 warn!("Client attempted to join via transfer")
172 }
173 }
174
175 Ok(())
176}
177
178async fn transfer(
179 mut inbound: TcpStream,
180 intent: ServerboundIntention,
181 hello: ServerboundHello,
182) -> Result<(), Box<dyn Error>> {
183 let outbound = TcpStream::connect(PROXY_ADDR).await?;
184 let name = hello.name.clone();
185 outbound.set_nodelay(true)?;
186
187 // Repeat the intent and hello packet
188 // received earlier to the proxy target
189 let mut outbound_conn: Connection<ClientboundHandshakePacket, ServerboundHandshakePacket> =
190 Connection::wrap(outbound);
191 outbound_conn.write(intent).await?;
192
193 let mut outbound_conn = outbound_conn.login();
194 outbound_conn.write(hello).await?;
195
196 let mut outbound = outbound_conn.unwrap()?;
197
198 // Split the incoming and outgoing connections in
199 // halves and handle each pair on separate threads.
200 let (mut ri, mut wi) = inbound.split();
201 let (mut ro, mut wo) = outbound.split();
202
203 let client_to_server = async {
204 io::copy(&mut ri, &mut wo).await?;
205 wo.shutdown().await
206 };
207
208 let server_to_client = async {
209 io::copy(&mut ro, &mut wi).await?;
210 wi.shutdown().await
211 };
212
213 tokio::try_join!(client_to_server, server_to_client)?;
214 info!("Player \'{name}\' left the game");
215
216 Ok(())
217}Auto Trait Implementations§
impl<R, W> Freeze for Connection<R, W>
impl<R, W> RefUnwindSafe for Connection<R, W>where
R: RefUnwindSafe,
W: RefUnwindSafe,
impl<R, W> Send for Connection<R, W>
impl<R, W> Sync for Connection<R, W>
impl<R, W> Unpin for Connection<R, W>
impl<R, W> UnwindSafe for Connection<R, W>where
R: UnwindSafe,
W: UnwindSafe,
Blanket Implementations§
§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.