diff --git a/.vscode/settings.json b/.vscode/settings.json index 01fd4e6..7f05113 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,19 @@ { "editor.fontFamily": "Fira Code", "editor.fontLigatures": true, - "editor.tabSize": 4, + + "editor.tabSize": 2, + "editor.insertSpaces": false, + "editor.detectIndentation": false, + "editor.formatOnSave": true, + "editor.formatOnPaste": true, + + "[rust]": { + "editor.defaultFormatter": "rust-lang.rust-analyzer", + "editor.formatOnSave": true + }, + "rust-analyzer.rustfmt.extraArgs": [ + "--config", + "tab_spaces=2" + ] } \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..8cef326 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +# Пример настроек +tab_spaces = 2 +hard_tabs = true \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e34a01b..8ee9002 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,14 +3,14 @@ use std::{env::args, path::PathBuf, sync::Arc}; use log::{debug, error, info}; use rust_mc_proto::Packet; use server::{ - ServerError, - config::Config, - context::ServerContext, - data::text_component::TextComponent, - event::{Listener, PacketHandler}, - player::context::ClientContext, - protocol::ConnectionState, - start_server, + ServerError, + config::Config, + context::ServerContext, + data::text_component::TextComponent, + event::{Listener, PacketHandler}, + player::context::ClientContext, + protocol::ConnectionState, + start_server, }; pub mod server; @@ -18,13 +18,13 @@ pub mod server; struct ExampleListener; impl Listener for ExampleListener { - fn on_status( - &self, - client: Arc, - response: &mut String, - ) -> Result<(), ServerError> { - *response = format!( - "{{ + fn on_status( + &self, + client: Arc, + response: &mut String, + ) -> Result<(), ServerError> { + *response = format!( + "{{ \"version\": {{ \"name\": \"idk\", \"protocol\": {} @@ -43,124 +43,124 @@ impl Listener for ExampleListener { \"favicon\": \"data:image/png;base64,\", \"enforcesSecureChat\": false }}", - client.handshake().unwrap().protocol_version, - TextComponent::builder() - .text("Hello World! ") - .extra(vec![ - TextComponent::builder() - .text("Protocol: ") - .color("gold") - .extra(vec![ - TextComponent::builder() - .text(&client.handshake().unwrap().protocol_version.to_string()) - .underlined(true) - .build() - ]) - .build(), - TextComponent::builder() - .text("\nServer Addr: ") - .color("green") - .extra(vec![ - TextComponent::builder() - .text(&format!( - "{}:{}", - client.handshake().unwrap().server_address, - client.handshake().unwrap().server_port - )) - .underlined(true) - .build() - ]) - .build() - ]) - .build() - .as_json()? - ); + client.handshake().unwrap().protocol_version, + TextComponent::builder() + .text("Hello World! ") + .extra(vec![ + TextComponent::builder() + .text("Protocol: ") + .color("gold") + .extra(vec![ + TextComponent::builder() + .text(&client.handshake().unwrap().protocol_version.to_string()) + .underlined(true) + .build() + ]) + .build(), + TextComponent::builder() + .text("\nServer Addr: ") + .color("green") + .extra(vec![ + TextComponent::builder() + .text(&format!( + "{}:{}", + client.handshake().unwrap().server_address, + client.handshake().unwrap().server_port + )) + .underlined(true) + .build() + ]) + .build() + ]) + .build() + .as_json()? + ); - Ok(()) - } + Ok(()) + } } struct ExamplePacketHandler; impl PacketHandler for ExamplePacketHandler { - fn on_incoming_packet( - &self, - client: Arc, - packet: &mut Packet, - _: &mut bool, - state: ConnectionState, - ) -> Result<(), ServerError> { - debug!( - "{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", - client.addr.clone(), - packet.id(), - state, - packet.len() - ); + fn on_incoming_packet( + &self, + client: Arc, + packet: &mut Packet, + _: &mut bool, + state: ConnectionState, + ) -> Result<(), ServerError> { + debug!( + "{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", + client.addr.clone(), + packet.id(), + state, + packet.len() + ); - Ok(()) - } + Ok(()) + } - fn on_outcoming_packet( - &self, - client: Arc, - packet: &mut Packet, - _: &mut bool, - state: ConnectionState, - ) -> Result<(), ServerError> { - debug!( - "{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", - client.addr.clone(), - packet.id(), - state, - packet.len() - ); + fn on_outcoming_packet( + &self, + client: Arc, + packet: &mut Packet, + _: &mut bool, + state: ConnectionState, + ) -> Result<(), ServerError> { + debug!( + "{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", + client.addr.clone(), + packet.id(), + state, + packet.len() + ); - Ok(()) - } + Ok(()) + } } fn main() { - // Инициализируем логи - // Чтобы читать debug-логи, юзаем `RUST_LOG=debug cargo run` - colog::init(); + // Инициализируем логи + // Чтобы читать debug-логи, юзаем `RUST_LOG=debug cargo run` + colog::init(); - // Получение аргументов - let exec = args().next().expect("Неизвестная система"); - let args = args().skip(1).collect::>(); + // Получение аргументов + let exec = args().next().expect("Неизвестная система"); + let args = args().skip(1).collect::>(); - if args.len() > 1 { - info!("Использование: {exec} [путь до файла конфигурации]"); - return; - } + if args.len() > 1 { + info!("Использование: {exec} [путь до файла конфигурации]"); + return; + } - // Берем путь из аргумента либо по дефолту берем "./server.toml" - let config_path = PathBuf::from(args.get(0).unwrap_or(&"server.toml".to_string())); + // Берем путь из аргумента либо по дефолту берем "./server.toml" + let config_path = PathBuf::from(args.get(0).unwrap_or(&"server.toml".to_string())); - // Чтение конфига, если ошибка - выводим - let config = match Config::load_from_file(config_path) { - Some(config) => config, - None => { - error!("Ошибка чтения конфигурации"); - return; - } - }; + // Чтение конфига, если ошибка - выводим + let config = match Config::load_from_file(config_path) { + Some(config) => config, + None => { + error!("Ошибка чтения конфигурации"); + return; + } + }; - // Делаем немутабельную потокобезопасную ссылку на конфиг - // Впринципе можно и просто клонировать сам конфиг в каждый сука поток ебать того рот ебать блять - // но мы этого делать не будем чтобы не было мемори лик лишнего - let config = Arc::new(config); + // Делаем немутабельную потокобезопасную ссылку на конфиг + // Впринципе можно и просто клонировать сам конфиг в каждый сука поток ебать того рот ебать блять + // но мы этого делать не будем чтобы не было мемори лик лишнего + let config = Arc::new(config); - // Создаем контекст сервера - // Передается во все подключения - let mut server = ServerContext::new(config); + // Создаем контекст сервера + // Передается во все подключения + let mut server = ServerContext::new(config); - server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера - server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера + server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера + server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера - // Бетонируем сервер контекст от изменений - let server = Arc::new(server); + // Бетонируем сервер контекст от изменений + let server = Arc::new(server); - // Запускаем сервер из специально отведенной под это дело функцией - start_server(server); + // Запускаем сервер из специально отведенной под это дело функцией + start_server(server); } diff --git a/src/server/config.rs b/src/server/config.rs index a2f803b..8977e04 100644 --- a/src/server/config.rs +++ b/src/server/config.rs @@ -5,48 +5,48 @@ use serde_default::DefaultFromSerde; #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] pub struct BindConfig { - #[serde(default = "default_host")] - pub host: String, - #[serde(default = "default_timeout")] - pub timeout: u64, + #[serde(default = "default_host")] + pub host: String, + #[serde(default = "default_timeout")] + pub timeout: u64, } #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] pub struct ServerConfig { - #[serde(default)] - pub online_mode: bool, - #[serde(default = "default_compression")] - pub compression_threshold: Option, + #[serde(default)] + pub online_mode: bool, + #[serde(default = "default_compression")] + pub compression_threshold: Option, } #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] pub struct Config { - #[serde(default)] - pub bind: BindConfig, - #[serde(default)] - pub server: ServerConfig, + #[serde(default)] + pub bind: BindConfig, + #[serde(default)] + pub server: ServerConfig, } fn default_host() -> String { - "127.0.0.1:25565".to_string() + "127.0.0.1:25565".to_string() } fn default_timeout() -> u64 { - 5 + 5 } fn default_compression() -> Option { - Some(256) + Some(256) } impl Config { - pub fn load_from_file(path: PathBuf) -> Option { - if !fs::exists(&path).unwrap_or_default() { - let table = Config::default(); - fs::create_dir_all(&path.parent()?).ok()?; - fs::write(&path, toml::to_string_pretty(&table).ok()?).ok()?; - return Some(table); - } - let content = fs::read_to_string(&path).ok()?; - let table = toml::from_str::(&content).ok()?; - Some(table) - } + pub fn load_from_file(path: PathBuf) -> Option { + if !fs::exists(&path).unwrap_or_default() { + let table = Config::default(); + fs::create_dir_all(&path.parent()?).ok()?; + fs::write(&path, toml::to_string_pretty(&table).ok()?).ok()?; + return Some(table); + } + let content = fs::read_to_string(&path).ok()?; + let table = toml::from_str::(&content).ok()?; + Some(table) + } } diff --git a/src/server/context.rs b/src/server/context.rs index f54c795..5d0a706 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -5,87 +5,90 @@ use itertools::Itertools; use uuid::Uuid; use super::{ - config::Config, - event::{Listener, PacketHandler}, - player::context::ClientContext, + config::Config, + event::{Listener, PacketHandler}, + player::context::ClientContext, }; // Контекст сервера // Должен быть обернут в Arc для передачи между потоками pub struct ServerContext { - pub config: Arc, - pub clients: DashMap>, - listeners: Vec>, - handlers: Vec>, + pub config: Arc, + pub clients: DashMap>, + listeners: Vec>, + handlers: Vec>, } impl ServerContext { - pub fn new(config: Arc) -> ServerContext { - ServerContext { - config, - listeners: Vec::new(), - handlers: Vec::new(), - clients: DashMap::new(), - } - } + pub fn new(config: Arc) -> ServerContext { + ServerContext { + config, + listeners: Vec::new(), + handlers: Vec::new(), + clients: DashMap::new(), + } + } - pub fn get_player_by_uuid(self: &Arc, uuid: Uuid) -> Option> { - self.clients - .iter() - .find(|o| { - let info = o.player_info(); - if let Some(info) = info { - info.uuid == uuid - } else { - false - } - }) - .map(|o| o.clone()) - } + pub fn get_player_by_uuid(self: &Arc, uuid: Uuid) -> Option> { + self + .clients + .iter() + .find(|o| { + let info = o.player_info(); + if let Some(info) = info { + info.uuid == uuid + } else { + false + } + }) + .map(|o| o.clone()) + } - pub fn get_player_by_name(self: &Arc, name: &str) -> Option> { - self.clients - .iter() - .find(|o| { - let info = o.player_info(); - if let Some(info) = info { - info.name == name - } else { - false - } - }) - .map(|o| o.clone()) - } + pub fn get_player_by_name(self: &Arc, name: &str) -> Option> { + self + .clients + .iter() + .find(|o| { + let info = o.player_info(); + if let Some(info) = info { + info.name == name + } else { + false + } + }) + .map(|o| o.clone()) + } - pub fn players(self: &Arc) -> Vec> { - self.clients - .iter() - .filter(|o| o.player_info().is_some()) - .map(|o| o.clone()) - .collect() - } + pub fn players(self: &Arc) -> Vec> { + self + .clients + .iter() + .filter(|o| o.player_info().is_some()) + .map(|o| o.clone()) + .collect() + } - pub fn add_packet_handler(&mut self, handler: Box) { - self.handlers.push(handler); - } + pub fn add_packet_handler(&mut self, handler: Box) { + self.handlers.push(handler); + } - pub fn add_listener(&mut self, listener: Box) { - self.listeners.push(listener); - } + pub fn add_listener(&mut self, listener: Box) { + self.listeners.push(listener); + } - pub fn packet_handlers(self: &Arc, sort_by: F) -> Vec<&Box> - where - K: Ord, - F: FnMut(&&Box) -> K, - { - self.handlers.iter().sorted_by_key(sort_by).collect_vec() - } + pub fn packet_handlers(self: &Arc, sort_by: F) -> Vec<&Box> + where + K: Ord, + F: FnMut(&&Box) -> K, + { + self.handlers.iter().sorted_by_key(sort_by).collect_vec() + } - pub fn listeners(self: &Arc, sort_by: F) -> Vec<&Box> - where - K: Ord, - F: FnMut(&&Box) -> K, - { - self.listeners.iter().sorted_by_key(sort_by).collect_vec() - } + pub fn listeners(self: &Arc, sort_by: F) -> Vec<&Box> + where + K: Ord, + F: FnMut(&&Box) -> K, + { + self.listeners.iter().sorted_by_key(sort_by).collect_vec() + } } diff --git a/src/server/data/mod.rs b/src/server/data/mod.rs index 3fccec6..47ea8f6 100644 --- a/src/server/data/mod.rs +++ b/src/server/data/mod.rs @@ -9,42 +9,43 @@ pub mod text_component; // Трейт для чтения NBT-совместимых приколов pub trait ReadWriteNBT: DataReader + DataWriter { - fn read_nbt(&mut self) -> Result; - fn write_nbt(&mut self, val: &T) -> Result<(), ServerError>; + fn read_nbt(&mut self) -> Result; + fn write_nbt(&mut self, val: &T) -> Result<(), ServerError>; } impl ReadWriteNBT for Packet { - fn read_nbt(&mut self) -> Result { - let mut data = Vec::new(); - let pos = self.get_ref().position(); - self.get_mut() - .read_to_end(&mut data) - .map_err(|_| ServerError::DeNbt)?; - let (remaining, value) = - craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeNbt)?; - self.get_mut() - .set_position(pos + (data.len() - remaining.len()) as u64); - Ok(value) - } + fn read_nbt(&mut self) -> Result { + let mut data = Vec::new(); + let pos = self.get_ref().position(); + self + .get_mut() + .read_to_end(&mut data) + .map_err(|_| ServerError::DeNbt)?; + let (remaining, value) = craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeNbt)?; + self + .get_mut() + .set_position(pos + (data.len() - remaining.len()) as u64); + Ok(value) + } - fn write_nbt(&mut self, val: &DynNBT) -> Result<(), ServerError> { - craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerNbt)?; - Ok(()) - } + fn write_nbt(&mut self, val: &DynNBT) -> Result<(), ServerError> { + craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerNbt)?; + Ok(()) + } } pub trait ReadWritePosition: DataReader + DataWriter { - fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError>; - fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError>; + fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError>; + fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError>; } impl ReadWritePosition for Packet { - fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError> { - let val = self.read_long()?; - Ok((val >> 38, val << 52 >> 52, val << 26 >> 38)) - } + fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError> { + let val = self.read_long()?; + Ok((val >> 38, val << 52 >> 52, val << 26 >> 38)) + } - fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError> { - Ok(self.write_long(((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF))?) - } + fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError> { + Ok(self.write_long(((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF))?) + } } diff --git a/src/server/data/text_component.rs b/src/server/data/text_component.rs index c5095b9..3f63dc2 100644 --- a/src/server/data/text_component.rs +++ b/src/server/data/text_component.rs @@ -12,171 +12,173 @@ use super::ReadWriteNBT; #[derive(Debug, Serialize, Deserialize, Clone)] #[skip_serializing_none] pub struct TextComponent { - pub text: String, - pub color: Option, - pub bold: Option, - pub italic: Option, - pub underlined: Option, - pub strikethrough: Option, - pub obfuscated: Option, - pub extra: Option>, - // TODO: добавить все остальные стандартные поля для текст-компонента типа клик ивентов и сделать отдельный структ для транслейт компонент + pub text: String, + pub color: Option, + pub bold: Option, + pub italic: Option, + pub underlined: Option, + pub strikethrough: Option, + pub obfuscated: Option, + pub extra: Option>, + // TODO: добавить все остальные стандартные поля для текст-компонента типа клик ивентов и сделать отдельный структ для транслейт компонент } impl TextComponent { - pub fn new(text: String) -> Self { - Self { - text, - color: None, - bold: None, - italic: None, - underlined: None, - strikethrough: None, - obfuscated: None, - extra: None, - } - } + pub fn new(text: String) -> Self { + Self { + text, + color: None, + bold: None, + italic: None, + underlined: None, + strikethrough: None, + obfuscated: None, + extra: None, + } + } - pub fn rainbow(text: String) -> TextComponent { - if text.is_empty() { - return TextComponent::new(text); - } + pub fn rainbow(text: String) -> TextComponent { + if text.is_empty() { + return TextComponent::new(text); + } - let children = text - .char_indices() - .map(|(i, c)| { - let hue = (i as f32) / (text.chars().count() as f32) * 360.0; - let hsl = Hsl::new(hue, 1.0, 0.5); - let rgb: Srgb = hsl.into_color(); - let r = (rgb.red * 255.0).round() as u8; - let g = (rgb.green * 255.0).round() as u8; - let b = (rgb.blue * 255.0).round() as u8; - let mut component = TextComponent::new(c.to_string()); - component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b)); - component - }) - .collect::>(); + let children = text + .char_indices() + .map(|(i, c)| { + let hue = (i as f32) / (text.chars().count() as f32) * 360.0; + let hsl = Hsl::new(hue, 1.0, 0.5); + let rgb: Srgb = hsl.into_color(); + let r = (rgb.red * 255.0).round() as u8; + let g = (rgb.green * 255.0).round() as u8; + let b = (rgb.blue * 255.0).round() as u8; + let mut component = TextComponent::new(c.to_string()); + component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b)); + component + }) + .collect::>(); - let mut parent = children[0].clone(); - parent.extra = Some(children[1..].to_vec()); - parent - } + let mut parent = children[0].clone(); + parent.extra = Some(children[1..].to_vec()); + parent + } - pub fn builder() -> TextComponentBuilder { - TextComponentBuilder::new() - } + pub fn builder() -> TextComponentBuilder { + TextComponentBuilder::new() + } - pub fn as_json(self) -> Result { - serde_json::to_string(&self).map_err(|_| ServerError::SerTextComponent) - } + pub fn as_json(self) -> Result { + serde_json::to_string(&self).map_err(|_| ServerError::SerTextComponent) + } - pub fn from_json(text: &str) -> Result { - serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent) - } + pub fn from_json(text: &str) -> Result { + serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent) + } } impl Default for TextComponent { - fn default() -> Self { - Self::new(String::new()) - } + fn default() -> Self { + Self::new(String::new()) + } } pub struct TextComponentBuilder { - text: String, - color: Option, - bold: Option, - italic: Option, - underlined: Option, - strikethrough: Option, - obfuscated: Option, - extra: Option>, + text: String, + color: Option, + bold: Option, + italic: Option, + underlined: Option, + strikethrough: Option, + obfuscated: Option, + extra: Option>, } impl TextComponentBuilder { - pub fn new() -> Self { - Self { - text: String::new(), - color: None, - bold: None, - italic: None, - underlined: None, - strikethrough: None, - obfuscated: None, - extra: None, - } - } + pub fn new() -> Self { + Self { + text: String::new(), + color: None, + bold: None, + italic: None, + underlined: None, + strikethrough: None, + obfuscated: None, + extra: None, + } + } - pub fn text(mut self, text: &str) -> Self { - self.text = text.to_string(); - self - } + pub fn text(mut self, text: &str) -> Self { + self.text = text.to_string(); + self + } - pub fn color(mut self, color: &str) -> Self { - self.color = Some(color.to_string()); - self - } + pub fn color(mut self, color: &str) -> Self { + self.color = Some(color.to_string()); + self + } - pub fn bold(mut self, bold: bool) -> Self { - self.bold = Some(bold); - self - } + pub fn bold(mut self, bold: bool) -> Self { + self.bold = Some(bold); + self + } - pub fn italic(mut self, italic: bool) -> Self { - self.italic = Some(italic); - self - } + pub fn italic(mut self, italic: bool) -> Self { + self.italic = Some(italic); + self + } - pub fn underlined(mut self, underlined: bool) -> Self { - self.underlined = Some(underlined); - self - } + pub fn underlined(mut self, underlined: bool) -> Self { + self.underlined = Some(underlined); + self + } - pub fn strikethrough(mut self, strikethrough: bool) -> Self { - self.strikethrough = Some(strikethrough); - self - } + pub fn strikethrough(mut self, strikethrough: bool) -> Self { + self.strikethrough = Some(strikethrough); + self + } - pub fn obfuscated(mut self, obfuscated: bool) -> Self { - self.obfuscated = Some(obfuscated); - self - } + pub fn obfuscated(mut self, obfuscated: bool) -> Self { + self.obfuscated = Some(obfuscated); + self + } - pub fn extra(mut self, extra: Vec) -> Self { - self.extra = Some(extra); - self - } + pub fn extra(mut self, extra: Vec) -> Self { + self.extra = Some(extra); + self + } - pub fn build(self) -> TextComponent { - TextComponent { - text: self.text, - color: self.color, - bold: self.bold, - italic: self.italic, - underlined: self.underlined, - strikethrough: self.strikethrough, - obfuscated: self.obfuscated, - extra: self.extra, - } - } + pub fn build(self) -> TextComponent { + TextComponent { + text: self.text, + color: self.color, + bold: self.bold, + italic: self.italic, + underlined: self.underlined, + strikethrough: self.strikethrough, + obfuscated: self.obfuscated, + extra: self.extra, + } + } } // Реализуем читалку-записывалку текст-компонентов для пакета impl ReadWriteNBT for Packet { - fn read_nbt(&mut self) -> Result { - let mut data = Vec::new(); - let pos = self.get_ref().position(); - self.get_mut() - .read_to_end(&mut data) - .map_err(|_| ServerError::DeTextComponent)?; - let (remaining, value) = - craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?; - self.get_mut() - .set_position(pos + (data.len() - remaining.len()) as u64); - Ok(value) - } + fn read_nbt(&mut self) -> Result { + let mut data = Vec::new(); + let pos = self.get_ref().position(); + self + .get_mut() + .read_to_end(&mut data) + .map_err(|_| ServerError::DeTextComponent)?; + let (remaining, value) = + craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?; + self + .get_mut() + .set_position(pos + (data.len() - remaining.len()) as u64); + Ok(value) + } - fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> { - craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?; - Ok(()) - } + fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> { + craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?; + Ok(()) + } } diff --git a/src/server/event/mod.rs b/src/server/event/mod.rs index e8ff602..d7a8f8d 100644 --- a/src/server/event/mod.rs +++ b/src/server/event/mod.rs @@ -37,12 +37,12 @@ macro_rules! trigger_event { } pub trait Listener: Sync + Send { - generate_handlers!(status, &mut String); - generate_handlers!(plugin_message, &str, &[u8]); + generate_handlers!(status, &mut String); + generate_handlers!(plugin_message, &str, &[u8]); } pub trait PacketHandler: Sync + Send { - generate_handlers!(incoming_packet, &mut Packet, &mut bool, ConnectionState); - generate_handlers!(outcoming_packet, &mut Packet, &mut bool, ConnectionState); - generate_handlers!(state, ConnectionState); + generate_handlers!(incoming_packet, &mut Packet, &mut bool, ConnectionState); + generate_handlers!(outcoming_packet, &mut Packet, &mut bool, ConnectionState); + generate_handlers!(state, ConnectionState); } diff --git a/src/server/mod.rs b/src/server/mod.rs index 58414ec..6166e8d 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -17,89 +17,89 @@ pub mod protocol; // Ошибки сервера #[derive(Debug)] pub enum ServerError { - UnexpectedPacket(u8), // Неожиданный пакет - Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto - ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection - SerTextComponent, // Ошибка при сериализации текст-компонента - DeTextComponent, // Ошибка при десериализации текст-компонента - SerNbt, // Ошибка при сериализации nbt - DeNbt, // Ошибка при десериализации nbt - UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper) - Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки + UnexpectedPacket(u8), // Неожиданный пакет + Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto + ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection + SerTextComponent, // Ошибка при сериализации текст-компонента + DeTextComponent, // Ошибка при десериализации текст-компонента + SerNbt, // Ошибка при сериализации nbt + DeNbt, // Ошибка при десериализации nbt + UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper) + Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки } impl Display for ServerError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&format!("{:?}", self)) - } + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&format!("{:?}", self)) + } } impl Error for ServerError {} // Делаем чтобы ProtocolError мог переделываться в наш ServerError impl From for ServerError { - fn from(error: ProtocolError) -> ServerError { - match error { - // Если просто закрыто соединение, переделываем в нашу ошибку этого - ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed, - // Все остальное просто засовываем в обертку - error => ServerError::Protocol(error), - } - } + fn from(error: ProtocolError) -> ServerError { + match error { + // Если просто закрыто соединение, переделываем в нашу ошибку этого + ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed, + // Все остальное просто засовываем в обертку + error => ServerError::Protocol(error), + } + } } pub fn start_server(server: Arc) { - // Биндим сервер где надо - let Ok(listener) = TcpListener::bind(&server.config.bind.host) else { - error!( - "Не удалось забиндить сервер на {}", - &server.config.bind.host - ); - return; - }; + // Биндим сервер где надо + let Ok(listener) = TcpListener::bind(&server.config.bind.host) else { + error!( + "Не удалось забиндить сервер на {}", + &server.config.bind.host + ); + return; + }; - info!("Сервер запущен на {}", &server.config.bind.host); + info!("Сервер запущен на {}", &server.config.bind.host); - while let Ok((stream, addr)) = listener.accept() { - let server = server.clone(); + while let Ok((stream, addr)) = listener.accept() { + let server = server.clone(); - thread::spawn(move || { - info!("Подключение: {}", addr); + thread::spawn(move || { + info!("Подключение: {}", addr); - // Установка таймаутов на чтение и запись - // По умолчанию пусть будет 5 секунд, надо будет сделать настройку через конфиг - stream - .set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout))) - .ignore(); - stream - .set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout))) - .ignore(); + // Установка таймаутов на чтение и запись + // По умолчанию пусть будет 5 секунд, надо будет сделать настройку через конфиг + stream + .set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout))) + .ignore(); + stream + .set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout))) + .ignore(); - // Оборачиваем стрим в майнкрафт конекшн лично для нашего удовольствия - let conn = MinecraftConnection::new(stream); + // Оборачиваем стрим в майнкрафт конекшн лично для нашего удовольствия + let conn = MinecraftConnection::new(stream); - // Создаем контекст клиента - // Передавется во все листенеры и хандлеры чтобы определять именно этот клиент - let client = Arc::new(ClientContext::new(server.clone(), conn)); + // Создаем контекст клиента + // Передавется во все листенеры и хандлеры чтобы определять именно этот клиент + let client = Arc::new(ClientContext::new(server.clone(), conn)); - // Добавляем клиента в список клиентов сервера - // Используем адрес как ключ, врятли ipv4 будет нам врать - server.clients.insert(client.addr, client.clone()); + // Добавляем клиента в список клиентов сервера + // Используем адрес как ключ, врятли ipv4 будет нам врать + server.clients.insert(client.addr, client.clone()); - // Обработка подключения - // Если ошибка -> выводим - match handle_connection(client.clone()) { - Ok(_) => {} - Err(ServerError::ConnectionClosed) => {} - Err(error) => { - error!("Ошибка подключения: {error:?}"); - } - }; + // Обработка подключения + // Если ошибка -> выводим + match handle_connection(client.clone()) { + Ok(_) => {} + Err(ServerError::ConnectionClosed) => {} + Err(error) => { + error!("Ошибка подключения: {error:?}"); + } + }; - // Удаляем клиента из списка клиентов - server.clients.remove(&client.addr); + // Удаляем клиента из списка клиентов + server.clients.remove(&client.addr); - info!("Отключение: {}", addr); - }); - } + info!("Отключение: {}", addr); + }); + } } diff --git a/src/server/player/context.rs b/src/server/player/context.rs index 7e1183d..7b0731a 100644 --- a/src/server/player/context.rs +++ b/src/server/player/context.rs @@ -1,7 +1,13 @@ use std::{ - collections::VecDeque, hash::Hash, net::{SocketAddr, TcpStream}, sync::{ - atomic::{AtomicBool, Ordering}, Arc, Mutex, RwLock - }, thread, time::Duration + collections::VecDeque, + hash::Hash, + net::{SocketAddr, TcpStream}, + sync::{ + Arc, Mutex, RwLock, + atomic::{AtomicBool, Ordering}, + }, + thread, + time::Duration, }; use rust_mc_proto::{MinecraftConnection, Packet}; @@ -14,304 +20,287 @@ use super::helper::ProtocolHelper; // Клиент контекст // Должен быть обернут в Arc для передачи между потоками pub struct ClientContext { - pub server: Arc, - pub addr: SocketAddr, - conn: RwLock>, - handshake: RwLock>, - client_info: RwLock>, - player_info: RwLock>, - state: RwLock, - packet_buffer: Mutex>, - read_loop: AtomicBool, - is_alive: AtomicBool, - position: RwLock<(f64, f64, f64)>, - velocity: RwLock<(f64, f64, f64)>, - rotation: RwLock<(f32, f32)>, + pub server: Arc, + pub addr: SocketAddr, + conn: RwLock>, + handshake: RwLock>, + client_info: RwLock>, + player_info: RwLock>, + state: RwLock, + packet_buffer: Mutex>, + read_loop: AtomicBool, + is_alive: AtomicBool, + position: RwLock<(f64, f64, f64)>, + velocity: RwLock<(f64, f64, f64)>, + rotation: RwLock<(f32, f32)>, } // Реализуем сравнение через адрес // IPv4 не должен обманывать, иначе у нас случится коллапс impl PartialEq for ClientContext { - fn eq(&self, other: &Self) -> bool { - self.addr == other.addr - } + fn eq(&self, other: &Self) -> bool { + self.addr == other.addr + } } impl Hash for ClientContext { - fn hash(&self, state: &mut H) { - self.addr.hash(state); - } + fn hash(&self, state: &mut H) { + self.addr.hash(state); + } } impl Eq for ClientContext {} impl ClientContext { - pub fn new(server: Arc, conn: MinecraftConnection) -> ClientContext { - ClientContext { - server, - addr: conn.get_ref().peer_addr().unwrap(), - conn: RwLock::new(conn), - handshake: RwLock::new(None), - client_info: RwLock::new(None), - player_info: RwLock::new(None), - state: RwLock::new(ConnectionState::Handshake), - packet_buffer: Mutex::new(VecDeque::new()), - read_loop: AtomicBool::new(false), - is_alive: AtomicBool::new(true), - position: RwLock::new((0.0, 0.0, 0.0)), - velocity: RwLock::new((0.0, 0.0, 0.0)), - rotation: RwLock::new((0.0, 0.0)) - } - } + pub fn new(server: Arc, conn: MinecraftConnection) -> ClientContext { + ClientContext { + server, + addr: conn.get_ref().peer_addr().unwrap(), + conn: RwLock::new(conn), + handshake: RwLock::new(None), + client_info: RwLock::new(None), + player_info: RwLock::new(None), + state: RwLock::new(ConnectionState::Handshake), + packet_buffer: Mutex::new(VecDeque::new()), + read_loop: AtomicBool::new(false), + is_alive: AtomicBool::new(true), + position: RwLock::new((0.0, 0.0, 0.0)), + velocity: RwLock::new((0.0, 0.0, 0.0)), + rotation: RwLock::new((0.0, 0.0)), + } + } - pub fn set_handshake(self: &Arc, handshake: Handshake) { - *self.handshake.write().unwrap() = Some(handshake); - } + pub fn set_handshake(self: &Arc, handshake: Handshake) { + *self.handshake.write().unwrap() = Some(handshake); + } - pub fn set_client_info(self: &Arc, client_info: ClientInfo) { - *self.client_info.write().unwrap() = Some(client_info); - } + pub fn set_client_info(self: &Arc, client_info: ClientInfo) { + *self.client_info.write().unwrap() = Some(client_info); + } - pub fn set_player_info(self: &Arc, player_info: PlayerInfo) { - *self.player_info.write().unwrap() = Some(player_info); - } + pub fn set_player_info(self: &Arc, player_info: PlayerInfo) { + *self.player_info.write().unwrap() = Some(player_info); + } - pub fn set_state(self: &Arc, state: ConnectionState) -> Result<(), ServerError> { - *self.state.write().unwrap() = state.clone(); + pub fn set_state(self: &Arc, state: ConnectionState) -> Result<(), ServerError> { + *self.state.write().unwrap() = state.clone(); - for handler in self - .server - .packet_handlers(|o| o.on_state_priority()) - .iter() - { - handler.on_state(self.clone(), state.clone())?; - } + for handler in self + .server + .packet_handlers(|o| o.on_state_priority()) + .iter() + { + handler.on_state(self.clone(), state.clone())?; + } - Ok(()) - } + Ok(()) + } - pub fn handshake(self: &Arc) -> Option { - self.handshake.read().unwrap().clone() - } + pub fn handshake(self: &Arc) -> Option { + self.handshake.read().unwrap().clone() + } - pub fn client_info(self: &Arc) -> Option { - self.client_info.read().unwrap().clone() - } + pub fn client_info(self: &Arc) -> Option { + self.client_info.read().unwrap().clone() + } - pub fn player_info(self: &Arc) -> Option { - self.player_info.read().unwrap().clone() - } + pub fn player_info(self: &Arc) -> Option { + self.player_info.read().unwrap().clone() + } - pub fn state(self: &Arc) -> ConnectionState { - self.state.read().unwrap().clone() - } + pub fn state(self: &Arc) -> ConnectionState { + self.state.read().unwrap().clone() + } - pub fn set_position(self: &Arc, position: (f64, f64, f64)) { - *self.position.write().unwrap() = position; - } + pub fn set_position(self: &Arc, position: (f64, f64, f64)) { + *self.position.write().unwrap() = position; + } - pub fn set_velocity(self: &Arc, velocity: (f64, f64, f64)) { - *self.velocity.write().unwrap() = velocity; - } + pub fn set_velocity(self: &Arc, velocity: (f64, f64, f64)) { + *self.velocity.write().unwrap() = velocity; + } - pub fn set_rotation(self: &Arc, rotation: (f32, f32)) { - *self.rotation.write().unwrap() = rotation; - } + pub fn set_rotation(self: &Arc, rotation: (f32, f32)) { + *self.rotation.write().unwrap() = rotation; + } - pub fn position(self: &Arc) -> (f64, f64, f64) { - self.position.read().unwrap().clone() - } + pub fn position(self: &Arc) -> (f64, f64, f64) { + self.position.read().unwrap().clone() + } - pub fn velocity(self: &Arc) -> (f64, f64, f64) { - self.velocity.read().unwrap().clone() - } + pub fn velocity(self: &Arc) -> (f64, f64, f64) { + self.velocity.read().unwrap().clone() + } - pub fn rotation(self: &Arc) -> (f32, f32) { - self.rotation.read().unwrap().clone() - } + pub fn rotation(self: &Arc) -> (f32, f32) { + self.rotation.read().unwrap().clone() + } - pub fn write_packet(self: &Arc, packet: &Packet) -> Result<(), ServerError> { - let state = self.state(); - let mut packet = packet.clone(); - let mut cancelled = false; - for handler in self - .server - .packet_handlers(|o| o.on_outcoming_packet_priority()) - .iter() - { - handler.on_outcoming_packet( - self.clone(), - &mut packet, - &mut cancelled, - state.clone(), - )?; - packet.get_mut().set_position(0); - } - if !cancelled { - match self.conn.write().unwrap().write_packet(&packet) { - Ok(_) => {}, - Err(e) => { - self.is_alive.store(false, Ordering::SeqCst); - return Err(e.into()); - } - }; - } - Ok(()) - } + pub fn write_packet(self: &Arc, packet: &Packet) -> Result<(), ServerError> { + let state = self.state(); + let mut packet = packet.clone(); + let mut cancelled = false; + for handler in self + .server + .packet_handlers(|o| o.on_outcoming_packet_priority()) + .iter() + { + handler.on_outcoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?; + packet.get_mut().set_position(0); + } + if !cancelled { + match self.conn.write().unwrap().write_packet(&packet) { + Ok(_) => {} + Err(e) => { + self.is_alive.store(false, Ordering::SeqCst); + return Err(e.into()); + } + }; + } + Ok(()) + } - pub fn run_read_loop( - self: &Arc - ) -> Result<(), ServerError> { - self.read_loop.store(true, Ordering::SeqCst); + pub fn run_read_loop(self: &Arc) -> Result<(), ServerError> { + self.read_loop.store(true, Ordering::SeqCst); - let mut conn = self.conn.read().unwrap().try_clone()?; // так можно делать т.к сокет это просто поинтер + let mut conn = self.conn.read().unwrap().try_clone()?; // так можно делать т.к сокет это просто поинтер - while self.is_alive() { - let mut packet = match conn.read_packet() { - Ok(v) => v, - Err(e) => { - self.is_alive.store(false, Ordering::SeqCst); - return Err(e.into()); - } - }; - let mut cancelled = false; - let state = self.state(); - for handler in self - .server - .packet_handlers(|o| o.on_incoming_packet_priority()) - .iter() - { - handler.on_incoming_packet( - self.clone(), - &mut packet, - &mut cancelled, - state.clone(), - )?; - packet.get_mut().set_position(0); - } - if !cancelled { - self.packet_buffer.lock().unwrap().push_back(packet); - } - } + while self.is_alive() { + let mut packet = match conn.read_packet() { + Ok(v) => v, + Err(e) => { + self.is_alive.store(false, Ordering::SeqCst); + return Err(e.into()); + } + }; + let mut cancelled = false; + let state = self.state(); + for handler in self + .server + .packet_handlers(|o| o.on_incoming_packet_priority()) + .iter() + { + handler.on_incoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?; + packet.get_mut().set_position(0); + } + if !cancelled { + self.packet_buffer.lock().unwrap().push_back(packet); + } + } - Ok(()) - } + Ok(()) + } - pub fn read_any_packet(self: &Arc) -> Result { - if self.read_loop.load(Ordering::SeqCst) { - loop { - if let Some(packet) = self.packet_buffer.lock().unwrap().pop_front() { - return Ok(packet); - } - thread::sleep(Duration::from_millis(10)); - } - } else { - let state = self.state(); + pub fn read_any_packet(self: &Arc) -> Result { + if self.read_loop.load(Ordering::SeqCst) { + loop { + if let Some(packet) = self.packet_buffer.lock().unwrap().pop_front() { + return Ok(packet); + } + thread::sleep(Duration::from_millis(10)); + } + } else { + let state = self.state(); - loop { - let mut packet = match self.conn.write().unwrap().read_packet() { - Ok(v) => v, - Err(e) => { - self.is_alive.store(false, Ordering::SeqCst); - return Err(e.into()); - } - }; - let mut cancelled = false; - for handler in self - .server - .packet_handlers(|o| o.on_incoming_packet_priority()) - .iter() - { - handler.on_incoming_packet( - self.clone(), - &mut packet, - &mut cancelled, - state.clone(), - )?; - packet.get_mut().set_position(0); - } - if !cancelled { - break Ok(packet); - } - } - } - } + loop { + let mut packet = match self.conn.write().unwrap().read_packet() { + Ok(v) => v, + Err(e) => { + self.is_alive.store(false, Ordering::SeqCst); + return Err(e.into()); + } + }; + let mut cancelled = false; + for handler in self + .server + .packet_handlers(|o| o.on_incoming_packet_priority()) + .iter() + { + handler.on_incoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?; + packet.get_mut().set_position(0); + } + if !cancelled { + break Ok(packet); + } + } + } + } - pub fn read_packet(self: &Arc, id: u8) -> Result { - if self.read_loop.load(Ordering::SeqCst) { - loop { - { - let mut locked = self.packet_buffer.lock().unwrap(); - for (i, packet) in locked.clone().iter().enumerate() { - if packet.id() == id { - locked.remove(i); - return Ok(packet.clone()); - } - } - } - thread::sleep(Duration::from_millis(10)); - } - } else { - let packet = match self.read_any_packet() { - Ok(v) => v, - Err(e) => { - self.is_alive.store(false, Ordering::SeqCst); - return Err(e); - } - }; + pub fn read_packet(self: &Arc, id: u8) -> Result { + if self.read_loop.load(Ordering::SeqCst) { + loop { + { + let mut locked = self.packet_buffer.lock().unwrap(); + for (i, packet) in locked.clone().iter().enumerate() { + if packet.id() == id { + locked.remove(i); + return Ok(packet.clone()); + } + } + } + thread::sleep(Duration::from_millis(10)); + } + } else { + let packet = match self.read_any_packet() { + Ok(v) => v, + Err(e) => { + self.is_alive.store(false, Ordering::SeqCst); + return Err(e); + } + }; - if packet.id() != id { - Err(ServerError::UnexpectedPacket(packet.id())) - } else { - Ok(packet) - } - } - } + if packet.id() != id { + Err(ServerError::UnexpectedPacket(packet.id())) + } else { + Ok(packet) + } + } + } - pub fn push_back(self: &Arc, packet: Packet){ + pub fn push_back(self: &Arc, packet: Packet) { self.packet_buffer.lock().unwrap().push_back(packet) } - pub fn close(self: &Arc) { - self.conn.write().unwrap().close(); - } + pub fn close(self: &Arc) { + self.conn.write().unwrap().close(); + } - pub fn set_compression(self: &Arc, threshold: Option) { - self.conn.write().unwrap().set_compression(threshold); - } + pub fn set_compression(self: &Arc, threshold: Option) { + self.conn.write().unwrap().set_compression(threshold); + } - pub fn is_alive(self: &Arc) -> bool { - self.is_alive.load(Ordering::SeqCst) - } + pub fn is_alive(self: &Arc) -> bool { + self.is_alive.load(Ordering::SeqCst) + } - pub fn protocol_helper(self: &Arc) -> ProtocolHelper { - ProtocolHelper::new(self.clone()) - } + pub fn protocol_helper(self: &Arc) -> ProtocolHelper { + ProtocolHelper::new(self.clone()) + } } #[derive(Clone)] pub struct Handshake { - pub protocol_version: i32, - pub server_address: String, - pub server_port: u16, + pub protocol_version: i32, + pub server_address: String, + pub server_port: u16, } #[derive(Clone)] pub struct ClientInfo { - pub brand: String, - pub locale: String, - pub view_distance: i8, - pub chat_mode: i32, - pub chat_colors: bool, - pub displayed_skin_parts: u8, - pub main_hand: i32, - pub enable_text_filtering: bool, - pub allow_server_listings: bool, - pub particle_status: i32, + pub brand: String, + pub locale: String, + pub view_distance: i8, + pub chat_mode: i32, + pub chat_colors: bool, + pub displayed_skin_parts: u8, + pub main_hand: i32, + pub enable_text_filtering: bool, + pub allow_server_listings: bool, + pub particle_status: i32, } #[derive(Clone)] pub struct PlayerInfo { - pub name: String, - pub uuid: Uuid, + pub name: String, + pub uuid: Uuid, } diff --git a/src/server/player/helper.rs b/src/server/player/helper.rs index 540290a..ad7689b 100644 --- a/src/server/player/helper.rs +++ b/src/server/player/helper.rs @@ -1,18 +1,18 @@ use std::{ - io::Read, - sync::Arc, - time::{Duration, SystemTime}, + io::Read, + sync::Arc, + time::{Duration, SystemTime}, }; use rust_mc_proto::{DataReader, DataWriter, Packet}; use crate::server::{ - ServerError, - data::{ReadWriteNBT, text_component::TextComponent}, - protocol::{ - id::{clientbound, serverbound}, - *, - }, + ServerError, + data::{ReadWriteNBT, text_component::TextComponent}, + protocol::{ + id::{clientbound, serverbound}, + *, + }, }; use super::context::ClientContext; @@ -25,207 +25,212 @@ use super::context::ClientContext; // Пусть юзают подключение и отправляют пакеты через него если хотят // Почему бы и нет если да pub struct ProtocolHelper { - client: Arc, - state: ConnectionState, + client: Arc, + state: ConnectionState, } impl ProtocolHelper { - pub fn new(client: Arc) -> Self { - Self { - state: client.state(), - client, - } - } + pub fn new(client: Arc) -> Self { + Self { + state: client.state(), + client, + } + } - pub fn reset_chat(&self) -> Result<(), ServerError> { - match self.state { - ConnectionState::Configuration => { - self.client - .write_packet(&Packet::empty(clientbound::configuration::RESET_CHAT))?; - Ok(()) - } - _ => Err(ServerError::UnexpectedState), - } - } + pub fn reset_chat(&self) -> Result<(), ServerError> { + match self.state { + ConnectionState::Configuration => { + self + .client + .write_packet(&Packet::empty(clientbound::configuration::RESET_CHAT))?; + Ok(()) + } + _ => Err(ServerError::UnexpectedState), + } + } - pub fn store_cookie(&self, id: &str, data: &[u8]) -> Result<(), ServerError> { - self.client.write_packet(&Packet::build( - match self.state { - ConnectionState::Configuration => clientbound::configuration::STORE_COOKIE, - ConnectionState::Play => clientbound::play::STORE_COOKIE, - _ => return Err(ServerError::UnexpectedState), - }, - |p| { - p.write_string(id)?; - p.write_bytes(data) - }, - )?)?; - Ok(()) - } + pub fn store_cookie(&self, id: &str, data: &[u8]) -> Result<(), ServerError> { + self.client.write_packet(&Packet::build( + match self.state { + ConnectionState::Configuration => clientbound::configuration::STORE_COOKIE, + ConnectionState::Play => clientbound::play::STORE_COOKIE, + _ => return Err(ServerError::UnexpectedState), + }, + |p| { + p.write_string(id)?; + p.write_bytes(data) + }, + )?)?; + Ok(()) + } - /// Leave from Configuration to Play state - pub fn leave_configuration(&self) -> Result<(), ServerError> { - match self.state { - ConnectionState::Configuration => { - self.client - .write_packet(&Packet::empty(clientbound::configuration::FINISH))?; - self.client - .read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; - self.client.set_state(ConnectionState::Play)?; - Ok(()) - } - _ => Err(ServerError::UnexpectedState), - } - } + /// Leave from Configuration to Play state + pub fn leave_configuration(&self) -> Result<(), ServerError> { + match self.state { + ConnectionState::Configuration => { + self + .client + .write_packet(&Packet::empty(clientbound::configuration::FINISH))?; + self + .client + .read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; + self.client.set_state(ConnectionState::Play)?; + Ok(()) + } + _ => Err(ServerError::UnexpectedState), + } + } - /// Enter to Configuration from Play state - pub fn enter_configuration(&self) -> Result<(), ServerError> { - match self.state { - ConnectionState::Play => { - self.client - .write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?; - self.client - .read_packet(serverbound::play::ACKNOWLEDGE_CONFIGURATION)?; - self.client.set_state(ConnectionState::Configuration)?; - Ok(()) - } - _ => Err(ServerError::UnexpectedState), - } - } + /// Enter to Configuration from Play state + pub fn enter_configuration(&self) -> Result<(), ServerError> { + match self.state { + ConnectionState::Play => { + self + .client + .write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?; + self + .client + .read_packet(serverbound::play::ACKNOWLEDGE_CONFIGURATION)?; + self.client.set_state(ConnectionState::Configuration)?; + Ok(()) + } + _ => Err(ServerError::UnexpectedState), + } + } - /// Enter to Configuration from Play state - pub fn ping(&self) -> Result { - match self.state { - ConnectionState::Play => { - let time = SystemTime::now(); - self.client - .write_packet(&Packet::empty(clientbound::play::PING))?; - self.client.read_packet(serverbound::play::PONG)?; - Ok(SystemTime::now().duration_since(time).unwrap()) - } - ConnectionState::Configuration => { - let time = SystemTime::now(); - self.client - .write_packet(&Packet::empty(clientbound::configuration::PING))?; - self.client.read_packet(serverbound::configuration::PONG)?; - Ok(SystemTime::now().duration_since(time).unwrap()) - } - _ => Err(ServerError::UnexpectedState), - } - } + /// Enter to Configuration from Play state + pub fn ping(&self) -> Result { + match self.state { + ConnectionState::Play => { + let time = SystemTime::now(); + self + .client + .write_packet(&Packet::empty(clientbound::play::PING))?; + self.client.read_packet(serverbound::play::PONG)?; + Ok(SystemTime::now().duration_since(time).unwrap()) + } + ConnectionState::Configuration => { + let time = SystemTime::now(); + self + .client + .write_packet(&Packet::empty(clientbound::configuration::PING))?; + self.client.read_packet(serverbound::configuration::PONG)?; + Ok(SystemTime::now().duration_since(time).unwrap()) + } + _ => Err(ServerError::UnexpectedState), + } + } - pub fn disconnect(&self, reason: TextComponent) -> Result<(), ServerError> { - let packet = match self.state { - ConnectionState::Login => { - let text = reason.as_json()?; - Packet::build(0x00, |p| p.write_string(&text))? - } - ConnectionState::Configuration => { - let mut packet = Packet::empty(0x02); - packet.write_nbt(&reason)?; - packet - } - ConnectionState::Play => { - let mut packet = Packet::empty(0x1C); - packet.write_nbt(&reason)?; - packet - } - _ => { - self.client.close(); - return Ok(()); - } - }; - self.client.write_packet(&packet)?; - Ok(()) - } + pub fn disconnect(&self, reason: TextComponent) -> Result<(), ServerError> { + let packet = match self.state { + ConnectionState::Login => { + let text = reason.as_json()?; + Packet::build(0x00, |p| p.write_string(&text))? + } + ConnectionState::Configuration => { + let mut packet = Packet::empty(0x02); + packet.write_nbt(&reason)?; + packet + } + ConnectionState::Play => { + let mut packet = Packet::empty(0x1C); + packet.write_nbt(&reason)?; + packet + } + _ => { + self.client.close(); + return Ok(()); + } + }; + self.client.write_packet(&packet)?; + Ok(()) + } - /// Returns cookie content - pub fn request_cookie(&self, id: &str) -> Result>, ServerError> { - match self.state { - ConnectionState::Configuration => { - let mut packet = Packet::empty(clientbound::configuration::COOKIE_REQUEST); - packet.write_string(id)?; - self.client.write_packet(&packet)?; + /// Returns cookie content + pub fn request_cookie(&self, id: &str) -> Result>, ServerError> { + match self.state { + ConnectionState::Configuration => { + let mut packet = Packet::empty(clientbound::configuration::COOKIE_REQUEST); + packet.write_string(id)?; + self.client.write_packet(&packet)?; - let mut packet = self - .client - .read_packet(serverbound::configuration::COOKIE_RESPONSE)?; - packet.read_string()?; - let data = if packet.read_boolean()? { - let n = packet.read_usize_varint()?; - Some(packet.read_bytes(n)?) - } else { - None - }; + let mut packet = self + .client + .read_packet(serverbound::configuration::COOKIE_RESPONSE)?; + packet.read_string()?; + let data = if packet.read_boolean()? { + let n = packet.read_usize_varint()?; + Some(packet.read_bytes(n)?) + } else { + None + }; - Ok(data) - } - ConnectionState::Play => { - let mut packet = Packet::empty(clientbound::play::COOKIE_REQUEST); - packet.write_string(id)?; - self.client.write_packet(&packet)?; + Ok(data) + } + ConnectionState::Play => { + let mut packet = Packet::empty(clientbound::play::COOKIE_REQUEST); + packet.write_string(id)?; + self.client.write_packet(&packet)?; - let mut packet = self - .client - .read_packet(serverbound::play::COOKIE_RESPONSE)?; - packet.read_string()?; - let data = if packet.read_boolean()? { - let n = packet.read_usize_varint()?; - Some(packet.read_bytes(n)?) - } else { - None - }; + let mut packet = self + .client + .read_packet(serverbound::play::COOKIE_RESPONSE)?; + packet.read_string()?; + let data = if packet.read_boolean()? { + let n = packet.read_usize_varint()?; + Some(packet.read_bytes(n)?) + } else { + None + }; - Ok(data) - } - _ => Err(ServerError::UnexpectedState), - } - } + Ok(data) + } + _ => Err(ServerError::UnexpectedState), + } + } - /// Returns login plugin response - (message_id, payload) - pub fn send_login_plugin_request( - &self, - id: i32, - channel: &str, - data: &[u8], - ) -> Result<(i32, Option>), ServerError> { - match self.state { - ConnectionState::Login => { - let mut packet = Packet::empty(clientbound::login::PLUGIN_REQUEST); - packet.write_varint(id)?; - packet.write_string(channel)?; - packet.write_bytes(data)?; - self.client.write_packet(&packet)?; + /// Returns login plugin response - (message_id, payload) + pub fn send_login_plugin_request( + &self, + id: i32, + channel: &str, + data: &[u8], + ) -> Result<(i32, Option>), ServerError> { + match self.state { + ConnectionState::Login => { + let mut packet = Packet::empty(clientbound::login::PLUGIN_REQUEST); + packet.write_varint(id)?; + packet.write_string(channel)?; + packet.write_bytes(data)?; + self.client.write_packet(&packet)?; - let mut packet = self - .client - .read_packet(serverbound::login::PLUGIN_RESPONSE)?; - let identifier = packet.read_varint()?; - let data = if packet.read_boolean()? { - let mut data = Vec::new(); - packet.get_mut().read_to_end(&mut data).unwrap(); - Some(data) - } else { - None - }; + let mut packet = self + .client + .read_packet(serverbound::login::PLUGIN_RESPONSE)?; + let identifier = packet.read_varint()?; + let data = if packet.read_boolean()? { + let mut data = Vec::new(); + packet.get_mut().read_to_end(&mut data).unwrap(); + Some(data) + } else { + None + }; - Ok((identifier, data)) - } - _ => Err(ServerError::UnexpectedState), - } - } + Ok((identifier, data)) + } + _ => Err(ServerError::UnexpectedState), + } + } - pub fn send_plugin_message(&self, channel: &str, data: &[u8]) -> Result<(), ServerError> { - let mut packet = match self.state { - ConnectionState::Configuration => { - Packet::empty(clientbound::configuration::PLUGIN_MESSAGE) - } - ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE), - _ => return Err(ServerError::UnexpectedState), - }; - packet.write_string(channel)?; - packet.write_bytes(data)?; - self.client.write_packet(&packet)?; - Ok(()) - } + pub fn send_plugin_message(&self, channel: &str, data: &[u8]) -> Result<(), ServerError> { + let mut packet = match self.state { + ConnectionState::Configuration => Packet::empty(clientbound::configuration::PLUGIN_MESSAGE), + ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE), + _ => return Err(ServerError::UnexpectedState), + }; + packet.write_string(channel)?; + packet.write_bytes(data)?; + self.client.write_packet(&packet)?; + Ok(()) + } } diff --git a/src/server/protocol/handler.rs b/src/server/protocol/handler.rs index d532e33..5f51f07 100644 --- a/src/server/protocol/handler.rs +++ b/src/server/protocol/handler.rs @@ -1,189 +1,189 @@ use std::{io::Read, sync::Arc}; use crate::server::{ - ServerError, - player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo}, + ServerError, + player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo}, }; use rust_mc_proto::{DataReader, DataWriter, Packet}; use crate::trigger_event; use super::{ - ConnectionState, - id::*, - play::{handle_configuration_state, handle_play_state}, + ConnectionState, + id::*, + play::{handle_configuration_state, handle_play_state}, }; pub fn handle_connection( - client: Arc, // Контекст клиента + client: Arc, // Контекст клиента ) -> Result<(), ServerError> { - // Чтение рукопожатия - // Получение пакетов производится через client.conn(), - // ВАЖНО: не помещать сам client.conn() в переменные, - // он должен сразу убиваться иначе соединение гдето задедлочится - let mut packet = client.read_packet(serverbound::handshake::HANDSHAKE)?; + // Чтение рукопожатия + // Получение пакетов производится через client.conn(), + // ВАЖНО: не помещать сам client.conn() в переменные, + // он должен сразу убиваться иначе соединение гдето задедлочится + let mut packet = client.read_packet(serverbound::handshake::HANDSHAKE)?; - let protocol_version = packet.read_varint()?; // Получаем версия протокола, может быть отрицательным если наш клиент дэбил - let server_address = packet.read_string()?; // Получаем домен/адрес сервера к которому пытается подключиться клиент, например "play.example.com", а не айпи - let server_port = packet.read_unsigned_short()?; // Все тоже самое что и с адресом сервера и все потому же и за тем же - let next_state = packet.read_varint()?; // Тип подключения: 1 для получения статуса и пинга, 2 и 3 для обычного подключения + let protocol_version = packet.read_varint()?; // Получаем версия протокола, может быть отрицательным если наш клиент дэбил + let server_address = packet.read_string()?; // Получаем домен/адрес сервера к которому пытается подключиться клиент, например "play.example.com", а не айпи + let server_port = packet.read_unsigned_short()?; // Все тоже самое что и с адресом сервера и все потому же и за тем же + let next_state = packet.read_varint()?; // Тип подключения: 1 для получения статуса и пинга, 2 и 3 для обычного подключения - client.set_handshake(Handshake { - protocol_version, - server_address, - server_port, - }); + client.set_handshake(Handshake { + protocol_version, + server_address, + server_port, + }); - match next_state { - 1 => { - // Тип подключения - статус - client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status + match next_state { + 1 => { + // Тип подключения - статус + client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status - loop { - // Чтение запроса - let mut packet = client.read_any_packet()?; + loop { + // Чтение запроса + let mut packet = client.read_any_packet()?; - match packet.id() { - serverbound::status::REQUEST => { - // Запрос статуса - let mut packet = Packet::empty(clientbound::status::RESPONSE); + match packet.id() { + serverbound::status::REQUEST => { + // Запрос статуса + let mut packet = Packet::empty(clientbound::status::RESPONSE); - // Дефолтный статус - let mut status = "{ + // Дефолтный статус + let mut status = "{ \"version\": { \"name\": \"Error\", \"protocol\": 0 }, \"description\": {\"text\": \"Internal server error\"} }" - .to_string(); + .to_string(); - // Опрос всех листенеров - trigger_event!(client, status, &mut status); + // Опрос всех листенеров + trigger_event!(client, status, &mut status); - // Отправка статуса - packet.write_string(&status)?; + // Отправка статуса + packet.write_string(&status)?; - client.write_packet(&packet)?; - } - serverbound::status::PING_REQUEST => { - // Пинг - // Раньше мы просто отправляли ему его-же пакет, но сейчас, - // С приходом к власти констант айди-пакетов, нам приходится делать такое непотребство - let timestamp = packet.read_long()?; - let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE); - packet.write_long(timestamp)?; - client.write_packet(&packet)?; - } - id => { - return Err(ServerError::UnexpectedPacket(id)); - } - } - } - } - 2 => { - // Тип подключения - игра - client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login + client.write_packet(&packet)?; + } + serverbound::status::PING_REQUEST => { + // Пинг + // Раньше мы просто отправляли ему его-же пакет, но сейчас, + // С приходом к власти констант айди-пакетов, нам приходится делать такое непотребство + let timestamp = packet.read_long()?; + let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE); + packet.write_long(timestamp)?; + client.write_packet(&packet)?; + } + id => { + return Err(ServerError::UnexpectedPacket(id)); + } + } + } + } + 2 => { + // Тип подключения - игра + client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login - // Читаем пакет Login Start - let mut packet = client.read_packet(serverbound::login::START)?; + // Читаем пакет Login Start + let mut packet = client.read_packet(serverbound::login::START)?; - let name = packet.read_string()?; - let uuid = packet.read_uuid()?; + let name = packet.read_string()?; + let uuid = packet.read_uuid()?; - client.set_player_info(PlayerInfo { - name: name.clone(), - uuid: uuid.clone(), - }); + client.set_player_info(PlayerInfo { + name: name.clone(), + uuid: uuid.clone(), + }); - if client.server.config.server.online_mode { - // TODO: encryption packets - } + if client.server.config.server.online_mode { + // TODO: encryption packets + } - // Отправляем пакет Set Compression если сжатие указано - if let Some(threshold) = client.server.config.server.compression_threshold { - client.write_packet(&Packet::build(clientbound::login::SET_COMPRESSION, |p| { - p.write_usize_varint(threshold) - })?)?; - client.set_compression(Some(threshold)); // Устанавливаем сжатие на соединении - } + // Отправляем пакет Set Compression если сжатие указано + if let Some(threshold) = client.server.config.server.compression_threshold { + client.write_packet(&Packet::build(clientbound::login::SET_COMPRESSION, |p| { + p.write_usize_varint(threshold) + })?)?; + client.set_compression(Some(threshold)); // Устанавливаем сжатие на соединении + } - // Отправка пакета Login Success - client.write_packet(&Packet::build(clientbound::login::SUCCESS, |p| { - p.write_uuid(&uuid)?; - p.write_string(&name)?; - p.write_varint(0) - })?)?; + // Отправка пакета Login Success + client.write_packet(&Packet::build(clientbound::login::SUCCESS, |p| { + p.write_uuid(&uuid)?; + p.write_string(&name)?; + p.write_varint(0) + })?)?; - client.read_packet(serverbound::login::ACKNOWLEDGED)?; // Пакет Login Acknowledged + client.read_packet(serverbound::login::ACKNOWLEDGED)?; // Пакет Login Acknowledged - client.set_state(ConnectionState::Configuration)?; // Мы перешли в режим Configuration + client.set_state(ConnectionState::Configuration)?; // Мы перешли в режим Configuration - // Получение бренда клиента из Serverbound Plugin Message - // Identifier канала откуда берется бренд: minecraft:brand - let brand = loop { - let mut packet = client.read_packet(serverbound::configuration::PLUGIN_MESSAGE)?; // Пакет Serverbound Plugin Message + // Получение бренда клиента из Serverbound Plugin Message + // Identifier канала откуда берется бренд: minecraft:brand + let brand = loop { + let mut packet = client.read_packet(serverbound::configuration::PLUGIN_MESSAGE)?; // Пакет Serverbound Plugin Message - let identifier = packet.read_string()?; + let identifier = packet.read_string()?; - let mut data = Vec::new(); - packet.get_mut().read_to_end(&mut data).unwrap(); + let mut data = Vec::new(); + packet.get_mut().read_to_end(&mut data).unwrap(); - if identifier == "minecraft:brand" { - break String::from_utf8_lossy(&data).to_string(); - } else { - trigger_event!(client, plugin_message, &identifier, &data); - } - }; + if identifier == "minecraft:brand" { + break String::from_utf8_lossy(&data).to_string(); + } else { + trigger_event!(client, plugin_message, &identifier, &data); + } + }; - let mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information + let mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information - let locale = packet.read_string()?; // for example: en_us - let view_distance = packet.read_signed_byte()?; // client-side render distance in chunks - let chat_mode = packet.read_varint()?; // 0: enabled, 1: commands only, 2: hidden. See Chat#Client chat mode for more information. - let chat_colors = packet.read_boolean()?; // this settings does nothing on client but can be used on serverside - let displayed_skin_parts = packet.read_byte()?; // bit mask https://minecraft.wiki/w/Java_Edition_protocol#Client_Information_(configuration) - let main_hand = packet.read_varint()?; // 0 for left and 1 for right - let enable_text_filtering = packet.read_boolean()?; // filtering text for profanity, always false for offline mode - let allow_server_listings = packet.read_boolean()?; // allows showing player in server listings in status - let particle_status = packet.read_varint()?; // 0 for all, 1 for decreased, 2 for minimal + let locale = packet.read_string()?; // for example: en_us + let view_distance = packet.read_signed_byte()?; // client-side render distance in chunks + let chat_mode = packet.read_varint()?; // 0: enabled, 1: commands only, 2: hidden. See Chat#Client chat mode for more information. + let chat_colors = packet.read_boolean()?; // this settings does nothing on client but can be used on serverside + let displayed_skin_parts = packet.read_byte()?; // bit mask https://minecraft.wiki/w/Java_Edition_protocol#Client_Information_(configuration) + let main_hand = packet.read_varint()?; // 0 for left and 1 for right + let enable_text_filtering = packet.read_boolean()?; // filtering text for profanity, always false for offline mode + let allow_server_listings = packet.read_boolean()?; // allows showing player in server listings in status + let particle_status = packet.read_varint()?; // 0 for all, 1 for decreased, 2 for minimal - client.set_client_info(ClientInfo { - brand, - locale, - view_distance, - chat_mode, - chat_colors, - displayed_skin_parts, - main_hand, - enable_text_filtering, - allow_server_listings, - particle_status, - }); + client.set_client_info(ClientInfo { + brand, + locale, + view_distance, + chat_mode, + chat_colors, + displayed_skin_parts, + main_hand, + enable_text_filtering, + allow_server_listings, + particle_status, + }); - client.write_packet(&Packet::build( - clientbound::configuration::PLUGIN_MESSAGE, - |p| { - p.write_string("minecraft:brand")?; - p.write_string("rust_minecraft_server") - }, - )?)?; + client.write_packet(&Packet::build( + clientbound::configuration::PLUGIN_MESSAGE, + |p| { + p.write_string("minecraft:brand")?; + p.write_string("rust_minecraft_server") + }, + )?)?; - handle_configuration_state(client.clone())?; + handle_configuration_state(client.clone())?; - client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?; - client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; + client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?; + client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; - client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play + client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play - // Дальше работаем с режимом игры - handle_play_state(client)?; - } - _ => { - // Тип подключения не рукопожатный - return Err(ServerError::UnexpectedState); - } - } + // Дальше работаем с режимом игры + handle_play_state(client)?; + } + _ => { + // Тип подключения не рукопожатный + return Err(ServerError::UnexpectedState); + } + } - Ok(()) + Ok(()) } diff --git a/src/server/protocol/id.rs b/src/server/protocol/id.rs index 5c26854..58f14d1 100644 --- a/src/server/protocol/id.rs +++ b/src/server/protocol/id.rs @@ -5,268 +5,268 @@ Generated with parse_ids.py */ pub mod clientbound { - pub mod status { - pub const RESPONSE: u8 = 0x00; - pub const PONG_RESPONSE: u8 = 0x01; - } + pub mod status { + pub const RESPONSE: u8 = 0x00; + pub const PONG_RESPONSE: u8 = 0x01; + } - pub mod login { - pub const DISCONNECT: u8 = 0x00; - pub const ENCRYPTION_REQUEST: u8 = 0x01; - pub const SUCCESS: u8 = 0x02; - pub const SET_COMPRESSION: u8 = 0x03; - pub const PLUGIN_REQUEST: u8 = 0x04; - pub const COOKIE_REQUEST: u8 = 0x05; - } + pub mod login { + pub const DISCONNECT: u8 = 0x00; + pub const ENCRYPTION_REQUEST: u8 = 0x01; + pub const SUCCESS: u8 = 0x02; + pub const SET_COMPRESSION: u8 = 0x03; + pub const PLUGIN_REQUEST: u8 = 0x04; + pub const COOKIE_REQUEST: u8 = 0x05; + } - pub mod configuration { - pub const COOKIE_REQUEST: u8 = 0x00; - pub const PLUGIN_MESSAGE: u8 = 0x01; - pub const DISCONNECT: u8 = 0x02; - pub const FINISH: u8 = 0x03; - pub const KEEP_ALIVE: u8 = 0x04; - pub const PING: u8 = 0x05; - pub const RESET_CHAT: u8 = 0x06; - pub const REGISTRY_DATA: u8 = 0x07; - pub const REMOVE_RESOURCE_PACK: u8 = 0x08; - pub const ADD_RESOURCE_PACK: u8 = 0x09; - pub const STORE_COOKIE: u8 = 0x0A; - pub const TRANSFER: u8 = 0x0B; - pub const FEATURE_FLAGS: u8 = 0x0C; - pub const UPDATE_TAGS: u8 = 0x0D; - pub const KNOWN_PACKS: u8 = 0x0E; - pub const CUSTOM_REPORT_DETAILS: u8 = 0x0F; - pub const SERVER_LINKS: u8 = 0x10; - } + pub mod configuration { + pub const COOKIE_REQUEST: u8 = 0x00; + pub const PLUGIN_MESSAGE: u8 = 0x01; + pub const DISCONNECT: u8 = 0x02; + pub const FINISH: u8 = 0x03; + pub const KEEP_ALIVE: u8 = 0x04; + pub const PING: u8 = 0x05; + pub const RESET_CHAT: u8 = 0x06; + pub const REGISTRY_DATA: u8 = 0x07; + pub const REMOVE_RESOURCE_PACK: u8 = 0x08; + pub const ADD_RESOURCE_PACK: u8 = 0x09; + pub const STORE_COOKIE: u8 = 0x0A; + pub const TRANSFER: u8 = 0x0B; + pub const FEATURE_FLAGS: u8 = 0x0C; + pub const UPDATE_TAGS: u8 = 0x0D; + pub const KNOWN_PACKS: u8 = 0x0E; + pub const CUSTOM_REPORT_DETAILS: u8 = 0x0F; + pub const SERVER_LINKS: u8 = 0x10; + } - pub mod play { - pub const BUNDLE_DELIMITER: u8 = 0x00; - pub const SPAWN_ENTITY: u8 = 0x01; - pub const ENTITY_ANIMATION: u8 = 0x02; - pub const AWARD_STATISTICS: u8 = 0x03; - pub const ACKNOWLEDGE_BLOCK_CHANGE: u8 = 0x04; - pub const SET_BLOCK_DESTROY_STAGE: u8 = 0x05; - pub const BLOCK_ENTITY_DATA: u8 = 0x06; - pub const BLOCK_ACTION: u8 = 0x07; - pub const BLOCK_UPDATE: u8 = 0x08; - pub const BOSS_BAR: u8 = 0x09; - pub const CHANGE_DIFFICULTY: u8 = 0x0A; - pub const CHUNK_BATCH_FINISHED: u8 = 0x0B; - pub const CHUNK_BATCH_START: u8 = 0x0C; - pub const CHUNK_BIOMES: u8 = 0x0D; - pub const CLEAR_TITLES: u8 = 0x0E; - pub const COMMAND_SUGGESTIONS_RESPONSE: u8 = 0x0F; - pub const COMMANDS: u8 = 0x10; - pub const CLOSE_CONTAINER: u8 = 0x11; - pub const SET_CONTAINER_CONTENT: u8 = 0x12; - pub const SET_CONTAINER_PROPERTY: u8 = 0x13; - pub const SET_CONTAINER_SLOT: u8 = 0x14; - pub const COOKIE_REQUEST: u8 = 0x15; - pub const SET_COOLDOWN: u8 = 0x16; - pub const CHAT_SUGGESTIONS: u8 = 0x17; - pub const PLUGIN_MESSAGE: u8 = 0x18; - pub const DAMAGE_EVENT: u8 = 0x19; - pub const DEBUG_SAMPLE: u8 = 0x1A; - pub const DELETE_MESSAGE: u8 = 0x1B; - pub const DISCONNECT: u8 = 0x1C; - pub const DISGUISED_CHAT_MESSAGE: u8 = 0x1D; - pub const ENTITY_EVENT: u8 = 0x1E; - pub const TELEPORT_ENTITY: u8 = 0x1F; - pub const EXPLOSION: u8 = 0x20; - pub const UNLOAD_CHUNK: u8 = 0x21; - pub const GAME_EVENT: u8 = 0x22; - pub const OPEN_HORSE_SCREEN: u8 = 0x23; - pub const HURT_ANIMATION: u8 = 0x24; - pub const INITIALIZE_WORLD_BORDER: u8 = 0x25; - pub const KEEP_ALIVE: u8 = 0x26; - pub const CHUNK_DATA_AND_UPDATE_LIGHT: u8 = 0x27; - pub const WORLD_EVENT: u8 = 0x28; - pub const PARTICLE: u8 = 0x29; - pub const UPDATE_LIGHT: u8 = 0x2A; - pub const LOGIN: u8 = 0x2B; - pub const MAP_DATA: u8 = 0x2C; - pub const MERCHANT_OFFERS: u8 = 0x2D; - pub const UPDATE_ENTITY_POSITION: u8 = 0x2E; - pub const UPDATE_ENTITY_POSITION_AND_ROTATION: u8 = 0x2F; - pub const MOVE_MINECART_ALONG_TRACK: u8 = 0x30; - pub const UPDATE_ENTITY_ROTATION: u8 = 0x31; - pub const MOVE_VEHICLE: u8 = 0x32; - pub const OPEN_BOOK: u8 = 0x33; - pub const OPEN_SCREEN: u8 = 0x34; - pub const OPEN_SIGN_EDITOR: u8 = 0x35; - pub const PING: u8 = 0x36; - pub const PING_RESPONSE: u8 = 0x37; - pub const PLACE_GHOST_RECIPE: u8 = 0x38; - pub const PLAYER_ABILITIES: u8 = 0x39; - pub const PLAYER_CHAT_MESSAGE: u8 = 0x3A; - pub const END_COMBAT: u8 = 0x3B; - pub const ENTER_COMBAT: u8 = 0x3C; - pub const COMBAT_DEATH: u8 = 0x3D; - pub const PLAYER_INFO_REMOVE: u8 = 0x3E; - pub const PLAYER_INFO_UPDATE: u8 = 0x3F; - pub const LOOK_AT: u8 = 0x40; - pub const SYNCHRONIZE_PLAYER_POSITION: u8 = 0x41; - pub const PLAYER_ROTATION: u8 = 0x42; - pub const RECIPE_BOOK_ADD: u8 = 0x43; - pub const RECIPE_BOOK_REMOVE: u8 = 0x44; - pub const RECIPE_BOOK_SETTINGS: u8 = 0x45; - pub const REMOVE_ENTITIES: u8 = 0x46; - pub const REMOVE_ENTITY_EFFECT: u8 = 0x47; - pub const RESET_SCORE: u8 = 0x48; - pub const REMOVE_RESOURCE_PACK: u8 = 0x49; - pub const ADD_RESOURCE_PACK: u8 = 0x4A; - pub const RESPAWN: u8 = 0x4B; - pub const SET_HEAD_ROTATION: u8 = 0x4C; - pub const UPDATE_SECTION_BLOCKS: u8 = 0x4D; - pub const SELECT_ADVANCEMENTS_TAB: u8 = 0x4E; - pub const SERVER_DATA: u8 = 0x4F; - pub const SET_ACTION_BAR_TEXT: u8 = 0x50; - pub const SET_BORDER_CENTER: u8 = 0x51; - pub const SET_BORDER_LERP_SIZE: u8 = 0x52; - pub const SET_BORDER_SIZE: u8 = 0x53; - pub const SET_BORDER_WARNING_DELAY: u8 = 0x54; - pub const SET_BORDER_WARNING_DISTANCE: u8 = 0x55; - pub const SET_CAMERA: u8 = 0x56; - pub const SET_CENTER_CHUNK: u8 = 0x57; - pub const SET_RENDER_DISTANCE: u8 = 0x58; - pub const SET_CURSOR_ITEM: u8 = 0x59; - pub const SET_DEFAULT_SPAWN_POSITION: u8 = 0x5A; - pub const DISPLAY_OBJECTIVE: u8 = 0x5B; - pub const SET_ENTITY_METADATA: u8 = 0x5C; - pub const LINK_ENTITIES: u8 = 0x5D; - pub const SET_ENTITY_VELOCITY: u8 = 0x5E; - pub const SET_EQUIPMENT: u8 = 0x5F; - pub const SET_EXPERIENCE: u8 = 0x60; - pub const SET_HEALTH: u8 = 0x61; - pub const SET_HELD_ITEM: u8 = 0x62; - pub const UPDATE_OBJECTIVES: u8 = 0x63; - pub const SET_PASSENGERS: u8 = 0x64; - pub const SET_PLAYER_INVENTORY_SLOT: u8 = 0x65; - pub const UPDATE_TEAMS: u8 = 0x66; - pub const UPDATE_SCORE: u8 = 0x67; - pub const SET_SIMULATION_DISTANCE: u8 = 0x68; - pub const SET_SUBTITLE_TEXT: u8 = 0x69; - pub const UPDATE_TIME: u8 = 0x6A; - pub const SET_TITLE_TEXT: u8 = 0x6B; - pub const SET_TITLE_ANIMATION_TIMES: u8 = 0x6C; - pub const ENTITY_SOUND_EFFECT: u8 = 0x6D; - pub const SOUND_EFFECT: u8 = 0x6E; - pub const START_CONFIGURATION: u8 = 0x6F; - pub const STOP_SOUND: u8 = 0x70; - pub const STORE_COOKIE: u8 = 0x71; - pub const SYSTEM_CHAT_MESSAGE: u8 = 0x72; - pub const SET_TAB_LIST_HEADER_AND_FOOTER: u8 = 0x73; - pub const TAG_QUERY_RESPONSE: u8 = 0x74; - pub const PICKUP_ITEM: u8 = 0x75; - pub const SYNCHRONIZE_VEHICLE_POSITION: u8 = 0x76; - pub const TEST_INSTANCE_BLOCK_STATUS: u8 = 0x77; - pub const SET_TICKING_STATE: u8 = 0x78; - pub const STEP_TICK: u8 = 0x79; - pub const TRANSFER: u8 = 0x7A; - pub const UPDATE_ADVANCEMENTS: u8 = 0x7B; - pub const UPDATE_ATTRIBUTES: u8 = 0x7C; - pub const ENTITY_EFFECT: u8 = 0x7D; - pub const UPDATE_RECIPES: u8 = 0x7E; - pub const UPDATE_TAGS: u8 = 0x7F; - pub const PROJECTILE_POWER: u8 = 0x80; - pub const CUSTOM_REPORT_DETAILS: u8 = 0x81; - pub const SERVER_LINKS: u8 = 0x82; - } + pub mod play { + pub const BUNDLE_DELIMITER: u8 = 0x00; + pub const SPAWN_ENTITY: u8 = 0x01; + pub const ENTITY_ANIMATION: u8 = 0x02; + pub const AWARD_STATISTICS: u8 = 0x03; + pub const ACKNOWLEDGE_BLOCK_CHANGE: u8 = 0x04; + pub const SET_BLOCK_DESTROY_STAGE: u8 = 0x05; + pub const BLOCK_ENTITY_DATA: u8 = 0x06; + pub const BLOCK_ACTION: u8 = 0x07; + pub const BLOCK_UPDATE: u8 = 0x08; + pub const BOSS_BAR: u8 = 0x09; + pub const CHANGE_DIFFICULTY: u8 = 0x0A; + pub const CHUNK_BATCH_FINISHED: u8 = 0x0B; + pub const CHUNK_BATCH_START: u8 = 0x0C; + pub const CHUNK_BIOMES: u8 = 0x0D; + pub const CLEAR_TITLES: u8 = 0x0E; + pub const COMMAND_SUGGESTIONS_RESPONSE: u8 = 0x0F; + pub const COMMANDS: u8 = 0x10; + pub const CLOSE_CONTAINER: u8 = 0x11; + pub const SET_CONTAINER_CONTENT: u8 = 0x12; + pub const SET_CONTAINER_PROPERTY: u8 = 0x13; + pub const SET_CONTAINER_SLOT: u8 = 0x14; + pub const COOKIE_REQUEST: u8 = 0x15; + pub const SET_COOLDOWN: u8 = 0x16; + pub const CHAT_SUGGESTIONS: u8 = 0x17; + pub const PLUGIN_MESSAGE: u8 = 0x18; + pub const DAMAGE_EVENT: u8 = 0x19; + pub const DEBUG_SAMPLE: u8 = 0x1A; + pub const DELETE_MESSAGE: u8 = 0x1B; + pub const DISCONNECT: u8 = 0x1C; + pub const DISGUISED_CHAT_MESSAGE: u8 = 0x1D; + pub const ENTITY_EVENT: u8 = 0x1E; + pub const TELEPORT_ENTITY: u8 = 0x1F; + pub const EXPLOSION: u8 = 0x20; + pub const UNLOAD_CHUNK: u8 = 0x21; + pub const GAME_EVENT: u8 = 0x22; + pub const OPEN_HORSE_SCREEN: u8 = 0x23; + pub const HURT_ANIMATION: u8 = 0x24; + pub const INITIALIZE_WORLD_BORDER: u8 = 0x25; + pub const KEEP_ALIVE: u8 = 0x26; + pub const CHUNK_DATA_AND_UPDATE_LIGHT: u8 = 0x27; + pub const WORLD_EVENT: u8 = 0x28; + pub const PARTICLE: u8 = 0x29; + pub const UPDATE_LIGHT: u8 = 0x2A; + pub const LOGIN: u8 = 0x2B; + pub const MAP_DATA: u8 = 0x2C; + pub const MERCHANT_OFFERS: u8 = 0x2D; + pub const UPDATE_ENTITY_POSITION: u8 = 0x2E; + pub const UPDATE_ENTITY_POSITION_AND_ROTATION: u8 = 0x2F; + pub const MOVE_MINECART_ALONG_TRACK: u8 = 0x30; + pub const UPDATE_ENTITY_ROTATION: u8 = 0x31; + pub const MOVE_VEHICLE: u8 = 0x32; + pub const OPEN_BOOK: u8 = 0x33; + pub const OPEN_SCREEN: u8 = 0x34; + pub const OPEN_SIGN_EDITOR: u8 = 0x35; + pub const PING: u8 = 0x36; + pub const PING_RESPONSE: u8 = 0x37; + pub const PLACE_GHOST_RECIPE: u8 = 0x38; + pub const PLAYER_ABILITIES: u8 = 0x39; + pub const PLAYER_CHAT_MESSAGE: u8 = 0x3A; + pub const END_COMBAT: u8 = 0x3B; + pub const ENTER_COMBAT: u8 = 0x3C; + pub const COMBAT_DEATH: u8 = 0x3D; + pub const PLAYER_INFO_REMOVE: u8 = 0x3E; + pub const PLAYER_INFO_UPDATE: u8 = 0x3F; + pub const LOOK_AT: u8 = 0x40; + pub const SYNCHRONIZE_PLAYER_POSITION: u8 = 0x41; + pub const PLAYER_ROTATION: u8 = 0x42; + pub const RECIPE_BOOK_ADD: u8 = 0x43; + pub const RECIPE_BOOK_REMOVE: u8 = 0x44; + pub const RECIPE_BOOK_SETTINGS: u8 = 0x45; + pub const REMOVE_ENTITIES: u8 = 0x46; + pub const REMOVE_ENTITY_EFFECT: u8 = 0x47; + pub const RESET_SCORE: u8 = 0x48; + pub const REMOVE_RESOURCE_PACK: u8 = 0x49; + pub const ADD_RESOURCE_PACK: u8 = 0x4A; + pub const RESPAWN: u8 = 0x4B; + pub const SET_HEAD_ROTATION: u8 = 0x4C; + pub const UPDATE_SECTION_BLOCKS: u8 = 0x4D; + pub const SELECT_ADVANCEMENTS_TAB: u8 = 0x4E; + pub const SERVER_DATA: u8 = 0x4F; + pub const SET_ACTION_BAR_TEXT: u8 = 0x50; + pub const SET_BORDER_CENTER: u8 = 0x51; + pub const SET_BORDER_LERP_SIZE: u8 = 0x52; + pub const SET_BORDER_SIZE: u8 = 0x53; + pub const SET_BORDER_WARNING_DELAY: u8 = 0x54; + pub const SET_BORDER_WARNING_DISTANCE: u8 = 0x55; + pub const SET_CAMERA: u8 = 0x56; + pub const SET_CENTER_CHUNK: u8 = 0x57; + pub const SET_RENDER_DISTANCE: u8 = 0x58; + pub const SET_CURSOR_ITEM: u8 = 0x59; + pub const SET_DEFAULT_SPAWN_POSITION: u8 = 0x5A; + pub const DISPLAY_OBJECTIVE: u8 = 0x5B; + pub const SET_ENTITY_METADATA: u8 = 0x5C; + pub const LINK_ENTITIES: u8 = 0x5D; + pub const SET_ENTITY_VELOCITY: u8 = 0x5E; + pub const SET_EQUIPMENT: u8 = 0x5F; + pub const SET_EXPERIENCE: u8 = 0x60; + pub const SET_HEALTH: u8 = 0x61; + pub const SET_HELD_ITEM: u8 = 0x62; + pub const UPDATE_OBJECTIVES: u8 = 0x63; + pub const SET_PASSENGERS: u8 = 0x64; + pub const SET_PLAYER_INVENTORY_SLOT: u8 = 0x65; + pub const UPDATE_TEAMS: u8 = 0x66; + pub const UPDATE_SCORE: u8 = 0x67; + pub const SET_SIMULATION_DISTANCE: u8 = 0x68; + pub const SET_SUBTITLE_TEXT: u8 = 0x69; + pub const UPDATE_TIME: u8 = 0x6A; + pub const SET_TITLE_TEXT: u8 = 0x6B; + pub const SET_TITLE_ANIMATION_TIMES: u8 = 0x6C; + pub const ENTITY_SOUND_EFFECT: u8 = 0x6D; + pub const SOUND_EFFECT: u8 = 0x6E; + pub const START_CONFIGURATION: u8 = 0x6F; + pub const STOP_SOUND: u8 = 0x70; + pub const STORE_COOKIE: u8 = 0x71; + pub const SYSTEM_CHAT_MESSAGE: u8 = 0x72; + pub const SET_TAB_LIST_HEADER_AND_FOOTER: u8 = 0x73; + pub const TAG_QUERY_RESPONSE: u8 = 0x74; + pub const PICKUP_ITEM: u8 = 0x75; + pub const SYNCHRONIZE_VEHICLE_POSITION: u8 = 0x76; + pub const TEST_INSTANCE_BLOCK_STATUS: u8 = 0x77; + pub const SET_TICKING_STATE: u8 = 0x78; + pub const STEP_TICK: u8 = 0x79; + pub const TRANSFER: u8 = 0x7A; + pub const UPDATE_ADVANCEMENTS: u8 = 0x7B; + pub const UPDATE_ATTRIBUTES: u8 = 0x7C; + pub const ENTITY_EFFECT: u8 = 0x7D; + pub const UPDATE_RECIPES: u8 = 0x7E; + pub const UPDATE_TAGS: u8 = 0x7F; + pub const PROJECTILE_POWER: u8 = 0x80; + pub const CUSTOM_REPORT_DETAILS: u8 = 0x81; + pub const SERVER_LINKS: u8 = 0x82; + } } pub mod serverbound { - pub mod handshake { - pub const HANDSHAKE: u8 = 0x00; - } + pub mod handshake { + pub const HANDSHAKE: u8 = 0x00; + } - pub mod status { - pub const REQUEST: u8 = 0x00; - pub const PING_REQUEST: u8 = 0x01; - } + pub mod status { + pub const REQUEST: u8 = 0x00; + pub const PING_REQUEST: u8 = 0x01; + } - pub mod login { - pub const START: u8 = 0x00; - pub const ENCRYPTION_RESPONSE: u8 = 0x01; - pub const PLUGIN_RESPONSE: u8 = 0x02; - pub const ACKNOWLEDGED: u8 = 0x03; - pub const COOKIE_RESPONSE: u8 = 0x04; - } + pub mod login { + pub const START: u8 = 0x00; + pub const ENCRYPTION_RESPONSE: u8 = 0x01; + pub const PLUGIN_RESPONSE: u8 = 0x02; + pub const ACKNOWLEDGED: u8 = 0x03; + pub const COOKIE_RESPONSE: u8 = 0x04; + } - pub mod configuration { - pub const CLIENT_INFORMATION: u8 = 0x00; - pub const COOKIE_RESPONSE: u8 = 0x01; - pub const PLUGIN_MESSAGE: u8 = 0x02; - pub const ACKNOWLEDGE_FINISH: u8 = 0x03; - pub const KEEP_ALIVE: u8 = 0x04; - pub const PONG: u8 = 0x05; - pub const RESOURCE_PACK_RESPONSE: u8 = 0x06; - pub const KNOWN_PACKS: u8 = 0x07; - } + pub mod configuration { + pub const CLIENT_INFORMATION: u8 = 0x00; + pub const COOKIE_RESPONSE: u8 = 0x01; + pub const PLUGIN_MESSAGE: u8 = 0x02; + pub const ACKNOWLEDGE_FINISH: u8 = 0x03; + pub const KEEP_ALIVE: u8 = 0x04; + pub const PONG: u8 = 0x05; + pub const RESOURCE_PACK_RESPONSE: u8 = 0x06; + pub const KNOWN_PACKS: u8 = 0x07; + } - pub mod play { - pub const CONFIRM_TELEPORTATION: u8 = 0x00; - pub const QUERY_BLOCK_ENTITY_TAG: u8 = 0x01; - pub const BUNDLE_ITEM_SELECTED: u8 = 0x02; - pub const CHANGE_DIFFICULTY: u8 = 0x03; - pub const ACKNOWLEDGE_MESSAGE: u8 = 0x04; - pub const CHAT_COMMAND: u8 = 0x05; - pub const SIGNED_CHAT_COMMAND: u8 = 0x06; - pub const CHAT_MESSAGE: u8 = 0x07; - pub const PLAYER_SESSION: u8 = 0x08; - pub const CHUNK_BATCH_RECEIVED: u8 = 0x09; - pub const CLIENT_STATUS: u8 = 0x0A; - pub const CLIENT_TICK_END: u8 = 0x0B; - pub const CLIENT_INFORMATION: u8 = 0x0C; - pub const COMMAND_SUGGESTIONS_REQUEST: u8 = 0x0D; - pub const ACKNOWLEDGE_CONFIGURATION: u8 = 0x0E; - pub const CLICK_CONTAINER_BUTTON: u8 = 0x0F; - pub const CLICK_CONTAINER: u8 = 0x10; - pub const CLOSE_CONTAINER: u8 = 0x11; - pub const CHANGE_CONTAINER_SLOT_STATE: u8 = 0x12; - pub const COOKIE_RESPONSE: u8 = 0x13; - pub const PLUGIN_MESSAGE: u8 = 0x14; - pub const DEBUG_SAMPLE_SUBSCRIPTION: u8 = 0x15; - pub const EDIT_BOOK: u8 = 0x16; - pub const QUERY_ENTITY_TAG: u8 = 0x17; - pub const INTERACT: u8 = 0x18; - pub const JIGSAW_GENERATE: u8 = 0x19; - pub const KEEP_ALIVE: u8 = 0x1A; - pub const LOCK_DIFFICULTY: u8 = 0x1B; - pub const SET_PLAYER_POSITION: u8 = 0x1C; - pub const SET_PLAYER_POSITION_AND_ROTATION: u8 = 0x1D; - pub const SET_PLAYER_ROTATION: u8 = 0x1E; - pub const SET_PLAYER_MOVEMENT_FLAGS: u8 = 0x1F; - pub const MOVE_VEHICLE: u8 = 0x20; - pub const PADDLE_BOAT: u8 = 0x21; - pub const PICK_ITEM_FROM_BLOCK: u8 = 0x22; - pub const PICK_ITEM_FROM_ENTITY: u8 = 0x23; - pub const PING_REQUEST: u8 = 0x24; - pub const PLACE_RECIPE: u8 = 0x25; - pub const PLAYER_ABILITIES: u8 = 0x26; - pub const PLAYER_ACTION: u8 = 0x27; - pub const PLAYER_COMMAND: u8 = 0x28; - pub const PLAYER_INPUT: u8 = 0x29; - pub const PLAYER_LOADED: u8 = 0x2A; - pub const PONG: u8 = 0x2B; - pub const CHANGE_RECIPE_BOOK_SETTINGS: u8 = 0x2C; - pub const SET_SEEN_RECIPE: u8 = 0x2D; - pub const RENAME_ITEM: u8 = 0x2E; - pub const RESOURCE_PACK_RESPONSE: u8 = 0x2F; - pub const SEEN_ADVANCEMENTS: u8 = 0x30; - pub const SELECT_TRADE: u8 = 0x31; - pub const SET_BEACON_EFFECT: u8 = 0x32; - pub const SET_HELD_ITEM: u8 = 0x33; - pub const PROGRAM_COMMAND_BLOCK: u8 = 0x34; - pub const PROGRAM_COMMAND_BLOCK_MINECART: u8 = 0x35; - pub const SET_CREATIVE_MODE_SLOT: u8 = 0x36; - pub const PROGRAM_JIGSAW_BLOCK: u8 = 0x37; - pub const PROGRAM_STRUCTURE_BLOCK: u8 = 0x38; - pub const SET_TEST_BLOCK: u8 = 0x39; - pub const UPDATE_SIGN: u8 = 0x3A; - pub const SWING_ARM: u8 = 0x3B; - pub const TELEPORT_TO_ENTITY: u8 = 0x3C; - pub const TEST_INSTANCE_BLOCK_ACTION: u8 = 0x3D; - pub const USE_ITEM_ON: u8 = 0x3E; - pub const USE_ITEM: u8 = 0x3F; - } + pub mod play { + pub const CONFIRM_TELEPORTATION: u8 = 0x00; + pub const QUERY_BLOCK_ENTITY_TAG: u8 = 0x01; + pub const BUNDLE_ITEM_SELECTED: u8 = 0x02; + pub const CHANGE_DIFFICULTY: u8 = 0x03; + pub const ACKNOWLEDGE_MESSAGE: u8 = 0x04; + pub const CHAT_COMMAND: u8 = 0x05; + pub const SIGNED_CHAT_COMMAND: u8 = 0x06; + pub const CHAT_MESSAGE: u8 = 0x07; + pub const PLAYER_SESSION: u8 = 0x08; + pub const CHUNK_BATCH_RECEIVED: u8 = 0x09; + pub const CLIENT_STATUS: u8 = 0x0A; + pub const CLIENT_TICK_END: u8 = 0x0B; + pub const CLIENT_INFORMATION: u8 = 0x0C; + pub const COMMAND_SUGGESTIONS_REQUEST: u8 = 0x0D; + pub const ACKNOWLEDGE_CONFIGURATION: u8 = 0x0E; + pub const CLICK_CONTAINER_BUTTON: u8 = 0x0F; + pub const CLICK_CONTAINER: u8 = 0x10; + pub const CLOSE_CONTAINER: u8 = 0x11; + pub const CHANGE_CONTAINER_SLOT_STATE: u8 = 0x12; + pub const COOKIE_RESPONSE: u8 = 0x13; + pub const PLUGIN_MESSAGE: u8 = 0x14; + pub const DEBUG_SAMPLE_SUBSCRIPTION: u8 = 0x15; + pub const EDIT_BOOK: u8 = 0x16; + pub const QUERY_ENTITY_TAG: u8 = 0x17; + pub const INTERACT: u8 = 0x18; + pub const JIGSAW_GENERATE: u8 = 0x19; + pub const KEEP_ALIVE: u8 = 0x1A; + pub const LOCK_DIFFICULTY: u8 = 0x1B; + pub const SET_PLAYER_POSITION: u8 = 0x1C; + pub const SET_PLAYER_POSITION_AND_ROTATION: u8 = 0x1D; + pub const SET_PLAYER_ROTATION: u8 = 0x1E; + pub const SET_PLAYER_MOVEMENT_FLAGS: u8 = 0x1F; + pub const MOVE_VEHICLE: u8 = 0x20; + pub const PADDLE_BOAT: u8 = 0x21; + pub const PICK_ITEM_FROM_BLOCK: u8 = 0x22; + pub const PICK_ITEM_FROM_ENTITY: u8 = 0x23; + pub const PING_REQUEST: u8 = 0x24; + pub const PLACE_RECIPE: u8 = 0x25; + pub const PLAYER_ABILITIES: u8 = 0x26; + pub const PLAYER_ACTION: u8 = 0x27; + pub const PLAYER_COMMAND: u8 = 0x28; + pub const PLAYER_INPUT: u8 = 0x29; + pub const PLAYER_LOADED: u8 = 0x2A; + pub const PONG: u8 = 0x2B; + pub const CHANGE_RECIPE_BOOK_SETTINGS: u8 = 0x2C; + pub const SET_SEEN_RECIPE: u8 = 0x2D; + pub const RENAME_ITEM: u8 = 0x2E; + pub const RESOURCE_PACK_RESPONSE: u8 = 0x2F; + pub const SEEN_ADVANCEMENTS: u8 = 0x30; + pub const SELECT_TRADE: u8 = 0x31; + pub const SET_BEACON_EFFECT: u8 = 0x32; + pub const SET_HELD_ITEM: u8 = 0x33; + pub const PROGRAM_COMMAND_BLOCK: u8 = 0x34; + pub const PROGRAM_COMMAND_BLOCK_MINECART: u8 = 0x35; + pub const SET_CREATIVE_MODE_SLOT: u8 = 0x36; + pub const PROGRAM_JIGSAW_BLOCK: u8 = 0x37; + pub const PROGRAM_STRUCTURE_BLOCK: u8 = 0x38; + pub const SET_TEST_BLOCK: u8 = 0x39; + pub const UPDATE_SIGN: u8 = 0x3A; + pub const SWING_ARM: u8 = 0x3B; + pub const TELEPORT_TO_ENTITY: u8 = 0x3C; + pub const TEST_INSTANCE_BLOCK_ACTION: u8 = 0x3D; + pub const USE_ITEM_ON: u8 = 0x3E; + pub const USE_ITEM: u8 = 0x3F; + } } diff --git a/src/server/protocol/mod.rs b/src/server/protocol/mod.rs index 5110371..a1de46a 100644 --- a/src/server/protocol/mod.rs +++ b/src/server/protocol/mod.rs @@ -4,9 +4,9 @@ pub mod play; #[derive(Debug, Clone)] pub enum ConnectionState { - Handshake, - Status, - Login, - Configuration, - Play, + Handshake, + Status, + Login, + Configuration, + Play, } diff --git a/src/server/protocol/play.rs b/src/server/protocol/play.rs index 8d1dac0..91db8bb 100644 --- a/src/server/protocol/play.rs +++ b/src/server/protocol/play.rs @@ -1,215 +1,233 @@ -use std::{io::Cursor, sync::Arc, thread, time::{Duration, SystemTime, UNIX_EPOCH}}; +use std::{ + io::Cursor, + sync::Arc, + thread, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; -use rust_mc_proto::{read_packet, DataReader, DataWriter, Packet}; +use rust_mc_proto::{DataReader, DataWriter, Packet, read_packet}; -use crate::server::{data::{text_component::TextComponent, ReadWriteNBT}, player::context::ClientContext, ServerError}; +use crate::server::{ + ServerError, + data::{ReadWriteNBT, text_component::TextComponent}, + player::context::ClientContext, +}; use super::id::*; pub fn send_update_tags(client: Arc) -> Result<(), ServerError> { - // TODO: rewrite this hardcode bullshit + // TODO: rewrite this hardcode bullshit - client.write_packet(&Packet::from_bytes( - clientbound::configuration::UPDATE_TAGS, - include_bytes!("update-tags.bin"), - )) + client.write_packet(&Packet::from_bytes( + clientbound::configuration::UPDATE_TAGS, + include_bytes!("update-tags.bin"), + )) } pub fn send_registry_data(client: Arc) -> Result<(), ServerError> { - // TODO: rewrite this hardcode bullshit + // TODO: rewrite this hardcode bullshit - let mut registry_data = Cursor::new(include_bytes!("registry-data.bin")); + let mut registry_data = Cursor::new(include_bytes!("registry-data.bin")); - while let Ok(mut packet) = read_packet(&mut registry_data, None) { - packet.set_id(clientbound::configuration::REGISTRY_DATA); - client.write_packet(&packet)?; - } + while let Ok(mut packet) = read_packet(&mut registry_data, None) { + packet.set_id(clientbound::configuration::REGISTRY_DATA); + client.write_packet(&packet)?; + } - Ok(()) + Ok(()) } // Добавки в Configuration стейт чтобы все работало pub fn handle_configuration_state( - client: Arc, // Контекст клиента + client: Arc, // Контекст клиента ) -> Result<(), ServerError> { - let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS); - packet.write_varint(1)?; - packet.write_string("minecraft:vanilla")?; - client.write_packet(&packet)?; + let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS); + packet.write_varint(1)?; + packet.write_string("minecraft:vanilla")?; + client.write_packet(&packet)?; - let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS); - packet.write_varint(1)?; - packet.write_string("minecraft")?; - packet.write_string("core")?; - packet.write_string("1.21.5")?; - client.write_packet(&packet)?; + let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS); + packet.write_varint(1)?; + packet.write_string("minecraft")?; + packet.write_string("core")?; + packet.write_string("1.21.5")?; + client.write_packet(&packet)?; - client.read_packet(serverbound::configuration::KNOWN_PACKS)?; + client.read_packet(serverbound::configuration::KNOWN_PACKS)?; - send_registry_data(client.clone())?; - send_update_tags(client.clone()) + send_registry_data(client.clone())?; + send_update_tags(client.clone()) } pub fn send_login(client: Arc) -> Result<(), ServerError> { - // Отправка пакета Login - let mut packet = Packet::empty(clientbound::play::LOGIN); + // Отправка пакета Login + let mut packet = Packet::empty(clientbound::play::LOGIN); - packet.write_int(0)?; // Entity ID - packet.write_boolean(false)?; // Is hardcore - packet.write_varint(4)?; // Dimension Names - packet.write_string("minecraft:overworld")?; - packet.write_string("minecraft:nether")?; - packet.write_string("minecraft:the_end")?; - packet.write_string("minecraft:overworld_caves")?; - packet.write_varint(0)?; // Max Players - packet.write_varint(8)?; // View Distance - packet.write_varint(5)?; // Simulation Distance - packet.write_boolean(false)?; // Reduced Debug Info - packet.write_boolean(true)?; // Enable respawn screen - packet.write_boolean(false)?; // Do limited crafting + packet.write_int(0)?; // Entity ID + packet.write_boolean(false)?; // Is hardcore + packet.write_varint(4)?; // Dimension Names + packet.write_string("minecraft:overworld")?; + packet.write_string("minecraft:nether")?; + packet.write_string("minecraft:the_end")?; + packet.write_string("minecraft:overworld_caves")?; + packet.write_varint(0)?; // Max Players + packet.write_varint(8)?; // View Distance + packet.write_varint(5)?; // Simulation Distance + packet.write_boolean(false)?; // Reduced Debug Info + packet.write_boolean(true)?; // Enable respawn screen + packet.write_boolean(false)?; // Do limited crafting - packet.write_varint(0)?; // Dimension Type - packet.write_string("minecraft:overworld")?; // Dimension Name - packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed - packet.write_byte(0)?; // Game mode - packet.write_signed_byte(-1)?; // Previous Game mode - packet.write_boolean(false)?; // Is Debug - packet.write_boolean(true)?; // Is Flat - packet.write_boolean(false)?; // Has death location - packet.write_varint(20)?; // Portal cooldown - packet.write_varint(60)?; // Sea level + packet.write_varint(0)?; // Dimension Type + packet.write_string("minecraft:overworld")?; // Dimension Name + packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed + packet.write_byte(0)?; // Game mode + packet.write_signed_byte(-1)?; // Previous Game mode + packet.write_boolean(false)?; // Is Debug + packet.write_boolean(true)?; // Is Flat + packet.write_boolean(false)?; // Has death location + packet.write_varint(20)?; // Portal cooldown + packet.write_varint(60)?; // Sea level - packet.write_boolean(false)?; // Enforces Secure Chat + packet.write_boolean(false)?; // Enforces Secure Chat - client.write_packet(&packet) + client.write_packet(&packet) } -pub fn send_game_event(client: Arc, event: u8, value: f32) -> Result<(), ServerError> { - let mut packet = Packet::empty(clientbound::play::GAME_EVENT); +pub fn send_game_event( + client: Arc, + event: u8, + value: f32, +) -> Result<(), ServerError> { + let mut packet = Packet::empty(clientbound::play::GAME_EVENT); - packet.write_byte(event)?; - packet.write_float(value)?; + packet.write_byte(event)?; + packet.write_float(value)?; - client.write_packet(&packet) + client.write_packet(&packet) } pub fn sync_player_pos( - client: Arc, - x: f64, - y: f64, - z: f64, - vel_x: f64, - vel_y: f64, - vel_z: f64, - yaw: f32, - pitch: f32, - flags: i32 + client: Arc, + x: f64, + y: f64, + z: f64, + vel_x: f64, + vel_y: f64, + vel_z: f64, + yaw: f32, + pitch: f32, + flags: i32, ) -> Result<(), ServerError> { - let timestamp = (SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() & 0xFFFFFFFF) as i32; + let timestamp = (SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis() + & 0xFFFFFFFF) as i32; - let mut packet = Packet::empty(clientbound::play::SYNCHRONIZE_PLAYER_POSITION); + let mut packet = Packet::empty(clientbound::play::SYNCHRONIZE_PLAYER_POSITION); - packet.write_varint(timestamp)?; - packet.write_double(x)?; - packet.write_double(y)?; - packet.write_double(z)?; - packet.write_double(vel_x)?; - packet.write_double(vel_y)?; - packet.write_double(vel_z)?; - packet.write_float(yaw)?; - packet.write_float(pitch)?; - packet.write_int(flags)?; + packet.write_varint(timestamp)?; + packet.write_double(x)?; + packet.write_double(y)?; + packet.write_double(z)?; + packet.write_double(vel_x)?; + packet.write_double(vel_y)?; + packet.write_double(vel_z)?; + packet.write_float(yaw)?; + packet.write_float(pitch)?; + packet.write_int(flags)?; - client.write_packet(&packet)?; + client.write_packet(&packet)?; - Ok(()) + Ok(()) } pub fn set_center_chunk(client: Arc, x: i32, z: i32) -> Result<(), ServerError> { - let mut packet = Packet::empty(clientbound::play::SET_CENTER_CHUNK); + let mut packet = Packet::empty(clientbound::play::SET_CENTER_CHUNK); - packet.write_varint(x)?; - packet.write_varint(z)?; + packet.write_varint(x)?; + packet.write_varint(z)?; - client.write_packet(&packet) + client.write_packet(&packet) } pub fn send_example_chunk(client: Arc, x: i32, z: i32) -> Result<(), ServerError> { - let mut packet = Packet::empty(clientbound::play::CHUNK_DATA_AND_UPDATE_LIGHT); + let mut packet = Packet::empty(clientbound::play::CHUNK_DATA_AND_UPDATE_LIGHT); - packet.write_int(x)?; - packet.write_int(z)?; + packet.write_int(x)?; + packet.write_int(z)?; - // heightmap + // heightmap - packet.write_varint(1)?; // heightmaps count - packet.write_varint(0)?; // MOTION_BLOCKING - packet.write_varint(256)?; // Length of the following long array (16 * 16 = 256) - for _ in 0..256 { - packet.write_long(0)?; // height - 0 - } + packet.write_varint(1)?; // heightmaps count + packet.write_varint(0)?; // MOTION_BLOCKING + packet.write_varint(256)?; // Length of the following long array (16 * 16 = 256) + for _ in 0..256 { + packet.write_long(0)?; // height - 0 + } - // sending chunk data + // sending chunk data - let mut chunk_data = Vec::new(); + let mut chunk_data = Vec::new(); - // we want to fill the area from -64 to 0, so it will be 4 chunk sections + // we want to fill the area from -64 to 0, so it will be 4 chunk sections - for _ in 0..4 { - chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks + for _ in 0..4 { + chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks - // blocks paletted container - chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format - chunk_data.write_varint(1)?; // block state id in the registry (1 for stone) + // blocks paletted container + chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format + chunk_data.write_varint(1)?; // block state id in the registry (1 for stone) - // biomes palleted container - chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format - chunk_data.write_varint(27)?; // biome id in the registry - } + // biomes palleted container + chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format + chunk_data.write_varint(27)?; // biome id in the registry + } - // air chunk sections + // air chunk sections - for _ in 0..20 { - chunk_data.write_short(0)?; // non-air blocks count, 0 + for _ in 0..20 { + chunk_data.write_short(0)?; // non-air blocks count, 0 - // blocks paletted container - chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format - chunk_data.write_varint(0)?; // block state id in the registry (0 for air) + // blocks paletted container + chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format + chunk_data.write_varint(0)?; // block state id in the registry (0 for air) - // biomes palleted container - chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format - chunk_data.write_varint(27)?; // biome id in the registry - } + // biomes palleted container + chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format + chunk_data.write_varint(27)?; // biome id in the registry + } - packet.write_usize_varint(chunk_data.len())?; - packet.write_bytes(&chunk_data)?; + packet.write_usize_varint(chunk_data.len())?; + packet.write_bytes(&chunk_data)?; - packet.write_byte(0)?; + packet.write_byte(0)?; + // light data - // light data + packet.write_byte(0)?; + packet.write_byte(0)?; + packet.write_byte(0)?; + packet.write_byte(0)?; + packet.write_byte(0)?; + packet.write_byte(0)?; - packet.write_byte(0)?; - packet.write_byte(0)?; - packet.write_byte(0)?; - packet.write_byte(0)?; - packet.write_byte(0)?; - packet.write_byte(0)?; + client.write_packet(&packet)?; - - client.write_packet(&packet)?; - - Ok(()) + Ok(()) } pub fn send_keep_alive(client: Arc) -> Result<(), ServerError> { - let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() as i64; + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs() as i64; - let mut packet = Packet::empty(clientbound::play::KEEP_ALIVE); - packet.write_long(timestamp)?; - client.write_packet(&packet)?; + let mut packet = Packet::empty(clientbound::play::KEEP_ALIVE); + packet.write_long(timestamp)?; + client.write_packet(&packet)?; - let mut packet = client.read_packet(serverbound::play::KEEP_ALIVE)?; + let mut packet = client.read_packet(serverbound::play::KEEP_ALIVE)?; let timestamp2 = packet.read_long()?; if timestamp2 != timestamp { // Послать клиента нахуй @@ -218,103 +236,113 @@ pub fn send_keep_alive(client: Arc) -> Result<(), ServerError> { println!("KeepAlive Ok") } - Ok(()) + Ok(()) } -pub fn send_system_message(client: Arc, message: TextComponent, is_action_bar: bool) -> Result<(), ServerError> { - let mut packet = Packet::empty(clientbound::play::SYSTEM_CHAT_MESSAGE); - packet.write_nbt(&message)?; - packet.write_boolean(is_action_bar)?; - client.write_packet(&packet) +pub fn send_system_message( + client: Arc, + message: TextComponent, + is_action_bar: bool, +) -> Result<(), ServerError> { + let mut packet = Packet::empty(clientbound::play::SYSTEM_CHAT_MESSAGE); + packet.write_nbt(&message)?; + packet.write_boolean(is_action_bar)?; + client.write_packet(&packet) } // Отдельная функция для работы с самой игрой pub fn handle_play_state( - client: Arc, // Контекст клиента + client: Arc, // Контекст клиента ) -> Result<(), ServerError> { + thread::spawn({ + let client = client.clone(); - thread::spawn({ - let client = client.clone(); + move || { + let _ = client.run_read_loop(); + client.close(); + } + }); - move || { - let _ = client.run_read_loop(); - client.close(); - } - }); + send_login(client.clone())?; + sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?; + send_game_event(client.clone(), 13, 0.0)?; // 13 - Start waiting for level chunks + set_center_chunk(client.clone(), 0, 0)?; + send_example_chunk(client.clone(), 0, 0)?; - send_login(client.clone())?; - sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?; - send_game_event(client.clone(), 13, 0.0)?; // 13 - Start waiting for level chunks - set_center_chunk(client.clone(), 0, 0)?; - send_example_chunk(client.clone(), 0, 0)?; + thread::spawn({ + let client = client.clone(); - thread::spawn({ - let client = client.clone(); + move || -> Result<(), ServerError> { + while client.is_alive() { + let mut packet = client.read_any_packet()?; - move || -> Result<(), ServerError> { - while client.is_alive() { - let mut packet = client.read_any_packet()?; + match packet.id() { + serverbound::play::SET_PLAYER_POSITION => { + let x = packet.read_double()?; + let y = packet.read_double()?; + let z = packet.read_double()?; + let _ = packet.read_byte()?; // flags - match packet.id() { - serverbound::play::SET_PLAYER_POSITION => { - let x = packet.read_double()?; - let y = packet.read_double()?; - let z = packet.read_double()?; - let _ = packet.read_byte()?; // flags + client.set_position((x, y, z)); + } + serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => { + let x = packet.read_double()?; + let y = packet.read_double()?; + let z = packet.read_double()?; + let yaw = packet.read_float()?; + let pitch = packet.read_float()?; + let _ = packet.read_byte()?; // flags - client.set_position((x, y, z)); - }, - serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => { - let x = packet.read_double()?; - let y = packet.read_double()?; - let z = packet.read_double()?; - let yaw = packet.read_float()?; - let pitch = packet.read_float()?; - let _ = packet.read_byte()?; // flags + client.set_position((x, y, z)); + client.set_rotation((yaw, pitch)); + } + serverbound::play::SET_PLAYER_ROTATION => { + let yaw = packet.read_float()?; + let pitch = packet.read_float()?; + let _ = packet.read_byte()?; // flags - client.set_position((x, y, z)); - client.set_rotation((yaw, pitch)); - }, - serverbound::play::SET_PLAYER_ROTATION => { - let yaw = packet.read_float()?; - let pitch = packet.read_float()?; - let _ = packet.read_byte()?; // flags - - client.set_rotation((yaw, pitch)); - }, - _ => { + client.set_rotation((yaw, pitch)); + } + _ => { client.push_back(packet); } - } - } + } + } - Ok(()) - } - }); + Ok(()) + } + }); - let mut ticks_alive = 0u64; + let mut ticks_alive = 0u64; - while client.is_alive() { + while client.is_alive() { println!("{ticks_alive}"); - if ticks_alive % 200 == 0 { // 10 secs timer - send_keep_alive(client.clone())?; - } + if ticks_alive % 200 == 0 { + // 10 secs timer + send_keep_alive(client.clone())?; + } - if ticks_alive % 20 == 0 { // 1 sec timer - let (x, y, z) = client.position(); + if ticks_alive % 20 == 0 { + // 1 sec timer + let (x, y, z) = client.position(); - send_system_message(client.clone(), - TextComponent::rainbow(format!( - "Pos: {} {} {}", x as i64, y as i64, z as i64 - )), false)?; - } + send_system_message( + client.clone(), + TextComponent::rainbow(format!("Pos: {} {} {}", x as i64, y as i64, z as i64)), + false, + )?; + } - send_system_message(client.clone(), TextComponent::rainbow(format!("Ticks alive: {}", ticks_alive)), true)?; + send_system_message( + client.clone(), + TextComponent::rainbow(format!("Ticks alive: {}", ticks_alive)), + true, + )?; - thread::sleep(Duration::from_millis(50)); // 1 tick - ticks_alive += 1; - } + thread::sleep(Duration::from_millis(50)); // 1 tick + ticks_alive += 1; + } println!("Client die"); - Ok(()) + Ok(()) }