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