1use crate::codec::{Decode, Encode, PacketId};
2use crate::error::ProtocolError;
3use crate::types::VarInt;
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5
6fn encode_string(s: &str, dst: &mut BytesMut) -> Result<(), ProtocolError> {
7 let bytes = s.as_bytes();
8 VarInt(bytes.len() as i32).encode(dst)?;
9 dst.put_slice(bytes);
10 Ok(())
11}
12
13fn decode_string(src: &mut Bytes, context: &'static str) -> Result<String, ProtocolError> {
14 let len = VarInt::decode(src)?.0 as usize;
15 if src.remaining() < len {
16 return Err(ProtocolError::Io(std::io::Error::new(
17 std::io::ErrorKind::UnexpectedEof,
18 format!("Missing bytes for {context}"),
19 )));
20 }
21 let mut b = vec![0u8; len];
22 src.copy_to_slice(&mut b);
23 String::from_utf8(b).map_err(|_| {
24 ProtocolError::Io(std::io::Error::new(
25 std::io::ErrorKind::InvalidData,
26 format!("Invalid UTF-8 in {context}"),
27 ))
28 })
29}
30
31fn encode_byte_array(data: &[u8], dst: &mut BytesMut) -> Result<(), ProtocolError> {
32 VarInt(data.len() as i32).encode(dst)?;
33 dst.put_slice(data);
34 Ok(())
35}
36
37fn decode_byte_array(src: &mut Bytes, context: &'static str) -> Result<Vec<u8>, ProtocolError> {
38 let len = VarInt::decode(src)?.0 as usize;
39 if src.remaining() < len {
40 return Err(ProtocolError::Io(std::io::Error::new(
41 std::io::ErrorKind::UnexpectedEof,
42 format!("Missing bytes for {context}"),
43 )));
44 }
45 let mut b = vec![0u8; len];
46 src.copy_to_slice(&mut b);
47 Ok(b)
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub struct ServerboundLoginStart {
52 pub username: String,
53}
54
55impl PacketId for ServerboundLoginStart {
56 fn packet_id(_ver: u32) -> u8 {
57 0x00
58 }
59}
60
61impl Encode for ServerboundLoginStart {
62 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
63 encode_string(&self.username, dst)
64 }
65}
66
67impl Decode for ServerboundLoginStart {
68 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
69 let username = decode_string(src, "ServerboundLoginStart username")?;
70 Ok(Self { username })
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub struct ClientboundLoginDisconnect {
76 pub reason: String,
77}
78
79impl PacketId for ClientboundLoginDisconnect {
80 fn packet_id(_ver: u32) -> u8 {
81 0x00
82 }
83}
84
85impl Encode for ClientboundLoginDisconnect {
86 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
87 encode_string(&self.reason, dst)
88 }
89}
90
91impl Decode for ClientboundLoginDisconnect {
92 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
93 let reason = decode_string(src, "ClientboundLoginDisconnect reason")?;
94 Ok(Self { reason })
95 }
96}
97
98#[derive(Debug, Clone, PartialEq)]
99pub struct ClientboundEncryptionRequest {
100 pub server_id: String,
101 pub public_key: Vec<u8>,
102 pub verify_token: Vec<u8>,
103}
104
105impl PacketId for ClientboundEncryptionRequest {
106 fn packet_id(_ver: u32) -> u8 {
107 0x01
108 }
109}
110
111impl Encode for ClientboundEncryptionRequest {
112 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
113 let id_bytes = self.server_id.as_bytes();
114 VarInt(id_bytes.len() as i32).encode(dst)?;
115 dst.put_slice(id_bytes);
116
117 VarInt(self.public_key.len() as i32).encode(dst)?;
118 dst.put_slice(&self.public_key);
119
120 VarInt(self.verify_token.len() as i32).encode(dst)?;
121 dst.put_slice(&self.verify_token);
122
123 Ok(())
124 }
125}
126
127impl Decode for ClientboundEncryptionRequest {
128 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
129 let id_len = VarInt::decode(src)?.0 as usize;
130 if src.remaining() < id_len {
131 return Err(ProtocolError::UnexpectedEof);
132 }
133 let mut id_bytes = vec![0u8; id_len];
134 src.copy_to_slice(&mut id_bytes);
135 let server_id = String::from_utf8(id_bytes).map_err(|_| ProtocolError::UnexpectedEof)?;
136
137 let key_len = VarInt::decode(src)?.0 as usize;
138 if src.remaining() < key_len {
139 return Err(ProtocolError::UnexpectedEof);
140 }
141 let mut public_key = vec![0u8; key_len];
142 src.copy_to_slice(&mut public_key);
143
144 let token_len = VarInt::decode(src)?.0 as usize;
145 if src.remaining() < token_len {
146 return Err(ProtocolError::UnexpectedEof);
147 }
148 let mut verify_token = vec![0u8; token_len];
149 src.copy_to_slice(&mut verify_token);
150
151 let remaining_trailing_bytes = src.remaining();
152 src.advance(remaining_trailing_bytes);
153
154 Ok(Self {
155 server_id,
156 public_key,
157 verify_token,
158 })
159 }
160}
161
162#[derive(Debug, Clone, PartialEq)]
163pub struct ServerboundEncryptionResponse {
164 pub shared_secret: Vec<u8>,
165 pub verify_token: Vec<u8>,
166}
167
168impl PacketId for ServerboundEncryptionResponse {
169 fn packet_id(_ver: u32) -> u8 {
170 0x01
171 }
172}
173
174impl Encode for ServerboundEncryptionResponse {
175 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
176 encode_byte_array(&self.shared_secret, dst)?;
177 encode_byte_array(&self.verify_token, dst)
178 }
179}
180
181impl Decode for ServerboundEncryptionResponse {
182 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
183 let shared_secret = decode_byte_array(src, "ServerboundEncryptionResponse shared_secret")?;
184 let verify_token = decode_byte_array(src, "ServerboundEncryptionResponse verify_token")?;
185 Ok(Self {
186 shared_secret,
187 verify_token,
188 })
189 }
190}
191
192#[derive(Debug, Clone, PartialEq)]
193pub struct ProfileProperty {
194 pub name: String,
195 pub value: String,
196 pub signature: Option<String>,
197}
198
199impl Encode for ProfileProperty {
200 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
201 encode_string(&self.name, dst)?;
202 encode_string(&self.value, dst)?;
203 match &self.signature {
204 Some(sig) => {
205 dst.put_u8(1);
206 encode_string(sig, dst)?;
207 },
208 None => dst.put_u8(0),
209 }
210 Ok(())
211 }
212}
213
214impl Decode for ProfileProperty {
215 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
216 let name = decode_string(src, "ProfileProperty name")?;
217 let value = decode_string(src, "ProfileProperty value")?;
218 if src.remaining() < 1 {
219 return Err(ProtocolError::Io(std::io::Error::new(
220 std::io::ErrorKind::UnexpectedEof,
221 "Missing signature flag in ProfileProperty",
222 )));
223 }
224 let signature = if src.get_u8() != 0 {
225 Some(decode_string(src, "ProfileProperty signature")?)
226 } else {
227 None
228 };
229 Ok(Self {
230 name,
231 value,
232 signature,
233 })
234 }
235}
236
237#[derive(Debug, Clone, PartialEq)]
238pub struct ClientboundLoginSuccess {
239 pub uuid: uuid::Uuid,
240 pub username: String,
241 pub properties: Vec<ProfileProperty>,
242}
243
244impl PacketId for ClientboundLoginSuccess {
245 fn packet_id(_ver: u32) -> u8 {
246 0x02
247 }
248}
249
250impl Encode for ClientboundLoginSuccess {
251 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
252 let uuid_str = self.uuid.hyphenated().to_string();
253 let uuid_bytes = uuid_str.as_bytes();
254 VarInt(uuid_bytes.len() as i32).encode(dst)?;
255 dst.put_slice(uuid_bytes);
256
257 let user_bytes = self.username.as_bytes();
258 VarInt(user_bytes.len() as i32).encode(dst)?;
259 dst.put_slice(user_bytes);
260
261 Ok(())
262 }
263}
264
265impl Decode for ClientboundLoginSuccess {
266 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
267 let uuid_len = VarInt::decode(src)?.0 as usize;
268 if src.remaining() < uuid_len {
269 return Err(ProtocolError::Io(std::io::Error::new(
270 std::io::ErrorKind::UnexpectedEof,
271 "Missing bytes for LoginSuccess UUID",
272 )));
273 }
274 let mut uuid_bytes = vec![0u8; uuid_len];
275 src.copy_to_slice(&mut uuid_bytes);
276 let uuid_str = String::from_utf8(uuid_bytes).map_err(|_| {
277 ProtocolError::Io(std::io::Error::new(
278 std::io::ErrorKind::InvalidData,
279 "Invalid UTF-8 inside LoginSuccess UUID",
280 ))
281 })?;
282
283 let uuid = uuid::Uuid::parse_str(&uuid_str).map_err(|_| {
284 ProtocolError::Io(std::io::Error::new(
285 std::io::ErrorKind::InvalidData,
286 "Invalid string format for UUID parsing",
287 ))
288 })?;
289
290 let user_len = VarInt::decode(src)?.0 as usize;
291 if src.remaining() < user_len {
292 return Err(ProtocolError::Io(std::io::Error::new(
293 std::io::ErrorKind::UnexpectedEof,
294 "Missing bytes for LoginSuccess Username",
295 )));
296 }
297 let mut user_bytes = vec![0u8; user_len];
298 src.copy_to_slice(&mut user_bytes);
299 let username = String::from_utf8(user_bytes).map_err(|_| {
300 ProtocolError::Io(std::io::Error::new(
301 std::io::ErrorKind::InvalidData,
302 "Invalid UTF-8 inside LoginSuccess Username",
303 ))
304 })?;
305
306 Ok(Self {
307 uuid,
308 username,
309 properties: Vec::new(),
310 })
311 }
312}
313
314#[derive(Debug, Clone, PartialEq)]
315pub struct ClientboundSetCompression {
316 pub threshold: VarInt,
317}
318
319impl PacketId for ClientboundSetCompression {
320 fn packet_id(_ver: u32) -> u8 {
321 0x03
322 }
323}
324
325impl Encode for ClientboundSetCompression {
326 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
327 self.threshold.encode(dst)
328 }
329}
330
331impl Decode for ClientboundSetCompression {
332 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
333 let threshold = VarInt::decode(src)?;
334 Ok(Self { threshold })
335 }
336}
337
338#[derive(Debug, Clone, PartialEq)]
339pub struct ClientboundLoginPluginRequest {
340 pub message_id: VarInt,
341 pub channel: String,
342 pub data: Vec<u8>,
343}
344
345impl PacketId for ClientboundLoginPluginRequest {
346 fn packet_id(ver: u32) -> u8 {
347 if ver == 340 {
348 0xFF
349 } else {
350 0x04
351 }
352 }
353}
354
355impl Encode for ClientboundLoginPluginRequest {
356 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
357 self.message_id.encode(dst)?;
358 encode_string(&self.channel, dst)?;
359 dst.put_slice(&self.data);
360 Ok(())
361 }
362}
363
364impl Decode for ClientboundLoginPluginRequest {
365 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
366 let message_id = VarInt::decode(src)?;
367 let channel = decode_string(src, "ClientboundLoginPluginRequest channel")?;
368 let remaining = src.remaining();
369 let mut data = vec![0u8; remaining];
370 src.copy_to_slice(&mut data);
371 Ok(Self {
372 message_id,
373 channel,
374 data,
375 })
376 }
377}
378
379#[derive(Debug, Clone, PartialEq)]
380pub struct ServerboundLoginPluginResponse {
381 pub message_id: VarInt,
382 pub successful: bool,
383 pub data: Option<Vec<u8>>,
384}
385
386impl PacketId for ServerboundLoginPluginResponse {
387 fn packet_id(ver: u32) -> u8 {
388 if ver == 340 {
389 0xFF
390 } else {
391 0x02
392 }
393 }
394}
395
396impl Encode for ServerboundLoginPluginResponse {
397 fn encode(&self, dst: &mut BytesMut) -> Result<(), ProtocolError> {
398 self.message_id.encode(dst)?;
399 dst.put_u8(if self.successful { 1 } else { 0 });
400 if self.successful {
401 if let Some(ref data) = self.data {
402 dst.put_slice(data);
403 }
404 }
405 Ok(())
406 }
407}
408
409impl Decode for ServerboundLoginPluginResponse {
410 fn decode(src: &mut Bytes) -> Result<Self, ProtocolError> {
411 let message_id = VarInt::decode(src)?;
412 if src.remaining() < 1 {
413 return Err(ProtocolError::Io(std::io::Error::new(
414 std::io::ErrorKind::UnexpectedEof,
415 "Missing successful flag in ServerboundLoginPluginResponse",
416 )));
417 }
418 let successful = src.get_u8() != 0;
419 let data = if successful && src.remaining() > 0 {
420 let mut d = vec![0u8; src.remaining()];
421 src.copy_to_slice(&mut d);
422 Some(d)
423 } else {
424 None
425 };
426 Ok(Self {
427 message_id,
428 successful,
429 data,
430 })
431 }
432}
433
434#[derive(Debug, Clone, PartialEq)]
435pub struct ServerboundLoginAcknowledged;
436
437impl PacketId for ServerboundLoginAcknowledged {
438 fn packet_id(ver: u32) -> u8 {
439 if ver == 340 {
440 0xFF
441 } else {
442 0x03
443 }
444 }
445}
446
447impl Encode for ServerboundLoginAcknowledged {
448 fn encode(&self, _dst: &mut BytesMut) -> Result<(), ProtocolError> {
449 Ok(())
450 }
451}
452
453impl Decode for ServerboundLoginAcknowledged {
454 fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
455 Ok(Self)
456 }
457}
458
459#[derive(Debug, Clone, PartialEq)]
460pub struct AcknowledgeFinishConfiguration;
461
462impl PacketId for AcknowledgeFinishConfiguration {
463 fn packet_id(ver: u32) -> u8 {
464 if ver == 340 {
465 0xFF
466 } else {
467 0x02
468 }
469 }
470}
471
472impl Encode for AcknowledgeFinishConfiguration {
473 fn encode(&self, _dst: &mut BytesMut) -> Result<(), ProtocolError> {
474 Ok(())
475 }
476}
477
478impl Decode for AcknowledgeFinishConfiguration {
479 fn decode(_src: &mut Bytes) -> Result<Self, ProtocolError> {
480 Ok(Self)
481 }
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487
488 #[test]
489 fn login_start_roundtrip() {
490 let p = ServerboundLoginStart {
491 username: "Steve".to_string(),
492 };
493 let mut buf = BytesMut::new();
494 p.encode(&mut buf).unwrap();
495 let mut b = buf.freeze();
496 assert_eq!(ServerboundLoginStart::decode(&mut b).unwrap(), p);
497 }
498
499 #[test]
500 fn login_disconnect_roundtrip() {
501 let p = ClientboundLoginDisconnect {
502 reason: "Banned".to_string(),
503 };
504 let mut buf = BytesMut::new();
505 p.encode(&mut buf).unwrap();
506 let mut b = buf.freeze();
507 assert_eq!(ClientboundLoginDisconnect::decode(&mut b).unwrap(), p);
508 }
509
510 #[test]
511 fn encryption_roundtrip() {
512 let p = ClientboundEncryptionRequest {
513 server_id: String::new(),
514 public_key: vec![0xDE, 0xAD],
515 verify_token: vec![0xBE, 0xEF],
516 };
517 let mut buf = BytesMut::new();
518 p.encode(&mut buf).unwrap();
519 let mut b = buf.freeze();
520 assert_eq!(ClientboundEncryptionRequest::decode(&mut b).unwrap(), p);
521 }
522
523 #[test]
524 fn login_success_roundtrip() {
525 let p = ClientboundLoginSuccess {
526 uuid: uuid::Uuid::new_v4(),
527 username: "Steve".to_string(),
528 properties: vec![ProfileProperty {
529 name: "textures".to_string(),
530 value: "abc123".to_string(),
531 signature: Some("sig".to_string()),
532 }],
533 };
534 let mut buf = BytesMut::new();
535 p.encode(&mut buf).unwrap();
536 let mut b = buf.freeze();
537 let d = ClientboundLoginSuccess::decode(&mut b).unwrap();
538 assert_eq!(d.uuid, p.uuid);
539 assert_eq!(d.username, p.username);
540 }
541
542 #[test]
543 fn set_compression_roundtrip() {
544 let p = ClientboundSetCompression {
545 threshold: VarInt(512),
546 };
547 let mut buf = BytesMut::new();
548 p.encode(&mut buf).unwrap();
549 let mut b = buf.freeze();
550 assert_eq!(ClientboundSetCompression::decode(&mut b).unwrap(), p);
551 }
552
553 #[test]
554 fn plugin_request_roundtrip() {
555 let p = ClientboundLoginPluginRequest {
556 message_id: VarInt(1),
557 channel: "my:channel".to_string(),
558 data: vec![1, 2, 3],
559 };
560 let mut buf = BytesMut::new();
561 p.encode(&mut buf).unwrap();
562 let mut b = buf.freeze();
563 assert_eq!(ClientboundLoginPluginRequest::decode(&mut b).unwrap(), p);
564 }
565
566 #[test]
567 fn plugin_response_roundtrip() {
568 let p = ServerboundLoginPluginResponse {
569 message_id: VarInt(1),
570 successful: true,
571 data: Some(vec![0xAB, 0xCD]),
572 };
573 let mut buf = BytesMut::new();
574 p.encode(&mut buf).unwrap();
575 let mut b = buf.freeze();
576 assert_eq!(ServerboundLoginPluginResponse::decode(&mut b).unwrap(), p);
577 }
578}