aboutsummaryrefslogtreecommitdiff
path: root/azalea-buf/src
diff options
context:
space:
mode:
Diffstat (limited to 'azalea-buf/src')
-rw-r--r--azalea-buf/src/definitions.rs39
-rw-r--r--azalea-buf/src/lib.rs190
-rw-r--r--azalea-buf/src/read.rs380
-rw-r--r--azalea-buf/src/write.rs284
4 files changed, 893 insertions, 0 deletions
diff --git a/azalea-buf/src/definitions.rs b/azalea-buf/src/definitions.rs
new file mode 100644
index 00000000..e5d8e0c0
--- /dev/null
+++ b/azalea-buf/src/definitions.rs
@@ -0,0 +1,39 @@
+use buf_macros::McBuf;
+use std::ops::Deref;
+
+/// A Vec<u8> that isn't prefixed by a VarInt with the size.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct UnsizedByteArray(Vec<u8>);
+
+impl Deref for UnsizedByteArray {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<Vec<u8>> for UnsizedByteArray {
+ fn from(vec: Vec<u8>) -> Self {
+ Self(vec)
+ }
+}
+
+impl From<&str> for UnsizedByteArray {
+ fn from(s: &str) -> Self {
+ Self(s.as_bytes().to_vec())
+ }
+}
+
+/// Represents Java's BitSet, a list of bits.
+#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)]
+pub struct BitSet {
+ data: Vec<u64>,
+}
+
+// the Index trait requires us to return a reference, but we can't do that
+impl BitSet {
+ pub fn index(&self, index: usize) -> bool {
+ (self.data[index / 64] & (1u64 << (index % 64))) != 0
+ }
+}
diff --git a/azalea-buf/src/lib.rs b/azalea-buf/src/lib.rs
new file mode 100644
index 00000000..2ba17ac2
--- /dev/null
+++ b/azalea-buf/src/lib.rs
@@ -0,0 +1,190 @@
+//! Utilities for reading and writing for the Minecraft protocol
+
+#![feature(min_specialization)]
+#![feature(arbitrary_enum_discriminant)]
+
+mod definitions;
+mod read;
+mod write;
+
+pub use definitions::*;
+pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable};
+pub use write::{McBufVarWritable, McBufWritable, Writable};
+
+// const DEFAULT_NBT_QUOTA: u32 = 2097152;
+const MAX_STRING_LENGTH: u16 = 32767;
+// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144;
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::{collections::HashMap, io::Cursor};
+
+ #[test]
+ fn test_write_varint() {
+ let mut buf = Vec::new();
+ buf.write_varint(0).unwrap();
+ assert_eq!(buf, vec![0]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(1).unwrap();
+ assert_eq!(buf, vec![1]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(2).unwrap();
+ assert_eq!(buf, vec![2]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(127).unwrap();
+ assert_eq!(buf, vec![127]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(128).unwrap();
+ assert_eq!(buf, vec![128, 1]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(255).unwrap();
+ assert_eq!(buf, vec![255, 1]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(25565).unwrap();
+ assert_eq!(buf, vec![221, 199, 1]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(2097151).unwrap();
+ assert_eq!(buf, vec![255, 255, 127]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(2147483647).unwrap();
+ assert_eq!(buf, vec![255, 255, 255, 255, 7]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(-1).unwrap();
+ assert_eq!(buf, vec![255, 255, 255, 255, 15]);
+
+ let mut buf = Vec::new();
+ buf.write_varint(-2147483648).unwrap();
+ assert_eq!(buf, vec![128, 128, 128, 128, 8]);
+ }
+
+ #[test]
+ fn test_read_varint() {
+ let mut buf = Cursor::new(vec![0]);
+ assert_eq!(buf.read_varint().unwrap(), 0);
+ assert_eq!(buf.get_varint_size(0), 1);
+
+ let mut buf = Cursor::new(vec![1]);
+ assert_eq!(buf.read_varint().unwrap(), 1);
+ assert_eq!(buf.get_varint_size(1), 1);
+
+ let mut buf = Cursor::new(vec![2]);
+ assert_eq!(buf.read_varint().unwrap(), 2);
+ assert_eq!(buf.get_varint_size(2), 1);
+
+ let mut buf = Cursor::new(vec![127]);
+ assert_eq!(buf.read_varint().unwrap(), 127);
+ assert_eq!(buf.get_varint_size(127), 1);
+
+ let mut buf = Cursor::new(vec![128, 1]);
+ assert_eq!(buf.read_varint().unwrap(), 128);
+ assert_eq!(buf.get_varint_size(128), 2);
+
+ let mut buf = Cursor::new(vec![255, 1]);
+ assert_eq!(buf.read_varint().unwrap(), 255);
+ assert_eq!(buf.get_varint_size(255), 2);
+
+ let mut buf = Cursor::new(vec![221, 199, 1]);
+ assert_eq!(buf.read_varint().unwrap(), 25565);
+ assert_eq!(buf.get_varint_size(25565), 3);
+
+ let mut buf = Cursor::new(vec![255, 255, 127]);
+ assert_eq!(buf.read_varint().unwrap(), 2097151);
+ assert_eq!(buf.get_varint_size(2097151), 3);
+
+ let mut buf = Cursor::new(vec![255, 255, 255, 255, 7]);
+ assert_eq!(buf.read_varint().unwrap(), 2147483647);
+ assert_eq!(buf.get_varint_size(2147483647), 5);
+
+ let mut buf = Cursor::new(vec![255, 255, 255, 255, 15]);
+ assert_eq!(buf.read_varint().unwrap(), -1);
+ assert_eq!(buf.get_varint_size(-1), 5);
+
+ let mut buf = Cursor::new(vec![128, 128, 128, 128, 8]);
+ assert_eq!(buf.read_varint().unwrap(), -2147483648);
+ assert_eq!(buf.get_varint_size(-2147483648), 5);
+ }
+
+ #[test]
+ fn test_read_varint_longer() {
+ let mut buf = Cursor::new(vec![138, 56, 0, 135, 56, 123]);
+ assert_eq!(buf.read_varint().unwrap(), 7178);
+ }
+
+ #[test]
+ fn test_list() {
+ let mut buf = Vec::new();
+ buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s))
+ .unwrap();
+
+ // there's no read_list because idk how to do it in rust
+ let mut buf = Cursor::new(buf);
+
+ let mut result = Vec::new();
+ let length = buf.read_varint().unwrap();
+ for _ in 0..length {
+ result.push(buf.read_utf().unwrap());
+ }
+
+ assert_eq!(result, vec!["a", "bc", "def"]);
+ }
+
+ #[test]
+ fn test_int_id_list() {
+ let mut buf = Vec::new();
+ buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i))
+ .unwrap();
+
+ let mut buf = Cursor::new(buf);
+
+ let result = buf.read_int_id_list().unwrap();
+ assert_eq!(result, vec![1, 2, 3]);
+ }
+
+ #[test]
+ fn test_map() {
+ let mut buf = Vec::new();
+ buf.write_map(
+ vec![("a", 1), ("bc", 23), ("def", 456)],
+ Vec::write_utf,
+ Vec::write_varint,
+ )
+ .unwrap();
+
+ let mut buf = Cursor::new(buf);
+
+ let mut result = Vec::new();
+ let length = buf.read_varint().unwrap();
+ for _ in 0..length {
+ result.push((buf.read_utf().unwrap(), buf.read_varint().unwrap()));
+ }
+
+ assert_eq!(
+ result,
+ vec![
+ ("a".to_string(), 1),
+ ("bc".to_string(), 23),
+ ("def".to_string(), 456)
+ ]
+ );
+ }
+
+ #[test]
+ fn test_long() {
+ let mut buf = Vec::new();
+ buf.write_long(123456).unwrap();
+
+ let mut buf = Cursor::new(buf);
+
+ assert_eq!(buf.read_long().unwrap(), 123456);
+ }
+}
diff --git a/azalea-buf/src/read.rs b/azalea-buf/src/read.rs
new file mode 100644
index 00000000..569a5b1d
--- /dev/null
+++ b/azalea-buf/src/read.rs
@@ -0,0 +1,380 @@
+use super::{UnsizedByteArray, MAX_STRING_LENGTH};
+use byteorder::{ReadBytesExt, BE};
+use std::{collections::HashMap, hash::Hash, io::Read};
+use tokio::io::{AsyncRead, AsyncReadExt};
+
+// TODO: get rid of Readable and use McBufReadable everywhere
+
+pub trait Readable {
+ fn read_int_id_list(&mut self) -> Result<Vec<i32>, String>;
+ fn read_varint(&mut self) -> Result<i32, String>;
+ fn get_varint_size(&mut self, value: i32) -> u8;
+ fn get_varlong_size(&mut self, value: i32) -> u8;
+ fn read_byte_array(&mut self) -> Result<Vec<u8>, String>;
+ fn read_bytes_with_len(&mut self, n: usize) -> Result<Vec<u8>, String>;
+ fn read_bytes(&mut self) -> Result<Vec<u8>, String>;
+ fn read_utf(&mut self) -> Result<String, String>;
+ fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String>;
+ fn read_byte(&mut self) -> Result<u8, String>;
+ fn read_int(&mut self) -> Result<i32, String>;
+ fn read_boolean(&mut self) -> Result<bool, String>;
+ fn read_long(&mut self) -> Result<i64, String>;
+ fn read_short(&mut self) -> Result<i16, String>;
+ fn read_float(&mut self) -> Result<f32, String>;
+ fn read_double(&mut self) -> Result<f64, String>;
+}
+
+impl<R> Readable for R
+where
+ R: Read,
+{
+ fn read_int_id_list(&mut self) -> Result<Vec<i32>, String> {
+ let len = self.read_varint()?;
+ let mut list = Vec::with_capacity(len as usize);
+ for _ in 0..len {
+ list.push(self.read_varint()?);
+ }
+ Ok(list)
+ }
+
+ // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67
+ /// Read a single varint from the reader and return the value, along with the number of bytes read
+ fn read_varint(&mut self) -> Result<i32, String> {
+ let mut buffer = [0];
+ let mut ans = 0;
+ for i in 0..5 {
+ self.read_exact(&mut buffer)
+ .map_err(|_| "Invalid VarInt".to_string())?;
+ ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i);
+ if buffer[0] & 0b1000_0000 == 0 {
+ return Ok(ans);
+ }
+ }
+ Ok(ans)
+ }
+
+ fn get_varint_size(&mut self, value: i32) -> u8 {
+ for i in 1..5 {
+ if (value & -1 << (i * 7)) != 0 {
+ continue;
+ }
+ return i;
+ }
+ 5
+ }
+
+ fn get_varlong_size(&mut self, value: i32) -> u8 {
+ for i in 1..10 {
+ if (value & -1 << (i * 7)) != 0 {
+ continue;
+ }
+ return i;
+ }
+ 10
+ }
+
+ fn read_byte_array(&mut self) -> Result<Vec<u8>, String> {
+ let length = self.read_varint()? as usize;
+ self.read_bytes_with_len(length)
+ }
+
+ fn read_bytes_with_len(&mut self, n: usize) -> Result<Vec<u8>, String> {
+ let mut buffer = vec![0; n];
+ self.read_exact(&mut buffer)
+ .map_err(|_| "Error reading bytes".to_string())?;
+ Ok(buffer)
+ }
+
+ fn read_bytes(&mut self) -> Result<Vec<u8>, String> {
+ // read to end of the buffer
+ let mut bytes = vec![];
+ self.read_to_end(&mut bytes)
+ .map_err(|_| "Error reading bytes".to_string())?;
+ Ok(bytes)
+ }
+
+ fn read_utf(&mut self) -> Result<String, String> {
+ self.read_utf_with_len(MAX_STRING_LENGTH.into())
+ }
+
+ fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String> {
+ let length = self.read_varint()?;
+ // i don't know why it's multiplied by 4 but it's like that in mojang's code so
+ if length < 0 {
+ return Err(
+ "The received encoded string buffer length is less than zero! Weird string!"
+ .to_string(),
+ );
+ }
+ if length as u32 > max_length * 4 {
+ return Err(format!(
+ "The received encoded string buffer length is longer than maximum allowed ({} > {})",
+ length,
+ max_length * 4
+ ));
+ }
+
+ // this is probably quite inefficient, idk how to do it better
+ let mut string = String::new();
+ let mut buffer = vec![0; length as usize];
+ self.read_exact(&mut buffer)
+ .map_err(|_| "Invalid UTF-8".to_string())?;
+ string.push_str(std::str::from_utf8(&buffer).unwrap());
+ if string.len() > length as usize {
+ return Err(format!(
+ "The received string length is longer than maximum allowed ({} > {})",
+ length, max_length
+ ));
+ }
+
+ Ok(string)
+ }
+
+ /// Read a single byte from the reader
+ fn read_byte(&mut self) -> Result<u8, String> {
+ self.read_u8().map_err(|_| "Error reading byte".to_string())
+ }
+
+ fn read_int(&mut self) -> Result<i32, String> {
+ match self.read_i32::<BE>() {
+ Ok(r) => Ok(r),
+ Err(_) => Err("Error reading int".to_string()),
+ }
+ }
+
+ fn read_boolean(&mut self) -> Result<bool, String> {
+ match self.read_byte()? {
+ 0 => Ok(false),
+ 1 => Ok(true),
+ _ => Err("Error reading boolean".to_string()),
+ }
+ }
+
+ fn read_long(&mut self) -> Result<i64, String> {
+ match self.read_i64::<BE>() {
+ Ok(r) => Ok(r),
+ Err(_) => Err("Error reading long".to_string()),
+ }
+ }
+
+ fn read_short(&mut self) -> Result<i16, String> {
+ match self.read_i16::<BE>() {
+ Ok(r) => Ok(r),
+ Err(_) => Err("Error reading short".to_string()),
+ }
+ }
+
+ fn read_float(&mut self) -> Result<f32, String> {
+ match self.read_f32::<BE>() {
+ Ok(r) => Ok(r),
+ Err(_) => Err("Error reading float".to_string()),
+ }
+ }
+
+ fn read_double(&mut self) -> Result<f64, String> {
+ match self.read_f64::<BE>() {
+ Ok(r) => Ok(r),
+ Err(_) => Err("Error reading double".to_string()),
+ }
+ }
+}
+
+// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67
+/// Read a single varint from the reader and return the value, along with the number of bytes read
+pub async fn read_varint_async(reader: &mut (dyn AsyncRead + Unpin + Send)) -> Result<i32, String> {
+ let mut buffer = [0];
+ let mut ans = 0;
+ for i in 0..5 {
+ reader
+ .read_exact(&mut buffer)
+ .await
+ .map_err(|_| "Invalid VarInt".to_string())?;
+ ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i);
+ if buffer[0] & 0b1000_0000 == 0 {
+ return Ok(ans);
+ }
+ }
+ Ok(ans)
+}
+
+pub trait McBufReadable
+where
+ Self: Sized,
+{
+ fn read_into(buf: &mut impl Read) -> Result<Self, String>;
+}
+
+pub trait McBufVarReadable
+where
+ Self: Sized,
+{
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String>;
+}
+
+impl McBufReadable for i32 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ Readable::read_int(buf)
+ }
+}
+
+impl McBufVarReadable for i32 {
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_varint()
+ }
+}
+
+impl McBufVarReadable for i64 {
+ // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L54
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let mut buffer = [0];
+ let mut ans = 0;
+ for i in 0..8 {
+ buf.read_exact(&mut buffer)
+ .map_err(|_| "Invalid VarLong".to_string())?;
+ ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i;
+ if buffer[0] & 0b1000_0000 == 0 {
+ break;
+ }
+ }
+ Ok(ans)
+ }
+}
+impl McBufVarReadable for u64 {
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String> {
+ i64::var_read_into(buf).map(|i| i as u64)
+ }
+}
+
+impl McBufReadable for UnsizedByteArray {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ Ok(buf.read_bytes()?.into())
+ }
+}
+
+impl<T: McBufReadable + Send> McBufReadable for Vec<T> {
+ default fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let length = buf.read_varint()? as usize;
+ let mut contents = Vec::with_capacity(length);
+ for _ in 0..length {
+ contents.push(T::read_into(buf)?);
+ }
+ Ok(contents)
+ }
+}
+
+impl<K: McBufReadable + Send + Eq + Hash, V: McBufReadable + Send> McBufReadable for HashMap<K, V> {
+ default fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let length = buf.read_varint()? as usize;
+ let mut contents = HashMap::with_capacity(length);
+ for _ in 0..length {
+ contents.insert(K::read_into(buf)?, V::read_into(buf)?);
+ }
+ Ok(contents)
+ }
+}
+
+impl McBufReadable for Vec<u8> {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_byte_array()
+ }
+}
+
+impl McBufReadable for String {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_utf()
+ }
+}
+
+impl McBufReadable for u32 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ Readable::read_int(buf).map(|i| i as u32)
+ }
+}
+
+impl McBufVarReadable for u32 {
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_varint().map(|i| i as u32)
+ }
+}
+
+impl McBufReadable for u16 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_short().map(|i| i as u16)
+ }
+}
+
+impl McBufReadable for i16 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_short()
+ }
+}
+
+impl McBufVarReadable for u16 {
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_varint().map(|i| i as u16)
+ }
+}
+
+impl<T: McBufVarReadable> McBufVarReadable for Vec<T> {
+ fn var_read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let length = buf.read_varint()? as usize;
+ let mut contents = Vec::with_capacity(length);
+ for _ in 0..length {
+ contents.push(T::var_read_into(buf)?);
+ }
+ Ok(contents)
+ }
+}
+
+impl McBufReadable for i64 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_long()
+ }
+}
+
+impl McBufReadable for u64 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ i64::read_into(buf).map(|i| i as u64)
+ }
+}
+
+impl McBufReadable for bool {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_boolean()
+ }
+}
+
+impl McBufReadable for u8 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_byte()
+ }
+}
+
+impl McBufReadable for i8 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_byte().map(|i| i as i8)
+ }
+}
+
+impl McBufReadable for f32 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_float()
+ }
+}
+
+impl McBufReadable for f64 {
+ fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ buf.read_double()
+ }
+}
+
+impl<T: McBufReadable> McBufReadable for Option<T> {
+ default fn read_into(buf: &mut impl Read) -> Result<Self, String> {
+ let present = buf.read_boolean()?;
+ Ok(if present {
+ Some(T::read_into(buf)?)
+ } else {
+ None
+ })
+ }
+}
diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs
new file mode 100644
index 00000000..fdf58203
--- /dev/null
+++ b/azalea-buf/src/write.rs
@@ -0,0 +1,284 @@
+use super::{UnsizedByteArray, MAX_STRING_LENGTH};
+use byteorder::{BigEndian, WriteBytesExt};
+use std::{collections::HashMap, io::Write};
+
+// TODO: get rid of Writable and use McBufWritable everywhere
+
+pub trait Writable: Write {
+ fn write_list<F, T>(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error>
+ where
+ F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy,
+ {
+ self.write_varint(list.len() as i32)?;
+ for item in list {
+ writer(self, item)?;
+ }
+ Ok(())
+ }
+
+ fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> {
+ self.write_list(list, |buf, n| buf.write_varint(*n))
+ }
+
+ fn write_map<KF, VF, KT, VT>(
+ &mut self,
+ map: Vec<(KT, VT)>,
+ key_writer: KF,
+ value_writer: VF,
+ ) -> Result<(), std::io::Error>
+ where
+ KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy,
+ VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy,
+ {
+ self.write_varint(map.len() as i32)?;
+ for (key, value) in map {
+ key_writer(self, key)?;
+ value_writer(self, value)?;
+ }
+ Ok(())
+ }
+
+ fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_u8(self, n)
+ }
+
+ fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
+ self.write_all(bytes)?;
+ Ok(())
+ }
+
+ fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> {
+ let mut buffer = [0];
+ if value == 0 {
+ self.write_all(&buffer).unwrap();
+ }
+ while value != 0 {
+ buffer[0] = (value & 0b0111_1111) as u8;
+ value = (value >> 7) & (i32::max_value() >> 6);
+ if value != 0 {
+ buffer[0] |= 0b1000_0000;
+ }
+ self.write_all(&buffer)?;
+ }
+ Ok(())
+ }
+
+ fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> {
+ if string.len() > len {
+ panic!(
+ "String too big (was {} bytes encoded, max {})",
+ string.len(),
+ len
+ );
+ }
+ self.write_varint(string.len() as i32)?;
+ self.write_bytes(string.as_bytes())
+ }
+
+ fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> {
+ self.write_utf_with_len(string, MAX_STRING_LENGTH.into())
+ }
+
+ fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_i16::<BigEndian>(self, n)
+ }
+
+ fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
+ self.write_varint(bytes.len() as i32)?;
+ self.write_bytes(bytes)
+ }
+
+ fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_i32::<BigEndian>(self, n)
+ }
+
+ fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> {
+ self.write_byte(if b { 1 } else { 0 })
+ }
+
+ fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_i64::<BigEndian>(self, n)
+ }
+
+ fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_f32::<BigEndian>(self, n)
+ }
+
+ fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> {
+ WriteBytesExt::write_f64::<BigEndian>(self, n)
+ }
+}
+
+impl<W: Write + ?Sized> Writable for W {}
+
+pub trait McBufWritable {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
+}
+
+pub trait McBufVarWritable {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
+}
+
+impl McBufWritable for i32 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ Writable::write_int(buf, *self)
+ }
+}
+
+impl McBufVarWritable for i32 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_varint(*self)
+ }
+}
+
+impl McBufWritable for UnsizedByteArray {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_bytes(self)
+ }
+}
+
+impl<T: McBufWritable> McBufWritable for Vec<T> {
+ default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_list(self, |buf, i| T::write_into(i, buf))
+ }
+}
+
+impl<K: McBufWritable, V: McBufWritable> McBufWritable for HashMap<K, V> {
+ default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ u32::var_write_into(&(self.len() as u32), buf)?;
+ for (key, value) in self {
+ key.write_into(buf)?;
+ value.write_into(buf)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl McBufWritable for Vec<u8> {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_byte_array(self)
+ }
+}
+
+impl McBufWritable for String {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_utf(self)
+ }
+}
+
+impl McBufWritable for u32 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i16::write_into(&(*self as i16), buf)
+ }
+}
+
+impl McBufVarWritable for u32 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i32::var_write_into(&(*self as i32), buf)
+ }
+}
+
+impl McBufVarWritable for i64 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ let mut buffer = [0];
+ let mut value = *self;
+ while value != 0 {
+ buffer[0] = (value & 0b0111_1111) as u8;
+ value = (value >> 7) & (i64::max_value() >> 6);
+ if value != 0 {
+ buffer[0] |= 0b1000_0000;
+ }
+ buf.write(&mut buffer)?;
+ }
+ Ok(())
+ }
+}
+
+impl McBufVarWritable for u64 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i64::var_write_into(&(*self as i64), buf)
+ }
+}
+
+impl McBufWritable for u16 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i16::write_into(&(*self as i16), buf)
+ }
+}
+
+impl McBufVarWritable for u16 {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i32::var_write_into(&(*self as i32), buf)
+ }
+}
+
+impl<T: McBufVarWritable> McBufVarWritable for Vec<T> {
+ fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ u32::var_write_into(&(self.len() as u32), buf)?;
+ for i in self {
+ i.var_write_into(buf)?;
+ }
+ Ok(())
+ }
+}
+
+impl McBufWritable for u8 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_byte(*self)
+ }
+}
+
+impl McBufWritable for i16 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ Writable::write_short(buf, *self)
+ }
+}
+
+impl McBufWritable for i64 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ Writable::write_long(buf, *self)
+ }
+}
+
+impl McBufWritable for u64 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ i64::write_into(&(*self as i64), buf)
+ }
+}
+
+impl McBufWritable for bool {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_boolean(*self)
+ }
+}
+
+impl McBufWritable for i8 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_byte(*self as u8)
+ }
+}
+
+impl McBufWritable for f32 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_float(*self)
+ }
+}
+
+impl McBufWritable for f64 {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ buf.write_double(*self)
+ }
+}
+
+impl<T: McBufWritable> McBufWritable for Option<T> {
+ default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ if let Some(s) = self {
+ buf.write_boolean(true)?;
+ s.write_into(buf)?;
+ } else {
+ buf.write_boolean(false)?;
+ };
+ Ok(())
+ }
+}