From d0968d1c344e4d9583738327b112fbaf990e5047 Mon Sep 17 00:00:00 2001 From: MeexReay Date: Sat, 3 May 2025 18:34:20 +0300 Subject: [PATCH] rustfmt --- .vscode/settings.json | 3 +- src/main.rs | 206 +++++++++++++----------- src/server/config.rs | 33 ++-- src/server/context.rs | 37 ++--- src/server/data/mod.rs | 2 +- src/server/data/text_component.rs | 49 +++--- src/server/event/mod.rs | 4 +- src/server/mod.rs | 45 +++--- src/server/player/context.rs | 59 ++++--- src/server/player/helper.rs | 95 +++++++---- src/server/player/mod.rs | 2 +- src/server/protocol/handler.rs | 253 ++++++++++++++++-------------- src/server/protocol/id.rs | 7 +- src/server/protocol/mod.rs | 6 +- src/server/protocol/play.rs | 21 +-- 15 files changed, 460 insertions(+), 362 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a2a5321..3ce68c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "editor.fontFamily": "Fira Code", - "editor.fontLigatures": true + "editor.fontLigatures": true, + "editor.tabSize": 2, } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e39d408..e34a01b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,18 +3,28 @@ use std::{env::args, path::PathBuf, sync::Arc}; use log::{debug, error, info}; use rust_mc_proto::Packet; use server::{ - config::Config, context::ServerContext, data::text_component::TextComponent, event::{Listener, PacketHandler}, player::context::ClientContext, protocol::ConnectionState, start_server, ServerError + ServerError, + config::Config, + context::ServerContext, + data::text_component::TextComponent, + event::{Listener, PacketHandler}, + player::context::ClientContext, + protocol::ConnectionState, + start_server, }; 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\": {} @@ -33,112 +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 90c5226..a2f803b 100644 --- a/src/server/config.rs +++ b/src/server/config.rs @@ -3,28 +3,39 @@ use std::{fs, path::PathBuf}; use serde::{Deserialize, Serialize}; 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() } -fn default_timeout() -> u64 { 5 } -fn default_compression() -> Option { Some(256) } +fn default_host() -> String { + "127.0.0.1:25565".to_string() +} +fn default_timeout() -> u64 { + 5 +} +fn default_compression() -> Option { + Some(256) +} impl Config { pub fn load_from_file(path: PathBuf) -> Option { @@ -38,4 +49,4 @@ impl Config { let table = toml::from_str::(&content).ok()?; Some(table) } -} \ No newline at end of file +} diff --git a/src/server/context.rs b/src/server/context.rs index 9814cd4..f54c795 100644 --- a/src/server/context.rs +++ b/src/server/context.rs @@ -4,7 +4,11 @@ use dashmap::DashMap; use itertools::Itertools; use uuid::Uuid; -use super::{config::Config, event::{Listener, PacketHandler}, player::context::ClientContext}; +use super::{ + config::Config, + event::{Listener, PacketHandler}, + player::context::ClientContext, +}; // Контекст сервера // Должен быть обернут в Arc для передачи между потоками @@ -12,7 +16,7 @@ pub struct ServerContext { pub config: Arc, pub clients: DashMap>, listeners: Vec>, - handlers: Vec> + handlers: Vec>, } impl ServerContext { @@ -21,12 +25,13 @@ impl ServerContext { config, listeners: Vec::new(), handlers: Vec::new(), - clients: DashMap::new() + clients: DashMap::new(), } } pub fn get_player_by_uuid(self: &Arc, uuid: Uuid) -> Option> { - self.clients.iter() + self.clients + .iter() .find(|o| { let info = o.player_info(); if let Some(info) = info { @@ -39,7 +44,8 @@ impl ServerContext { } pub fn get_player_by_name(self: &Arc, name: &str) -> Option> { - self.clients.iter() + self.clients + .iter() .find(|o| { let info = o.player_info(); if let Some(info) = info { @@ -52,7 +58,8 @@ impl ServerContext { } pub fn players(self: &Arc) -> Vec> { - self.clients.iter() + self.clients + .iter() .filter(|o| o.player_info().is_some()) .map(|o| o.clone()) .collect() @@ -66,24 +73,18 @@ impl ServerContext { self.listeners.push(listener); } - pub fn packet_handlers( - self: &Arc, - sort_by: F - ) -> Vec<&Box> - where + pub fn packet_handlers(self: &Arc, sort_by: F) -> Vec<&Box> + where K: Ord, - F: FnMut(&&Box) -> K + 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 + pub fn listeners(self: &Arc, sort_by: F) -> Vec<&Box> + where K: Ord, - F: FnMut(&&Box) -> K + 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 540fa53..1e887b3 100644 --- a/src/server/data/mod.rs +++ b/src/server/data/mod.rs @@ -8,4 +8,4 @@ pub mod text_component; pub trait ReadWriteNBT: DataReader + DataWriter { fn read_nbt(&mut self) -> Result; fn write_nbt(&mut self, val: &T) -> Result<(), ServerError>; -} \ No newline at end of file +} diff --git a/src/server/data/text_component.rs b/src/server/data/text_component.rs index cd2b839..c5095b9 100644 --- a/src/server/data/text_component.rs +++ b/src/server/data/text_component.rs @@ -9,7 +9,6 @@ use crate::server::ServerError; use super::ReadWriteNBT; - #[derive(Debug, Serialize, Deserialize, Clone)] #[skip_serializing_none] pub struct TextComponent { @@ -34,7 +33,7 @@ impl TextComponent { underlined: None, strikethrough: None, obfuscated: None, - extra: None + extra: None, } } @@ -43,7 +42,8 @@ impl TextComponent { return TextComponent::new(text); } - let children = text.char_indices() + 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); @@ -56,7 +56,7 @@ impl TextComponent { component }) .collect::>(); - + let mut parent = children[0].clone(); parent.extra = Some(children[1..].to_vec()); parent @@ -67,13 +67,11 @@ impl TextComponent { } pub fn as_json(self) -> Result { - serde_json::to_string(&self) - .map_err(|_| ServerError::SerTextComponent) + 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) + serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent) } } @@ -149,15 +147,15 @@ impl TextComponentBuilder { } 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 + 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, } } } @@ -167,15 +165,18 @@ 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); + 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)?; + craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?; Ok(()) } -} \ No newline at end of file +} diff --git a/src/server/event/mod.rs b/src/server/event/mod.rs index 1477b8e..e8ff602 100644 --- a/src/server/event/mod.rs +++ b/src/server/event/mod.rs @@ -18,7 +18,7 @@ macro_rules! generate_handlers { } /// Пример использования: -/// +/// /// trigger_event!(client, status, &mut response, state); #[macro_export] macro_rules! trigger_event { @@ -45,4 +45,4 @@ 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); -} \ No newline at end of file +} diff --git a/src/server/mod.rs b/src/server/mod.rs index 51aa3bd..8a27de6 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -8,10 +8,10 @@ use protocol::handler::handle_connection; use rust_mc_proto::{MinecraftConnection, ProtocolError}; pub mod config; +pub mod context; pub mod data; pub mod event; pub mod player; -pub mod context; pub mod protocol; // Ошибки сервера @@ -19,11 +19,11 @@ pub mod protocol; pub enum ServerError { UnexpectedPacket, // Неожиданный пакет Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto - ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection - SerTextComponent, // Ошибка при сериализации текст-компонента - DeTextComponent, // Ошибка при десериализации текст-компонента - UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper) - Other(String) // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки + ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection + SerTextComponent, // Ошибка при сериализации текст-компонента + DeTextComponent, // Ошибка при десериализации текст-компонента + UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper) + Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки } impl Display for ServerError { @@ -39,36 +39,39 @@ impl From for ServerError { fn from(error: ProtocolError) -> ServerError { match error { // Если просто закрыто соединение, переделываем в нашу ошибку этого - ProtocolError::ConnectionClosedError => { - ServerError::ConnectionClosed - }, + ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed, // Все остальное просто засовываем в обертку - error => { - ServerError::Protocol(error) - }, + 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); + 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(); - thread::spawn(move || { + 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(); + 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); @@ -84,11 +87,11 @@ pub fn start_server(server: Arc) { // Обработка подключения // Если ошибка -> выводим match handle_connection(client.clone()) { - Ok(_) => {}, - Err(ServerError::ConnectionClosed) => {}, + Ok(_) => {} + Err(ServerError::ConnectionClosed) => {} Err(error) => { error!("Ошибка подключения: {error:?}"); - }, + } }; // Удаляем клиента из списка клиентов diff --git a/src/server/player/context.rs b/src/server/player/context.rs index 08b8140..05bdbb1 100644 --- a/src/server/player/context.rs +++ b/src/server/player/context.rs @@ -1,9 +1,13 @@ -use std::{hash::Hash, net::{SocketAddr, TcpStream}, sync::{Arc, RwLock}}; +use std::{ + hash::Hash, + net::{SocketAddr, TcpStream}, + sync::{Arc, RwLock}, +}; use rust_mc_proto::{MinecraftConnection, Packet}; use uuid::Uuid; -use crate::server::{context::ServerContext, protocol::ConnectionState, ServerError}; +use crate::server::{ServerError, context::ServerContext, protocol::ConnectionState}; use super::helper::ProtocolHelper; @@ -16,7 +20,7 @@ pub struct ClientContext { handshake: RwLock>, client_info: RwLock>, player_info: RwLock>, - state: RwLock + state: RwLock, } // Реализуем сравнение через адрес @@ -36,10 +40,7 @@ impl Hash for ClientContext { impl Eq for ClientContext {} impl ClientContext { - pub fn new( - server: Arc, - conn: MinecraftConnection - ) -> ClientContext { + pub fn new(server: Arc, conn: MinecraftConnection) -> ClientContext { ClientContext { server, addr: conn.get_ref().peer_addr().unwrap(), @@ -47,7 +48,7 @@ impl ClientContext { handshake: RwLock::new(None), client_info: RwLock::new(None), player_info: RwLock::new(None), - state: RwLock::new(ConnectionState::Handshake) + state: RwLock::new(ConnectionState::Handshake), } } @@ -66,9 +67,11 @@ impl ClientContext { 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() { + for handler in self + .server + .packet_handlers(|o| o.on_state_priority()) + .iter() + { handler.on_state(self.clone(), state.clone())?; } @@ -95,10 +98,17 @@ impl ClientContext { 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())?; + 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 { @@ -115,10 +125,17 @@ impl ClientContext { loop { let mut packet = conn.read_packet()?; 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())?; + 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 { @@ -167,11 +184,11 @@ pub struct ClientInfo { pub main_hand: i32, pub enable_text_filtering: bool, pub allow_server_listings: bool, - pub particle_status: i32 + pub particle_status: i32, } #[derive(Clone)] pub struct PlayerInfo { pub name: String, - pub uuid: Uuid + pub uuid: Uuid, } diff --git a/src/server/player/helper.rs b/src/server/player/helper.rs index b73d826..ccb6146 100644 --- a/src/server/player/helper.rs +++ b/src/server/player/helper.rs @@ -1,12 +1,22 @@ -use std::{io::Read, sync::Arc, time::{Duration, SystemTime}}; +use std::{ + io::Read, + sync::Arc, + time::{Duration, SystemTime}, +}; use rust_mc_proto::{DataReader, DataWriter, Packet}; -use crate::server::{data::{text_component::TextComponent, ReadWriteNBT}, protocol::{id::{clientbound, serverbound}, *}, ServerError}; +use crate::server::{ + ServerError, + data::{ReadWriteNBT, text_component::TextComponent}, + protocol::{ + id::{clientbound, serverbound}, + *, + }, +}; use super::context::ClientContext; - // Помощник в работе с протоколом // Может быть использован где угодно, но сделан именно для листенеров и пакет хандлеров // Через него удобно делать всякую одинаковую херь @@ -16,14 +26,14 @@ use super::context::ClientContext; // Почему бы и нет если да pub struct ProtocolHelper { client: Arc, - state: ConnectionState + state: ConnectionState, } impl ProtocolHelper { pub fn new(client: Arc) -> Self { Self { state: client.state(), - client + client, } } @@ -31,12 +41,14 @@ impl ProtocolHelper { 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 + .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) + } + _ => Err(ServerError::UnexpectedState), } } @@ -44,12 +56,14 @@ impl ProtocolHelper { 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 + .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) + } + _ => Err(ServerError::UnexpectedState), } } @@ -58,40 +72,42 @@ impl ProtocolHelper { match self.state { ConnectionState::Play => { let time = SystemTime::now(); - self.client.write_packet(&Packet::empty(clientbound::play::PING))?; + 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 + .write_packet(&Packet::empty(clientbound::configuration::PING))?; self.client.read_packet(serverbound::configuration::PONG)?; Ok(SystemTime::now().duration_since(time).unwrap()) - }, - _ => Err(ServerError::UnexpectedState) + } + _ => 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(()) - }, + return Ok(()); + } }; self.client.write_packet(&packet)?; Ok(()) @@ -105,7 +121,9 @@ impl ProtocolHelper { packet.write_string(id)?; self.client.write_packet(&packet)?; - let mut packet = self.client.read_packet(serverbound::configuration::COOKIE_RESPONSE)?; + 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()?; @@ -115,13 +133,18 @@ impl ProtocolHelper { }; Ok(data) - }, - _ => Err(ServerError::UnexpectedState) + } + _ => 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> { + 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); @@ -130,7 +153,9 @@ impl ProtocolHelper { packet.write_bytes(data)?; self.client.write_packet(&packet)?; - let mut packet = self.client.read_packet(serverbound::login::PLUGIN_RESPONSE)?; + 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(); @@ -141,20 +166,22 @@ impl ProtocolHelper { }; Ok((identifier, data)) - }, - _ => Err(ServerError::UnexpectedState) + } + _ => 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::Configuration => { + Packet::empty(clientbound::configuration::PLUGIN_MESSAGE) + } ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE), - _ => return Err(ServerError::UnexpectedState) + _ => return Err(ServerError::UnexpectedState), }; packet.write_string(channel)?; packet.write_bytes(data)?; self.client.write_packet(&packet)?; Ok(()) } -} \ No newline at end of file +} diff --git a/src/server/player/mod.rs b/src/server/player/mod.rs index bbad114..ccbba0b 100644 --- a/src/server/player/mod.rs +++ b/src/server/player/mod.rs @@ -1,2 +1,2 @@ pub mod context; -pub mod helper; \ No newline at end of file +pub mod helper; diff --git a/src/server/protocol/handler.rs b/src/server/protocol/handler.rs index 336ad22..de7165c 100644 --- a/src/server/protocol/handler.rs +++ b/src/server/protocol/handler.rs @@ -1,160 +1,177 @@ use std::{io::Read, sync::Arc}; -use crate::server::{player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo}, ServerError}; +use crate::server::{ + ServerError, + player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo}, +}; use rust_mc_proto::{DataReader, DataWriter, Packet}; use crate::trigger_event; -use super::{id::*, play::handle_play_state, ConnectionState}; - +use super::{ConnectionState, id::*, play::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)?; - }, - _ => { - return Err(ServerError::UnexpectedPacket); - } - } - } - }, - 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)?; + } + _ => { + return Err(ServerError::UnexpectedPacket); + } + } + } + } + 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 - - // Получение бренда клиента из Serverbound Plugin Message - // Identifier канала откуда берется бренд: minecraft:brand - let brand = loop { - let mut packet = client.read_packet(serverbound::configuration::PLUGIN_MESSAGE)?; // Пакет Serverbound Plugin Message + client.set_state(ConnectionState::Configuration)?; // Мы перешли в режим Configuration - let identifier = packet.read_string()?; + // Получение бренда клиента из Serverbound Plugin Message + // Identifier канала откуда берется бренд: minecraft:brand + let brand = loop { + let mut packet = client.read_packet(serverbound::configuration::PLUGIN_MESSAGE)?; // Пакет Serverbound Plugin Message - let mut data = Vec::new(); - packet.get_mut().read_to_end(&mut data).unwrap(); + let identifier = packet.read_string()?; - if identifier == "minecraft:brand" { - break String::from_utf8_lossy(&data).to_string(); - } else { - trigger_event!(client, plugin_message, &identifier, &data); - } - }; + let mut data = Vec::new(); + packet.get_mut().read_to_end(&mut data).unwrap(); - let mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information + if identifier == "minecraft:brand" { + break String::from_utf8_lossy(&data).to_string(); + } else { + trigger_event!(client, plugin_message, &identifier, &data); + } + }; - 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 mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information - 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 - }); + 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 - // TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото + 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::empty(clientbound::configuration::FINISH))?; - client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; + // TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото - client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play + client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?; + client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; - // Дальше работаем с режимом игры - handle_play_state(client)?; - }, - _ => { // Тип подключения не рукопожатный - return Err(ServerError::UnexpectedPacket); - } - } + client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play - Ok(()) -} \ No newline at end of file + // Дальше работаем с режимом игры + handle_play_state(client)?; + } + _ => { + // Тип подключения не рукопожатный + return Err(ServerError::UnexpectedPacket); + } + } + + Ok(()) +} diff --git a/src/server/protocol/id.rs b/src/server/protocol/id.rs index fe7bab2..5c26854 100644 --- a/src/server/protocol/id.rs +++ b/src/server/protocol/id.rs @@ -1,8 +1,8 @@ /* - Generated with parse_ids.py +Generated with parse_ids.py - */ +*/ pub mod clientbound { pub mod status { @@ -172,7 +172,6 @@ pub mod clientbound { pub const CUSTOM_REPORT_DETAILS: u8 = 0x81; pub const SERVER_LINKS: u8 = 0x82; } - } pub mod serverbound { @@ -270,6 +269,4 @@ pub mod serverbound { 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 53ab91d..5110371 100644 --- a/src/server/protocol/mod.rs +++ b/src/server/protocol/mod.rs @@ -1,7 +1,6 @@ +pub mod handler; pub mod id; pub mod play; -pub mod handler; - #[derive(Debug, Clone)] pub enum ConnectionState { @@ -9,6 +8,5 @@ pub enum ConnectionState { Status, Login, Configuration, - Play + Play, } - diff --git a/src/server/protocol/play.rs b/src/server/protocol/play.rs index de5cff6..9f5e99a 100644 --- a/src/server/protocol/play.rs +++ b/src/server/protocol/play.rs @@ -1,16 +1,19 @@ use std::sync::Arc; -use crate::server::{data::text_component::TextComponent, player::context::ClientContext, ServerError}; +use crate::server::{ + ServerError, data::text_component::TextComponent, player::context::ClientContext, +}; // Отдельная функция для работы с самой игрой pub fn handle_play_state( - client: Arc, // Контекст клиента -) -> Result<(), ServerError> { + client: Arc, // Контекст клиента +) -> Result<(), ServerError> { + // Отключение игрока с сообщением + client.protocol_helper().disconnect(TextComponent::rainbow( + "server is in developement suka".to_string(), + ))?; - // Отключение игрока с сообщением - client.protocol_helper().disconnect(TextComponent::rainbow("server is in developement suka".to_string()))?; + // TODO: Сделать отправку пакетов Play - // TODO: Сделать отправку пакетов Play - - Ok(()) -} \ No newline at end of file + Ok(()) +}