Skip to main content

steel_protocol/packets/game/
s_rename_item.rs

1//! Serverbound packet for renaming the item inside of an anvil's first slot
2
3use std::io::Cursor;
4
5use steel_macros::ServerPacket;
6use steel_utils::serial::{ReadFrom, prefixed_read::read_utf};
7
8const MAX_PACKET_NAME_LENGTH: usize = 32_767;
9
10/// Sent by the client when the player changes an anvil's item name.
11#[derive(ServerPacket, Clone, Debug)]
12pub struct SRenameItem {
13    /// The new name
14    pub name: String,
15}
16
17impl ReadFrom for SRenameItem {
18    fn read(data: &mut Cursor<&[u8]>) -> std::io::Result<Self> {
19        Ok(Self {
20            name: read_utf(data, MAX_PACKET_NAME_LENGTH)?,
21        })
22    }
23}
24
25#[cfg(test)]
26mod tests {
27    use steel_utils::{
28        codec::VarInt,
29        serial::{ReadFrom as _, WriteTo as _},
30    };
31
32    use super::{MAX_PACKET_NAME_LENGTH, SRenameItem};
33
34    fn encoded_string(bytes: &[u8]) -> Vec<u8> {
35        let mut encoded = Vec::with_capacity(VarInt::MAX_SIZE + bytes.len());
36        VarInt(bytes.len() as i32)
37            .write(&mut encoded)
38            .expect("test string length should encode");
39        encoded.extend_from_slice(bytes);
40        encoded
41    }
42
43    fn decode(bytes: &[u8]) -> std::io::Result<SRenameItem> {
44        SRenameItem::read(&mut std::io::Cursor::new(bytes))
45    }
46
47    #[test]
48    fn accepts_vanilla_utf16_boundary() {
49        let name = "a".repeat(MAX_PACKET_NAME_LENGTH);
50        let decoded = decode(&encoded_string(name.as_bytes()))
51            .expect("the Vanilla UTF-16 boundary should decode");
52        assert_eq!(decoded.name, name);
53    }
54
55    #[test]
56    fn rejects_more_than_vanilla_utf16_boundary() {
57        let name = "a".repeat(MAX_PACKET_NAME_LENGTH + 1);
58        assert!(decode(&encoded_string(name.as_bytes())).is_err());
59    }
60
61    #[test]
62    fn accepts_multibyte_name_above_the_old_byte_bound() {
63        let name = format!("{}X", "ยง".repeat(16_251));
64        assert_eq!(name.encode_utf16().count(), 16_252);
65        assert_eq!(name.len(), 32_503);
66
67        let decoded =
68            decode(&encoded_string(name.as_bytes())).expect("Vanilla accepts this packet name");
69        assert_eq!(decoded.name, name);
70    }
71
72    #[test]
73    fn rejects_encoded_length_above_vanilla_maximum() {
74        let bytes = vec![b'a'; MAX_PACKET_NAME_LENGTH * 3 + 1];
75        assert!(decode(&encoded_string(&bytes)).is_err());
76    }
77
78    #[test]
79    fn malformed_utf8_uses_replacement_characters() {
80        let decoded = decode(&encoded_string(&[0xFF]))
81            .expect("Vanilla decodes malformed UTF-8 with replacement");
82        assert_eq!(decoded.name, "\u{FFFD}");
83    }
84
85    #[test]
86    fn malformed_surrogate_sequence_uses_one_replacement_character() {
87        let decoded = decode(&encoded_string(&[0xED, 0xA0, 0x80]))
88            .expect("Vanilla replaces the complete malformed surrogate sequence");
89        assert_eq!(decoded.name, "\u{FFFD}");
90    }
91}