kojacoord_protocol/versions/v1_8/login/
mod.rs1use bytes::{Buf, BufMut, Bytes, BytesMut};
2
3use crate::codec::{Decode, Encode, PacketId};
4use crate::error::ProtocolError;
5use crate::types::VarInt;
6
7pub use packets::{
8 ClientboundEncryptionRequest, ClientboundLoginDisconnect, ClientboundLoginSuccess,
9 ClientboundSetCompression, ProfileProperty, ServerboundEncryptionResponse,
10 ServerboundLoginStart,
11};
12
13mod packets {
14 use super::*;
15
16 fn encode_str(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
17 let bytes = s.as_bytes();
18 VarInt(bytes.len() as i32).encode(dst)?;
19 dst.put_slice(bytes);
20 Ok(())
21 }
22
23 fn decode_str(src: &mut Bytes, ctx: &'static str) -> Result<String, ProtocolError> {
24 let len = VarInt::decode(src)?.0 as usize;
25 if src.remaining() < len {
26 return Err(ProtocolError::Io(std::io::Error::new(
27 std::io::ErrorKind::UnexpectedEof,
28 format!("Missing bytes for {ctx}"),
29 )));
30 }
31 let mut b = vec![0u8; len];
32 src.copy_to_slice(&mut b);
33 String::from_utf8(b).map_err(|_| {
34 ProtocolError::Io(std::io::Error::new(
35 std::io::ErrorKind::InvalidData,
36 format!("Invalid UTF-8 in {ctx}"),
37 ))
38 })
39 }
40
41 fn encode_byte_array(data: &[u8], dst: &mut BytesMut) -> Result<(), ProtocolError> {
42 VarInt(data.len() as i32).encode(dst)?;
43 dst.put_slice(data);
44 Ok(())
45 }
46
47 fn decode_byte_array(src: &mut Bytes, ctx: &'static str) -> Result<Vec<u8>, ProtocolError> {
48 let len = VarInt::decode(src)?.0 as usize;
49 if src.remaining() < len {
50 return Err(ProtocolError::Io(std::io::Error::new(
51 std::io::ErrorKind::UnexpectedEof,
52 format!("Missing bytes for {ctx}"),
53 )));
54 }
55 let mut b = vec![0u8; len];
56 src.copy_to_slice(&mut b);
57 Ok(b)
58 }
59
60 #[derive(Debug, Clone, PartialEq)]
61 pub struct ServerboundLoginStart {
62 pub username: String,
63 }
64
65 impl PacketId for ServerboundLoginStart {
66 fn packet_id(_ver: u32) -> u8 {
67 0x00
68 }
69 }
70
71 impl Encode for ServerboundLoginStart {
72 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
73 encode_str(&self.username, dst)
74 }
75 }
76
77 impl Decode for ServerboundLoginStart {
78 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
79 let username = decode_str(src, "ServerboundLoginStart username")?;
80 Ok(Self { username })
81 }
82 }
83
84 #[derive(Debug, Clone, PartialEq)]
85 pub struct ClientboundLoginDisconnect {
86 pub reason: String,
87 }
88
89 impl PacketId for ClientboundLoginDisconnect {
90 fn packet_id(_ver: u32) -> u8 {
91 0x00
92 }
93 }
94
95 impl Encode for ClientboundLoginDisconnect {
96 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
97 encode_str(&self.reason, dst)
98 }
99 }
100
101 impl Decode for ClientboundLoginDisconnect {
102 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
103 let reason = decode_str(src, "ClientboundLoginDisconnect reason")?;
104 Ok(Self { reason })
105 }
106 }
107
108 #[derive(Debug, Clone, PartialEq)]
109 pub struct ClientboundEncryptionRequest {
110 pub server_id: String,
111 pub public_key: Vec<u8>,
112 pub verify_token: Vec<u8>,
113 }
114
115 impl PacketId for ClientboundEncryptionRequest {
116 fn packet_id(_ver: u32) -> u8 {
117 0x01
118 }
119 }
120
121 impl Encode for ClientboundEncryptionRequest {
122 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
123 encode_str(&self.server_id, dst)?;
124 encode_byte_array(&self.public_key, dst)?;
125 encode_byte_array(&self.verify_token, dst)
126 }
127 }
128
129 impl Decode for ClientboundEncryptionRequest {
130 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
131 let server_id = decode_str(src, "ClientboundEncryptionRequest server_id")?;
132 let public_key = decode_byte_array(src, "ClientboundEncryptionRequest public_key")?;
133 let verify_token = decode_byte_array(src, "ClientboundEncryptionRequest verify_token")?;
134 Ok(Self {
135 server_id,
136 public_key,
137 verify_token,
138 })
139 }
140 }
141
142 #[derive(Debug, Clone, PartialEq)]
143 pub struct ProfileProperty {
144 pub name: String,
145 pub value: String,
146 pub signature: Option<String>,
147 }
148
149 impl Encode for ProfileProperty {
150 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
151 encode_str(&self.name, dst)?;
152 encode_str(&self.value, dst)?;
153 match &self.signature {
154 Some(sig) => {
155 dst.put_u8(1);
156 encode_str(sig, dst)?;
157 },
158 None => dst.put_u8(0),
159 }
160 Ok(())
161 }
162 }
163
164 impl Decode for ProfileProperty {
165 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
166 let name = decode_str(src, "ProfileProperty name")?;
167 let value = decode_str(src, "ProfileProperty value")?;
168 if src.remaining() < 1 {
169 return Err(ProtocolError::Io(std::io::Error::new(
170 std::io::ErrorKind::UnexpectedEof,
171 "Missing signature flag in ProfileProperty",
172 )));
173 }
174 let signature = if src.get_u8() != 0 {
175 Some(decode_str(src, "ProfileProperty signature")?)
176 } else {
177 None
178 };
179 Ok(Self {
180 name,
181 value,
182 signature,
183 })
184 }
185 }
186
187 #[derive(Debug, Clone, PartialEq)]
188 pub struct ClientboundLoginSuccess {
189 pub uuid: uuid::Uuid,
190 pub username: String,
191 }
192
193 impl PacketId for ClientboundLoginSuccess {
194 fn packet_id(_ver: u32) -> u8 {
195 0x02
196 }
197 }
198
199 impl Encode for ClientboundLoginSuccess {
200 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
201 encode_str(&self.uuid.hyphenated().to_string(), dst)?;
202 encode_str(&self.username, dst)?;
203 Ok(())
204 }
205 }
206
207 impl Decode for ClientboundLoginSuccess {
208 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
209 let uuid_str = decode_str(src, "ClientboundLoginSuccess uuid")?;
210 let uuid = uuid::Uuid::parse_str(&uuid_str).map_err(|_| {
211 ProtocolError::Io(std::io::Error::new(
212 std::io::ErrorKind::InvalidData,
213 "Invalid UUID in ClientboundLoginSuccess",
214 ))
215 })?;
216 let username = decode_str(src, "ClientboundLoginSuccess username")?;
217
218 Ok(Self { uuid, username })
219 }
220 }
221
222 #[derive(Debug, Clone, PartialEq)]
223 pub struct ClientboundSetCompression {
224 pub threshold: VarInt,
225 }
226
227 impl PacketId for ClientboundSetCompression {
228 fn packet_id(_ver: u32) -> u8 {
229 0x03
230 }
231 }
232
233 impl Encode for ClientboundSetCompression {
234 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
235 self.threshold.encode(dst)
236 }
237 }
238
239 impl Decode for ClientboundSetCompression {
240 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
241 let threshold = VarInt::decode(src)?;
242 Ok(Self { threshold })
243 }
244 }
245
246 #[derive(Debug, Clone, PartialEq)]
247 pub struct ServerboundEncryptionResponse {
248 pub shared_secret: Vec<u8>,
249 pub verify_token: Vec<u8>,
250 }
251
252 impl PacketId for ServerboundEncryptionResponse {
253 fn packet_id(_ver: u32) -> u8 {
254 0x01
255 }
256 }
257
258 impl Encode for ServerboundEncryptionResponse {
259 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
260 encode_byte_array(&self.shared_secret, dst)?;
261 encode_byte_array(&self.verify_token, dst)
262 }
263 }
264
265 impl Decode for ServerboundEncryptionResponse {
266 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
267 let shared_secret =
268 decode_byte_array(src, "ServerboundEncryptionResponse shared_secret")?;
269 let verify_token =
270 decode_byte_array(src, "ServerboundEncryptionResponse verify_token")?;
271 Ok(Self {
272 shared_secret,
273 verify_token,
274 })
275 }
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::*;
282
283 fn rt_login_start(p: ServerboundLoginStart) -> ServerboundLoginStart {
284 let mut buf = BytesMut::new();
285 p.encode(&mut buf).unwrap();
286 let mut b = buf.freeze();
287 ServerboundLoginStart::decode(&mut b).unwrap()
288 }
289
290 #[test]
291 fn login_start_roundtrip() {
292 let p = ServerboundLoginStart {
293 username: "Notch".to_string(),
294 };
295 assert_eq!(rt_login_start(p.clone()), p);
296 }
297
298 #[test]
299 fn login_disconnect_roundtrip() {
300 let p = ClientboundLoginDisconnect {
301 reason: r#"{"text":"banned"}"#.to_string(),
302 };
303 let mut buf = BytesMut::new();
304 p.encode(&mut buf).unwrap();
305 let mut b = buf.freeze();
306 assert_eq!(ClientboundLoginDisconnect::decode(&mut b).unwrap(), p);
307 }
308
309 #[test]
310 fn encryption_request_roundtrip() {
311 let p = ClientboundEncryptionRequest {
312 server_id: String::new(),
313 public_key: vec![1, 2, 3, 4],
314 verify_token: vec![5, 6, 7, 8],
315 };
316 let mut buf = BytesMut::new();
317 p.encode(&mut buf).unwrap();
318 let mut b = buf.freeze();
319 assert_eq!(ClientboundEncryptionRequest::decode(&mut b).unwrap(), p);
320 }
321
322 #[test]
323 fn encryption_response_roundtrip() {
324 let p = ServerboundEncryptionResponse {
325 shared_secret: vec![0xAA; 128],
326 verify_token: vec![0xBB; 128],
327 };
328 let mut buf = BytesMut::new();
329 p.encode(&mut buf).unwrap();
330 let mut b = buf.freeze();
331 assert_eq!(ServerboundEncryptionResponse::decode(&mut b).unwrap(), p);
332 }
333
334 #[test]
335 fn login_success_roundtrip() {
336 let p = ClientboundLoginSuccess {
337 uuid: uuid::Uuid::new_v4(),
338 username: "Notch".to_string(),
339 };
340 let mut buf = BytesMut::new();
341 p.encode(&mut buf).unwrap();
342 let mut b = buf.freeze();
343 let decoded = ClientboundLoginSuccess::decode(&mut b).unwrap();
344 assert_eq!(decoded.uuid, p.uuid);
345 assert_eq!(decoded.username, p.username);
346 }
347
348 #[test]
349 fn set_compression_roundtrip() {
350 let p = ClientboundSetCompression {
351 threshold: VarInt(256),
352 };
353 let mut buf = BytesMut::new();
354 p.encode(&mut buf).unwrap();
355 let mut b = buf.freeze();
356 assert_eq!(ClientboundSetCompression::decode(&mut b).unwrap(), p);
357 }
358}