1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
//! A low-level crate to send and receive Minecraft packets.
//!
//! You should probably use [`azalea`] or [`azalea_client`] instead, as
//! azalea_protocol delegates much of the work, such as auth, to the user of
//! the crate.
//!
//! [`azalea`]: https://crates.io/crates/azalea
//! [`azalea_client`]: https://crates.io/crates/azalea-client
//!
//! See [`crate::connect::Connection`] for an example.
// these two are necessary for thiserror backtraces
#![feature(error_generic_member_access)]
#![feature(provide_any)]
use std::{net::SocketAddr, str::FromStr};
#[cfg(feature = "connecting")]
pub mod connect;
#[cfg(feature = "packets")]
pub mod packets;
pub mod read;
pub mod resolver;
pub mod write;
/// A host and port. It's possible that the port doesn't resolve to anything.
///
/// # Examples
///
/// ServerAddress implements TryFrom<&str>, so you can use it like this:
/// ```
/// use azalea_protocol::ServerAddress;
///
/// let addr = ServerAddress::try_from("localhost:25565").unwrap();
/// assert_eq!(addr.host, "localhost");
/// assert_eq!(addr.port, 25565);
/// ```
#[derive(Debug, Clone)]
pub struct ServerAddress {
pub host: String,
pub port: u16,
}
impl<'a> TryFrom<&'a str> for ServerAddress {
type Error = String;
/// Convert a Minecraft server address (host:port, the port is optional) to a ServerAddress
fn try_from(string: &str) -> Result<Self, Self::Error> {
if string.is_empty() {
return Err("Empty string".to_string());
}
let mut parts = string.split(':');
let host = parts.next().ok_or("No host specified")?.to_string();
// default the port to 25565
let port = parts.next().unwrap_or("25565");
let port = u16::from_str(port).map_err(|_| "Invalid port specified")?;
Ok(ServerAddress { host, port })
}
}
impl From<SocketAddr> for ServerAddress {
/// Convert an existing SocketAddr into a ServerAddress. This just converts
/// the ip to a string and passes along the port. The resolver will realize
/// it's already an IP address and not do any DNS requests.
fn from(addr: SocketAddr) -> Self {
ServerAddress {
host: addr.ip().to_string(),
port: addr.port(),
}
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use crate::{
packets::login::{
serverbound_hello_packet::ServerboundHelloPacket, ServerboundLoginPacket,
},
read::read_packet,
write::write_packet,
};
use bytes::BytesMut;
use uuid::Uuid;
#[tokio::test]
async fn test_hello_packet() {
let packet = ServerboundHelloPacket {
name: "test".to_string(),
profile_id: Some(Uuid::nil()),
}
.get();
let mut stream = Vec::new();
write_packet(&packet, &mut stream, None, &mut None)
.await
.unwrap();
let mut stream = Cursor::new(stream);
let _ = read_packet::<ServerboundLoginPacket, _>(
&mut stream,
&mut BytesMut::new(),
None,
&mut None,
)
.await
.unwrap();
}
#[tokio::test]
async fn test_double_hello_packet() {
let packet = ServerboundHelloPacket {
name: "test".to_string(),
profile_id: Some(Uuid::nil()),
}
.get();
let mut stream = Vec::new();
write_packet(&packet, &mut stream, None, &mut None)
.await
.unwrap();
write_packet(&packet, &mut stream, None, &mut None)
.await
.unwrap();
let mut stream = Cursor::new(stream);
let mut buffer = BytesMut::new();
let _ = read_packet::<ServerboundLoginPacket, _>(&mut stream, &mut buffer, None, &mut None)
.await
.unwrap();
let _ = read_packet::<ServerboundLoginPacket, _>(&mut stream, &mut buffer, None, &mut None)
.await
.unwrap();
}
}
|