From 52ed60c75852f2eb9daa5cbd630551ec1d695f08 Mon Sep 17 00:00:00 2001 From: GIKExe <72767917+GIKExe@users.noreply.github.com> Date: Mon, 19 May 2025 06:59:47 +0300 Subject: [PATCH] nbt 95% --- level.dat | Bin 0 -> 2374 bytes src/cycle.rs | 6 +- src/data/mod.rs | 6 +- src/data/nbt.rs | 154 --------------------------------------------- src/data/reader.rs | 8 ++- src/data/writer.rs | 24 +++++-- src/lib.rs | 4 ++ src/main.rs | 43 +++---------- src/nbt/mod.rs | 25 ++++++++ src/nbt/reader.rs | 98 +++++++++++++++++++++++++++++ src/nbt/tags.rs | 16 +++++ src/nbt/writer.rs | 109 ++++++++++++++++++++++++++++++++ 12 files changed, 293 insertions(+), 200 deletions(-) create mode 100644 level.dat delete mode 100644 src/data/nbt.rs create mode 100644 src/lib.rs create mode 100644 src/nbt/mod.rs create mode 100644 src/nbt/reader.rs create mode 100644 src/nbt/tags.rs create mode 100644 src/nbt/writer.rs diff --git a/level.dat b/level.dat new file mode 100644 index 0000000000000000000000000000000000000000..d7f150259e73cda0403da761f47c67abb7dbdb75 GIT binary patch literal 2374 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 HVqgFOzO9p6 literal 0 HcmV?d00001 diff --git a/src/cycle.rs b/src/cycle.rs index 77096ef..5bf2647 100644 --- a/src/cycle.rs +++ b/src/cycle.rs @@ -1,5 +1,7 @@ -use tokio::{net::TcpStream, time::Sleep}; +use std::{thread::sleep, time::Duration}; + +use tokio::net::TcpStream; use crate::data::{clientbound, serverbound, AsyncReader, AsyncWriter, DataError, Packet, Reader, TextComponentBuilder, Writer}; @@ -153,6 +155,6 @@ async fn the_configuration(conn: &mut Connection) -> Result<(), PacketError> { let packet = Packet::empty(clientbound::configuration::FINISH); conn.write_packet(packet).await?; loop { - + sleep(Duration::from_secs(1)); } } \ No newline at end of file diff --git a/src/data/mod.rs b/src/data/mod.rs index 6801428..432b7f9 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -12,7 +12,6 @@ mod writer; mod packet; mod packet_id; mod component; -mod nbt; #[derive(Debug)] pub enum DataError { @@ -25,7 +24,7 @@ pub enum DataError { DeSerializationError, ZlibError, NBTError, - NBTCompoundError, + // NBTCompoundError, } pub type Buffer = Cursor>; @@ -49,5 +48,4 @@ pub use async_writer::*; pub use writer::*; pub use packet::*; pub use packet_id::{clientbound, serverbound}; -pub use component::*; -pub use nbt::*; \ No newline at end of file +pub use component::*; \ No newline at end of file diff --git a/src/data/nbt.rs b/src/data/nbt.rs deleted file mode 100644 index c248d23..0000000 --- a/src/data/nbt.rs +++ /dev/null @@ -1,154 +0,0 @@ -use std::{collections::HashMap, io::Cursor}; - -use super::{DataError, Reader}; - - - - -const TAG_END: u8 = 0; -const TAG_BYTE: u8 = 1; -const TAG_SHORT: u8 = 2; -const TAG_INT: u8 = 3; -const TAG_LONG: u8 = 4; -const TAG_FLOAT: u8 = 5; -const TAG_DOUBLE: u8 = 6; -const TAG_BYTE_ARRAY: u8 = 7; -const TAG_STRING: u8 = 8; -const TAG_LIST: u8 = 9; -const TAG_COMPOUND: u8 = 10; -const TAG_INT_ARRAY: u8 = 11; -const TAG_LONG_ARRAY: u8 = 12; - -#[derive(Debug)] -pub enum TAG { - END, - COMPOUND(HashMap), - STRING(String), - BYTE(i8), - SHORT(i16), - INT(i32), - LONG(i64), - FLOAT(f32), - DOUBLE(f64), - LIST(Vec), - BYTE_ARRAY(Vec), - INT_ARRAY(Vec), - LONG_ARRAY(Vec), -} - -#[derive(Debug)] -pub struct NBT { - pub key: Option, - pub value: TAG, -} - -impl NBT { - fn new(key: Option, value: TAG) -> Self { - Self { key, value } - } -} - -pub trait NBT_Reader { - fn _read_nbt(&mut self, root: bool, inet: bool, byte: Option) -> Result; - fn read_nbt(&mut self, inet: bool) -> Result; -} - - -impl NBT_Reader for Cursor> { - fn read_nbt(&mut self, inet: bool) -> Result { - self._read_nbt(true, inet, None) - } - - fn _read_nbt(&mut self, root: bool, inet: bool, b: Option) -> Result { - let byte = match b { Some(v) => v, None => self.read_byte()? }; - let mut key: Option; - - if byte == TAG_END { - return Ok(NBT::new(None, TAG::END)); - } - - if root & (byte != TAG_COMPOUND) { - return Err(DataError::NBTCompoundError) - } - - if root & inet { - key = None; - } else { - let size = self.read_short()? as usize; - let buf = self.read_bytes(size)?; - key = Some(String::from_utf8_lossy(&buf).to_string()); - } - - if b.is_some() { - key = None; - } - - match byte { - TAG_BYTE => { Ok(NBT::new(key, TAG::BYTE(self.read_signed_byte()?))) }, - TAG_SHORT => { Ok(NBT::new(key, TAG::SHORT(self.read_signed_short()?))) }, - TAG_INT => { Ok(NBT::new(key, TAG::INT(self.read_signed_int()?))) }, - TAG_LONG => { Ok(NBT::new(key, TAG::LONG(self.read_signed_long()?))) }, - TAG_FLOAT => { Ok(NBT::new(key, TAG::FLOAT(self.read_float()?))) }, - TAG_DOUBLE => { Ok(NBT::new(key, TAG::DOUBLE(self.read_double()?))) }, - - TAG_STRING => { - let size = self.read_short()? as usize; - let buf = self.read_bytes(size)?; - Ok(NBT::new(key, TAG::STRING(String::from_utf8_lossy(&buf).to_string()))) - }, - - TAG_BYTE_ARRAY => { - let size = self.read_signed_int()? as usize; - let buf = self.read_bytes(size)?.into_iter().map(|x| x as i8).collect(); - Ok(NBT::new(key, TAG::BYTE_ARRAY(buf))) - }, - - TAG_INT_ARRAY => { - let count = self.read_signed_int()? as usize; - let mut buf: Vec = Vec::with_capacity(count); - for _ in 0..count { - buf.push(self.read_signed_int()?); - } - Ok(NBT::new(key, TAG::INT_ARRAY(buf))) - }, - - TAG_LONG_ARRAY => { - let count = self.read_signed_int()? as usize; - let mut buf: Vec = Vec::with_capacity(count); - for _ in 0..count { - buf.push(self.read_signed_long()?); - } - Ok(NBT::new(key, TAG::LONG_ARRAY(buf))) - }, - - TAG_LIST => { - // byte = self.read_byte()?; - let mut buf: Vec = Vec::new(); - let count = self.read_signed_int()? as usize; - println!("List count: {count}"); - for _ in 0..count { - buf.push(self._read_nbt(false, inet, None)?); - } - Ok(NBT::new(key, TAG::LIST(buf))) - }, - - TAG_COMPOUND => { - // println!("COMPOUND"); - let mut map = HashMap::new(); - loop { - let nbt = self._read_nbt(false, inet, None)?; - if matches!(nbt.value, TAG::END) { break; }; - let Some(key) = nbt.key else { continue; }; - map.insert(key, nbt.value); - } - Ok(NBT::new(key, TAG::COMPOUND(map))) - } - - _ => { - // println!("o: {other}"); - Err(DataError::NBTError) - } - } - - } -} \ No newline at end of file diff --git a/src/data/reader.rs b/src/data/reader.rs index e5e25ae..8008d7a 100644 --- a/src/data/reader.rs +++ b/src/data/reader.rs @@ -73,12 +73,16 @@ pub trait Reader { Ok(self.read_short()? as i16) } - fn read_signed_int(&mut self) -> Result { - Ok(i32::from_be_bytes( + fn read_int(&mut self) -> Result { + Ok(u32::from_be_bytes( self.read_bytes(4)?.try_into().unwrap() )) } + fn read_signed_int(&mut self) -> Result { + Ok(self.read_int()? as i32) + } + fn read_long(&mut self) -> Result { Ok(u64::from_be_bytes( self.read_bytes(8)?.try_into().unwrap() diff --git a/src/data/writer.rs b/src/data/writer.rs index ba5a9ee..10a2b67 100644 --- a/src/data/writer.rs +++ b/src/data/writer.rs @@ -74,10 +74,12 @@ pub trait Writer { self.write_short(value as u16) } - fn write_string(&mut self, value: &str) -> Result<(), DataError> { - self.write_varint(value.len() as i32)?; - self.write_bytes(value.as_bytes())?; - Ok(()) + fn write_int(&mut self, value: u32) -> Result<(), DataError> { + self.write_bytes(&value.to_be_bytes()) + } + + fn write_signed_int(&mut self, value: i32) -> Result<(), DataError> { + self.write_int(value as u32) } fn write_long(&mut self, value: u64) -> Result<(), DataError> { @@ -88,6 +90,20 @@ pub trait Writer { self.write_long(value as u64) } + fn write_float(&mut self, value: f32) -> Result<(), DataError> { + self.write_bytes(&value.to_be_bytes()) + } + + fn write_double(&mut self, value: f64) -> Result<(), DataError> { + self.write_bytes(&value.to_be_bytes()) + } + + fn write_string(&mut self, value: &str) -> Result<(), DataError> { + self.write_varint(value.len() as i32)?; + self.write_bytes(value.as_bytes())?; + Ok(()) + } + fn write_uuid(&mut self, value: u128) -> Result<(), DataError> { self.write_bytes(&value.to_be_bytes()) } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..aa86303 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,4 @@ +pub mod inet; +pub mod data; +pub mod cycle; +pub mod nbt; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index f01b6d6..a3786f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,41 +1,16 @@ +use rust_mc_serv::{cycle, inet::Server}; -use std::fs; - -use data::{Buffer, NBT_Reader, Reader}; -use inet::Server; - -mod data; -mod inet; -mod cycle; - #[tokio::main] async fn main() { - // let Ok(server) = Server::new("127.0.0.1:25565").await else { - // println!("Не удалось забиндить сервер"); - // return; - // }; + let Ok(server) = Server::new("127.0.0.1:25565").await else { + println!("Не удалось забиндить сервер"); + return; + }; - // loop { - // let stream = server.accept().await; - // tokio::spawn(cycle::main(stream)); - // } - - let mut buf = Buffer::new(fs::read("servers.dat").unwrap()); - - // let mut buf = Buffer::new(vec![ - // 0x0a, - // // 0x00, 0x0b, - // // 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, - // 0x08, - // 0x00, 0x04, - // 0x6e, 0x61, 0x6d, 0x65, - // 0x00, 0x05, - // 0x42, 0x61, 0x6e, 0x61, 0x6e, - // 0x00 - // ]); - - let nbt = buf.read_nbt(false).unwrap(); - println!("{nbt:?}"); + loop { + let stream = server.accept().await; + tokio::spawn(cycle::main(stream)); + } } diff --git a/src/nbt/mod.rs b/src/nbt/mod.rs new file mode 100644 index 0000000..3fe33fc --- /dev/null +++ b/src/nbt/mod.rs @@ -0,0 +1,25 @@ +pub mod reader; +pub mod writer; +mod tags; + +use std::collections::HashMap; + +pub use reader::*; +pub use writer::*; + +#[derive(Debug)] +pub enum Tag { + End, + Byte(i8), + Short(i16), + Int(i32), + Long(i64), + Float(f32), + Double(f64), + ByteArray(Vec), + String(String), + List(Vec), + Compound(HashMap), + IntArray(Vec), + LongArray(Vec), +} \ No newline at end of file diff --git a/src/nbt/reader.rs b/src/nbt/reader.rs new file mode 100644 index 0000000..8044885 --- /dev/null +++ b/src/nbt/reader.rs @@ -0,0 +1,98 @@ +use std::{collections::HashMap, io::Seek}; + +use crate::data::{Buffer, DataError, Reader}; + +use super::{tags::*, Tag}; + +trait NbtReaderInternal { + fn read_value(&mut self, t: u8) -> Result; + fn read_compound(&mut self) -> Result; + fn read_list(&mut self) -> Result; +} + +impl NbtReaderInternal for Buffer { + fn read_value(&mut self, t: u8) -> Result { + match t { + TAG_END => { self.read_byte()?; Ok(Tag::End) } + TAG_BYTE => { Ok(Tag::Byte(self.read_signed_byte()?)) } + TAG_SHORT => { Ok(Tag::Short(self.read_signed_short()?)) } + TAG_INT => { Ok(Tag::Int(self.read_signed_int()?)) } + TAG_LONG => { Ok(Tag::Long(self.read_signed_long()?)) } + TAG_FLOAT => { Ok(Tag::Float(self.read_float()?)) } + TAG_DOUBLE => { Ok(Tag::Double(self.read_double()?)) } + TAG_BYTE_ARRAY => { + let size = self.read_int()? as usize; + Ok(Tag::ByteArray(self.read_bytes(size)?.into_iter().map(|x| x as i8).collect())) + } + TAG_STRING => { + let size = self.read_short()? as usize; + let data = self.read_bytes(size)?; + Ok(Tag::String(String::from_utf8_lossy(&data).to_string())) + } + TAG_LIST => { self.read_list() } + TAG_COMPOUND => { self.read_compound() } + TAG_INT_ARRAY => { + let size = self.read_int()? as usize; + let mut data = Vec::new(); + for _ in 0..size { + data.push(self.read_signed_int()?); + } + Ok(Tag::IntArray(data)) + } + TAG_LONG_ARRAY => { + let size = self.read_int()? as usize; + let mut data = Vec::new(); + for _ in 0..size { + data.push(self.read_signed_long()?); + } + Ok(Tag::LongArray(data)) + } + other => { + println!("Неверный тэг: {other}"); + Err(DataError::NBTError) + } + } + } + + fn read_compound(&mut self) -> Result { + let mut map = HashMap::new(); + loop { + let type_of_value = self.read_byte()?; + if type_of_value == TAG_END { break; } + let size = self.read_short()? as usize; + let key = String::from_utf8_lossy(&self.read_bytes(size)?).to_string(); + map.insert(key, self.read_value(type_of_value)?); + } + Ok(Tag::Compound(map)) + } + + fn read_list(&mut self) -> Result { + let type_of_list = self.read_byte()?; + let size_of_list = self.read_signed_int()?; + let mut list = Vec::new(); + if size_of_list <= 0 { return Ok(Tag::List(list)); }; + for _ in 0..size_of_list { + list.push(self.read_value(type_of_list)?); + } + Ok(Tag::List(list)) + } +} + +pub trait NbtReader { + fn read_nbt(&mut self) -> Result; +} + +impl NbtReader for Buffer { + fn read_nbt(&mut self) -> Result { + + if self.read_byte()? != TAG_COMPOUND { + return Err(DataError::NBTError); + } + + if self.read_short()? != 0 { + let _ = self.seek_relative(-2); + } + + self.read_compound() + } +} \ No newline at end of file diff --git a/src/nbt/tags.rs b/src/nbt/tags.rs new file mode 100644 index 0000000..0170f9b --- /dev/null +++ b/src/nbt/tags.rs @@ -0,0 +1,16 @@ + + + +pub const TAG_END: u8 = 0; +pub const TAG_BYTE: u8 = 1; +pub const TAG_SHORT: u8 = 2; +pub const TAG_INT: u8 = 3; +pub const TAG_LONG: u8 = 4; +pub const TAG_FLOAT: u8 = 5; +pub const TAG_DOUBLE: u8 = 6; +pub const TAG_BYTE_ARRAY: u8 = 7; +pub const TAG_STRING: u8 = 8; +pub const TAG_LIST: u8 = 9; +pub const TAG_COMPOUND: u8 = 10; +pub const TAG_INT_ARRAY: u8 = 11; +pub const TAG_LONG_ARRAY: u8 = 12; \ No newline at end of file diff --git a/src/nbt/writer.rs b/src/nbt/writer.rs new file mode 100644 index 0000000..d032e23 --- /dev/null +++ b/src/nbt/writer.rs @@ -0,0 +1,109 @@ +use std::collections::HashMap; + +use crate::data::{Buffer, DataError, Writer}; + +use super::{tags::*, Tag}; + + + +fn get_id(tag: &Tag) -> u8 { + match tag { + Tag::End => TAG_END, + Tag::Byte(_) => TAG_BYTE, + Tag::Short(_) => TAG_SHORT, + Tag::Int(_) => TAG_INT, + Tag::Long(_) => TAG_LONG, + Tag::Float(_) => TAG_FLOAT, + Tag::Double(_) => TAG_DOUBLE, + Tag::ByteArray(_) => TAG_BYTE_ARRAY, + Tag::String(_) => TAG_STRING, + Tag::List(_) => TAG_LIST, + Tag::Compound(_) => TAG_COMPOUND, + Tag::IntArray(_) => TAG_INT_ARRAY, + Tag::LongArray(_) => TAG_LONG_ARRAY, + } +} + +trait NbtWriterInternal { + fn write_value(&mut self, tag: Tag) -> Result<(), DataError>; + fn write_compound(&mut self, map: HashMap) -> Result<(), DataError>; + fn write_list(&mut self, list: Vec) -> Result<(), DataError>; +} + +impl NbtWriterInternal for Buffer { + fn write_value(&mut self, tag: Tag) -> Result<(), DataError> { + match tag { + Tag::End => self.write_byte(0)?, + Tag::Byte(value) => self.write_signed_byte(value)?, + Tag::Short(value) => self.write_signed_short(value)?, + Tag::Int(value) => self.write_signed_int(value)?, + Tag::Long(value) => self.write_signed_long(value)?, + Tag::Float(value) => self.write_float(value)?, + Tag::Double(value) => self.write_double(value)?, + Tag::ByteArray(value) => { + self.write_int(value.len() as u32)?; + let data: Vec = value.into_iter().map(|x| x as u8).collect(); + self.write_bytes(&data)?; + } + Tag::String(value) => { + self.write_short(value.len() as u16)?; + self.write_bytes(value.as_bytes())?; + } + Tag::List(list) => self.write_list(list)?, + Tag::Compound(map) => self.write_compound(map)?, + Tag::IntArray(list) => { + self.write_int(list.len() as u32)?; + for value in list { + self.write_signed_int(value)?; + } + } + Tag::LongArray(list) => { + self.write_int(list.len() as u32)?; + for value in list { + self.write_signed_long(value)?; + } + } + } + Ok(()) + } + + fn write_compound(&mut self, map: HashMap) -> Result<(), DataError> { + for (key, tag) in map { + self.write_byte(get_id(&tag))?; + self.write_short(key.len() as u16)?; + self.write_bytes(key.as_bytes())?; + self.write_value(tag)?; + } + Ok(()) + } + + fn write_list(&mut self, list: Vec) -> Result<(), DataError> { + let type_of_list = match list.as_slice() { + [] => 0, [first, rest @ ..] => { + let first_id = get_id(first); + if rest.iter().all(|tag| get_id(tag) == first_id) { first_id } + else { return Err(DataError::NBTError) } + } + }; + self.write_byte(type_of_list)?; + self.write_int(list.len() as u32)?; + for tag in list { + self.write_value(tag)?; + } + Ok(()) + } +} + +pub trait NbtWriter { + fn write_nbt(&mut self, tag: Tag, to_file: bool) -> Result<(), DataError>; +} + +impl NbtWriter for Buffer { + fn write_nbt(&mut self, tag: Tag, to_file: bool) -> Result<(), DataError> { + let Tag::Compound(map) = tag else { return Err(DataError::NBTError); }; + self.write_byte(TAG_COMPOUND)?; + if to_file { self.write_short(0)? }; + self.write_compound(map)?; + Ok(()) + } +} \ No newline at end of file