This commit is contained in:
MeexReay 2025-05-03 18:34:20 +03:00
parent 800e2ba5ca
commit d0968d1c34
15 changed files with 460 additions and 362 deletions

View File

@ -1,4 +1,5 @@
{ {
"editor.fontFamily": "Fira Code", "editor.fontFamily": "Fira Code",
"editor.fontLigatures": true "editor.fontLigatures": true,
"editor.tabSize": 2,
} }

View File

@ -3,18 +3,28 @@ use std::{env::args, path::PathBuf, sync::Arc};
use log::{debug, error, info}; use log::{debug, error, info};
use rust_mc_proto::Packet; use rust_mc_proto::Packet;
use server::{ 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; pub mod server;
struct ExampleListener; struct ExampleListener;
impl Listener for ExampleListener { impl Listener for ExampleListener {
fn on_status(&self, client: Arc<ClientContext>, response: &mut String) -> Result<(), ServerError> { fn on_status(
*response = format!( &self,
"{{ client: Arc<ClientContext>,
response: &mut String,
) -> Result<(), ServerError> {
*response = format!(
"{{
\"version\": {{ \"version\": {{
\"name\": \"idk\", \"name\": \"idk\",
\"protocol\": {} \"protocol\": {}
@ -33,112 +43,124 @@ impl Listener for ExampleListener {
\"favicon\": \"data:image/png;base64,<data>\", \"favicon\": \"data:image/png;base64,<data>\",
\"enforcesSecureChat\": false \"enforcesSecureChat\": false
}}", }}",
client.handshake().unwrap().protocol_version, client.handshake().unwrap().protocol_version,
TextComponent::builder() TextComponent::builder()
.text("Hello World! ") .text("Hello World! ")
.extra(vec![ .extra(vec![
TextComponent::builder() TextComponent::builder()
.text("Protocol: ") .text("Protocol: ")
.color("gold") .color("gold")
.extra(vec![ .extra(vec![
TextComponent::builder() TextComponent::builder()
.text(&client.handshake().unwrap().protocol_version.to_string()) .text(&client.handshake().unwrap().protocol_version.to_string())
.underlined(true) .underlined(true)
.build() .build()
]) ])
.build(), .build(),
TextComponent::builder() TextComponent::builder()
.text("\nServer Addr: ") .text("\nServer Addr: ")
.color("green") .color("green")
.extra(vec![ .extra(vec![
TextComponent::builder() TextComponent::builder()
.text(&format!("{}:{}", .text(&format!(
client.handshake().unwrap().server_address, "{}:{}",
client.handshake().unwrap().server_port client.handshake().unwrap().server_address,
)) client.handshake().unwrap().server_port
.underlined(true) ))
.build() .underlined(true)
]) .build()
.build() ])
]) .build()
.build() ])
.as_json()? .build()
); .as_json()?
);
Ok(()) Ok(())
} }
} }
struct ExamplePacketHandler; struct ExamplePacketHandler;
impl PacketHandler for ExamplePacketHandler { impl PacketHandler for ExamplePacketHandler {
fn on_incoming_packet( fn on_incoming_packet(
&self, &self,
client: Arc<ClientContext>, client: Arc<ClientContext>,
packet: &mut Packet, packet: &mut Packet,
_: &mut bool, _: &mut bool,
state: ConnectionState state: ConnectionState,
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
debug!("{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len()); debug!(
"{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes",
client.addr.clone(),
packet.id(),
state,
packet.len()
);
Ok(()) Ok(())
} }
fn on_outcoming_packet( fn on_outcoming_packet(
&self, &self,
client: Arc<ClientContext>, client: Arc<ClientContext>,
packet: &mut Packet, packet: &mut Packet,
_: &mut bool, _: &mut bool,
state: ConnectionState state: ConnectionState,
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
debug!("{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len()); debug!(
"{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes",
client.addr.clone(),
packet.id(),
state,
packet.len()
);
Ok(()) Ok(())
} }
} }
fn main() { fn main() {
// Инициализируем логи // Инициализируем логи
// Чтобы читать debug-логи, юзаем `RUST_LOG=debug cargo run` // Чтобы читать debug-логи, юзаем `RUST_LOG=debug cargo run`
colog::init(); colog::init();
// Получение аргументов // Получение аргументов
let exec = args().next().expect("Неизвестная система"); let exec = args().next().expect("Неизвестная система");
let args = args().skip(1).collect::<Vec<String>>(); let args = args().skip(1).collect::<Vec<String>>();
if args.len() > 1 { if args.len() > 1 {
info!("Использование: {exec} [путь до файла конфигурации]"); info!("Использование: {exec} [путь до файла конфигурации]");
return; return;
} }
// Берем путь из аргумента либо по дефолту берем "./server.toml" // Берем путь из аргумента либо по дефолту берем "./server.toml"
let config_path = PathBuf::from(args.get(0).unwrap_or(&"server.toml".to_string())); let config_path = PathBuf::from(args.get(0).unwrap_or(&"server.toml".to_string()));
// Чтение конфига, если ошибка - выводим // Чтение конфига, если ошибка - выводим
let config = match Config::load_from_file(config_path) { let config = match Config::load_from_file(config_path) {
Some(config) => config, Some(config) => config,
None => { None => {
error!("Ошибка чтения конфигурации"); error!("Ошибка чтения конфигурации");
return; 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_listener(Box::new(ExampleListener)); // Добавляем пример листенера
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
// Бетонируем сервер контекст от изменений // Бетонируем сервер контекст от изменений
let server = Arc::new(server); let server = Arc::new(server);
// Запускаем сервер из специально отведенной под это дело функцией // Запускаем сервер из специально отведенной под это дело функцией
start_server(server); start_server(server);
} }

View File

@ -3,28 +3,39 @@ use std::{fs, path::PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_default::DefaultFromSerde; use serde_default::DefaultFromSerde;
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
pub struct BindConfig { pub struct BindConfig {
#[serde(default = "default_host")] pub host: String, #[serde(default = "default_host")]
#[serde(default = "default_timeout")] pub timeout: u64, pub host: String,
#[serde(default = "default_timeout")]
pub timeout: u64,
} }
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
pub struct ServerConfig { pub struct ServerConfig {
#[serde(default)] pub online_mode: bool, #[serde(default)]
#[serde(default = "default_compression")] pub compression_threshold: Option<usize>, pub online_mode: bool,
#[serde(default = "default_compression")]
pub compression_threshold: Option<usize>,
} }
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
pub struct Config { pub struct Config {
#[serde(default)] pub bind: BindConfig, #[serde(default)]
#[serde(default)] pub server: ServerConfig, pub bind: BindConfig,
#[serde(default)]
pub server: ServerConfig,
} }
fn default_host() -> String { "127.0.0.1:25565".to_string() } fn default_host() -> String {
fn default_timeout() -> u64 { 5 } "127.0.0.1:25565".to_string()
fn default_compression() -> Option<usize> { Some(256) } }
fn default_timeout() -> u64 {
5
}
fn default_compression() -> Option<usize> {
Some(256)
}
impl Config { impl Config {
pub fn load_from_file(path: PathBuf) -> Option<Config> { pub fn load_from_file(path: PathBuf) -> Option<Config> {
@ -38,4 +49,4 @@ impl Config {
let table = toml::from_str::<Config>(&content).ok()?; let table = toml::from_str::<Config>(&content).ok()?;
Some(table) Some(table)
} }
} }

View File

@ -4,7 +4,11 @@ use dashmap::DashMap;
use itertools::Itertools; use itertools::Itertools;
use uuid::Uuid; use uuid::Uuid;
use super::{config::Config, event::{Listener, PacketHandler}, player::context::ClientContext}; use super::{
config::Config,
event::{Listener, PacketHandler},
player::context::ClientContext,
};
// Контекст сервера // Контекст сервера
// Должен быть обернут в Arc для передачи между потоками // Должен быть обернут в Arc для передачи между потоками
@ -12,7 +16,7 @@ pub struct ServerContext {
pub config: Arc<Config>, pub config: Arc<Config>,
pub clients: DashMap<SocketAddr, Arc<ClientContext>>, pub clients: DashMap<SocketAddr, Arc<ClientContext>>,
listeners: Vec<Box<dyn Listener>>, listeners: Vec<Box<dyn Listener>>,
handlers: Vec<Box<dyn PacketHandler>> handlers: Vec<Box<dyn PacketHandler>>,
} }
impl ServerContext { impl ServerContext {
@ -21,12 +25,13 @@ impl ServerContext {
config, config,
listeners: Vec::new(), listeners: Vec::new(),
handlers: Vec::new(), handlers: Vec::new(),
clients: DashMap::new() clients: DashMap::new(),
} }
} }
pub fn get_player_by_uuid(self: &Arc<Self>, uuid: Uuid) -> Option<Arc<ClientContext>> { pub fn get_player_by_uuid(self: &Arc<Self>, uuid: Uuid) -> Option<Arc<ClientContext>> {
self.clients.iter() self.clients
.iter()
.find(|o| { .find(|o| {
let info = o.player_info(); let info = o.player_info();
if let Some(info) = info { if let Some(info) = info {
@ -39,7 +44,8 @@ impl ServerContext {
} }
pub fn get_player_by_name(self: &Arc<Self>, name: &str) -> Option<Arc<ClientContext>> { pub fn get_player_by_name(self: &Arc<Self>, name: &str) -> Option<Arc<ClientContext>> {
self.clients.iter() self.clients
.iter()
.find(|o| { .find(|o| {
let info = o.player_info(); let info = o.player_info();
if let Some(info) = info { if let Some(info) = info {
@ -52,7 +58,8 @@ impl ServerContext {
} }
pub fn players(self: &Arc<Self>) -> Vec<Arc<ClientContext>> { pub fn players(self: &Arc<Self>) -> Vec<Arc<ClientContext>> {
self.clients.iter() self.clients
.iter()
.filter(|o| o.player_info().is_some()) .filter(|o| o.player_info().is_some())
.map(|o| o.clone()) .map(|o| o.clone())
.collect() .collect()
@ -66,24 +73,18 @@ impl ServerContext {
self.listeners.push(listener); self.listeners.push(listener);
} }
pub fn packet_handlers<F, K>( pub fn packet_handlers<F, K>(self: &Arc<Self>, sort_by: F) -> Vec<&Box<dyn PacketHandler>>
self: &Arc<Self>, where
sort_by: F
) -> Vec<&Box<dyn PacketHandler>>
where
K: Ord, K: Ord,
F: FnMut(&&Box<dyn PacketHandler>) -> K F: FnMut(&&Box<dyn PacketHandler>) -> K,
{ {
self.handlers.iter().sorted_by_key(sort_by).collect_vec() self.handlers.iter().sorted_by_key(sort_by).collect_vec()
} }
pub fn listeners<F, K>( pub fn listeners<F, K>(self: &Arc<Self>, sort_by: F) -> Vec<&Box<dyn Listener>>
self: &Arc<Self>, where
sort_by: F
) -> Vec<&Box<dyn Listener>>
where
K: Ord, K: Ord,
F: FnMut(&&Box<dyn Listener>) -> K F: FnMut(&&Box<dyn Listener>) -> K,
{ {
self.listeners.iter().sorted_by_key(sort_by).collect_vec() self.listeners.iter().sorted_by_key(sort_by).collect_vec()
} }

View File

@ -8,4 +8,4 @@ pub mod text_component;
pub trait ReadWriteNBT<T>: DataReader + DataWriter { pub trait ReadWriteNBT<T>: DataReader + DataWriter {
fn read_nbt(&mut self) -> Result<T, ServerError>; fn read_nbt(&mut self) -> Result<T, ServerError>;
fn write_nbt(&mut self, val: &T) -> Result<(), ServerError>; fn write_nbt(&mut self, val: &T) -> Result<(), ServerError>;
} }

View File

@ -9,7 +9,6 @@ use crate::server::ServerError;
use super::ReadWriteNBT; use super::ReadWriteNBT;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
#[skip_serializing_none] #[skip_serializing_none]
pub struct TextComponent { pub struct TextComponent {
@ -34,7 +33,7 @@ impl TextComponent {
underlined: None, underlined: None,
strikethrough: None, strikethrough: None,
obfuscated: None, obfuscated: None,
extra: None extra: None,
} }
} }
@ -43,7 +42,8 @@ impl TextComponent {
return TextComponent::new(text); return TextComponent::new(text);
} }
let children = text.char_indices() let children = text
.char_indices()
.map(|(i, c)| { .map(|(i, c)| {
let hue = (i as f32) / (text.chars().count() as f32) * 360.0; let hue = (i as f32) / (text.chars().count() as f32) * 360.0;
let hsl = Hsl::new(hue, 1.0, 0.5); let hsl = Hsl::new(hue, 1.0, 0.5);
@ -56,7 +56,7 @@ impl TextComponent {
component component
}) })
.collect::<Vec<TextComponent>>(); .collect::<Vec<TextComponent>>();
let mut parent = children[0].clone(); let mut parent = children[0].clone();
parent.extra = Some(children[1..].to_vec()); parent.extra = Some(children[1..].to_vec());
parent parent
@ -67,13 +67,11 @@ impl TextComponent {
} }
pub fn as_json(self) -> Result<String, ServerError> { pub fn as_json(self) -> Result<String, ServerError> {
serde_json::to_string(&self) serde_json::to_string(&self).map_err(|_| ServerError::SerTextComponent)
.map_err(|_| ServerError::SerTextComponent)
} }
pub fn from_json(text: &str) -> Result<TextComponent, ServerError> { pub fn from_json(text: &str) -> Result<TextComponent, ServerError> {
serde_json::from_str(text) serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent)
.map_err(|_| ServerError::DeTextComponent)
} }
} }
@ -149,15 +147,15 @@ impl TextComponentBuilder {
} }
pub fn build(self) -> TextComponent { pub fn build(self) -> TextComponent {
TextComponent { TextComponent {
text: self.text, text: self.text,
color: self.color, color: self.color,
bold: self.bold, bold: self.bold,
italic: self.italic, italic: self.italic,
underlined: self.underlined, underlined: self.underlined,
strikethrough: self.strikethrough, strikethrough: self.strikethrough,
obfuscated: self.obfuscated, obfuscated: self.obfuscated,
extra: self.extra extra: self.extra,
} }
} }
} }
@ -167,15 +165,18 @@ impl ReadWriteNBT<TextComponent> for Packet {
fn read_nbt(&mut self) -> Result<TextComponent, ServerError> { fn read_nbt(&mut self) -> Result<TextComponent, ServerError> {
let mut data = Vec::new(); let mut data = Vec::new();
let pos = self.get_ref().position(); let pos = self.get_ref().position();
self.get_mut().read_to_end(&mut data).map_err(|_| ServerError::DeTextComponent)?; self.get_mut()
let (remaining, value) = craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?; .read_to_end(&mut data)
self.get_mut().set_position(pos + (data.len() - remaining.len()) as u64); .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) Ok(value)
} }
fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> { fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> {
craftflow_nbt::to_writer(self.get_mut(), val) craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?;
.map_err(|_| ServerError::SerTextComponent)?;
Ok(()) Ok(())
} }
} }

View File

@ -18,7 +18,7 @@ macro_rules! generate_handlers {
} }
/// Пример использования: /// Пример использования:
/// ///
/// trigger_event!(client, status, &mut response, state); /// trigger_event!(client, status, &mut response, state);
#[macro_export] #[macro_export]
macro_rules! trigger_event { macro_rules! trigger_event {
@ -45,4 +45,4 @@ pub trait PacketHandler: Sync + Send {
generate_handlers!(incoming_packet, &mut Packet, &mut bool, ConnectionState); generate_handlers!(incoming_packet, &mut Packet, &mut bool, ConnectionState);
generate_handlers!(outcoming_packet, &mut Packet, &mut bool, ConnectionState); generate_handlers!(outcoming_packet, &mut Packet, &mut bool, ConnectionState);
generate_handlers!(state, ConnectionState); generate_handlers!(state, ConnectionState);
} }

View File

@ -8,10 +8,10 @@ use protocol::handler::handle_connection;
use rust_mc_proto::{MinecraftConnection, ProtocolError}; use rust_mc_proto::{MinecraftConnection, ProtocolError};
pub mod config; pub mod config;
pub mod context;
pub mod data; pub mod data;
pub mod event; pub mod event;
pub mod player; pub mod player;
pub mod context;
pub mod protocol; pub mod protocol;
// Ошибки сервера // Ошибки сервера
@ -19,11 +19,11 @@ pub mod protocol;
pub enum ServerError { pub enum ServerError {
UnexpectedPacket, // Неожиданный пакет UnexpectedPacket, // Неожиданный пакет
Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto
ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection
SerTextComponent, // Ошибка при сериализации текст-компонента SerTextComponent, // Ошибка при сериализации текст-компонента
DeTextComponent, // Ошибка при десериализации текст-компонента DeTextComponent, // Ошибка при десериализации текст-компонента
UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper) UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper)
Other(String) // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки
} }
impl Display for ServerError { impl Display for ServerError {
@ -39,36 +39,39 @@ impl From<ProtocolError> for ServerError {
fn from(error: ProtocolError) -> ServerError { fn from(error: ProtocolError) -> ServerError {
match error { match error {
// Если просто закрыто соединение, переделываем в нашу ошибку этого // Если просто закрыто соединение, переделываем в нашу ошибку этого
ProtocolError::ConnectionClosedError => { ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed,
ServerError::ConnectionClosed
},
// Все остальное просто засовываем в обертку // Все остальное просто засовываем в обертку
error => { error => ServerError::Protocol(error),
ServerError::Protocol(error)
},
} }
} }
} }
pub fn start_server(server: Arc<ServerContext>) { pub fn start_server(server: Arc<ServerContext>) {
// Биндим сервер где надо // Биндим сервер где надо
let Ok(listener) = TcpListener::bind(&server.config.bind.host) else { let Ok(listener) = TcpListener::bind(&server.config.bind.host) else {
error!("Не удалось забиндить сервер на {}", &server.config.bind.host); error!(
"Не удалось забиндить сервер на {}",
&server.config.bind.host
);
return; return;
}; };
info!("Сервер запущен на {}", &server.config.bind.host); info!("Сервер запущен на {}", &server.config.bind.host);
while let Ok((stream, addr)) = listener.accept() { while let Ok((stream, addr)) = listener.accept() {
let server = server.clone(); let server = server.clone();
thread::spawn(move || { thread::spawn(move || {
info!("Подключение: {}", addr); info!("Подключение: {}", addr);
// Установка таймаутов на чтение и запись // Установка таймаутов на чтение и запись
// По умолчанию пусть будет 5 секунд, надо будет сделать настройку через конфиг // По умолчанию пусть будет 5 секунд, надо будет сделать настройку через конфиг
stream.set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout))).ignore(); stream
stream.set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout))).ignore(); .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);
@ -84,11 +87,11 @@ pub fn start_server(server: Arc<ServerContext>) {
// Обработка подключения // Обработка подключения
// Если ошибка -> выводим // Если ошибка -> выводим
match handle_connection(client.clone()) { match handle_connection(client.clone()) {
Ok(_) => {}, Ok(_) => {}
Err(ServerError::ConnectionClosed) => {}, Err(ServerError::ConnectionClosed) => {}
Err(error) => { Err(error) => {
error!("Ошибка подключения: {error:?}"); error!("Ошибка подключения: {error:?}");
}, }
}; };
// Удаляем клиента из списка клиентов // Удаляем клиента из списка клиентов

View File

@ -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 rust_mc_proto::{MinecraftConnection, Packet};
use uuid::Uuid; use uuid::Uuid;
use crate::server::{context::ServerContext, protocol::ConnectionState, ServerError}; use crate::server::{ServerError, context::ServerContext, protocol::ConnectionState};
use super::helper::ProtocolHelper; use super::helper::ProtocolHelper;
@ -16,7 +20,7 @@ pub struct ClientContext {
handshake: RwLock<Option<Handshake>>, handshake: RwLock<Option<Handshake>>,
client_info: RwLock<Option<ClientInfo>>, client_info: RwLock<Option<ClientInfo>>,
player_info: RwLock<Option<PlayerInfo>>, player_info: RwLock<Option<PlayerInfo>>,
state: RwLock<ConnectionState> state: RwLock<ConnectionState>,
} }
// Реализуем сравнение через адрес // Реализуем сравнение через адрес
@ -36,10 +40,7 @@ impl Hash for ClientContext {
impl Eq for ClientContext {} impl Eq for ClientContext {}
impl ClientContext { impl ClientContext {
pub fn new( pub fn new(server: Arc<ServerContext>, conn: MinecraftConnection<TcpStream>) -> ClientContext {
server: Arc<ServerContext>,
conn: MinecraftConnection<TcpStream>
) -> ClientContext {
ClientContext { ClientContext {
server, server,
addr: conn.get_ref().peer_addr().unwrap(), addr: conn.get_ref().peer_addr().unwrap(),
@ -47,7 +48,7 @@ impl ClientContext {
handshake: RwLock::new(None), handshake: RwLock::new(None),
client_info: RwLock::new(None), client_info: RwLock::new(None),
player_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<Self>, state: ConnectionState) -> Result<(), ServerError> { pub fn set_state(self: &Arc<Self>, state: ConnectionState) -> Result<(), ServerError> {
*self.state.write().unwrap() = state.clone(); *self.state.write().unwrap() = state.clone();
for handler in self.server.packet_handlers( for handler in self
|o| o.on_state_priority() .server
).iter() { .packet_handlers(|o| o.on_state_priority())
.iter()
{
handler.on_state(self.clone(), state.clone())?; handler.on_state(self.clone(), state.clone())?;
} }
@ -95,10 +98,17 @@ impl ClientContext {
let state = self.state(); let state = self.state();
let mut packet = packet.clone(); let mut packet = packet.clone();
let mut cancelled = false; let mut cancelled = false;
for handler in self.server.packet_handlers( for handler in self
|o| o.on_outcoming_packet_priority() .server
).iter() { .packet_handlers(|o| o.on_outcoming_packet_priority())
handler.on_outcoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?; .iter()
{
handler.on_outcoming_packet(
self.clone(),
&mut packet,
&mut cancelled,
state.clone(),
)?;
packet.get_mut().set_position(0); packet.get_mut().set_position(0);
} }
if !cancelled { if !cancelled {
@ -115,10 +125,17 @@ impl ClientContext {
loop { loop {
let mut packet = conn.read_packet()?; let mut packet = conn.read_packet()?;
let mut cancelled = false; let mut cancelled = false;
for handler in self.server.packet_handlers( for handler in self
|o| o.on_incoming_packet_priority() .server
).iter() { .packet_handlers(|o| o.on_incoming_packet_priority())
handler.on_incoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?; .iter()
{
handler.on_incoming_packet(
self.clone(),
&mut packet,
&mut cancelled,
state.clone(),
)?;
packet.get_mut().set_position(0); packet.get_mut().set_position(0);
} }
if !cancelled { if !cancelled {
@ -167,11 +184,11 @@ pub struct ClientInfo {
pub main_hand: i32, pub main_hand: i32,
pub enable_text_filtering: bool, pub enable_text_filtering: bool,
pub allow_server_listings: bool, pub allow_server_listings: bool,
pub particle_status: i32 pub particle_status: i32,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct PlayerInfo { pub struct PlayerInfo {
pub name: String, pub name: String,
pub uuid: Uuid pub uuid: Uuid,
} }

View File

@ -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 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; use super::context::ClientContext;
// Помощник в работе с протоколом // Помощник в работе с протоколом
// Может быть использован где угодно, но сделан именно для листенеров и пакет хандлеров // Может быть использован где угодно, но сделан именно для листенеров и пакет хандлеров
// Через него удобно делать всякую одинаковую херь // Через него удобно делать всякую одинаковую херь
@ -16,14 +26,14 @@ use super::context::ClientContext;
// Почему бы и нет если да // Почему бы и нет если да
pub struct ProtocolHelper { pub struct ProtocolHelper {
client: Arc<ClientContext>, client: Arc<ClientContext>,
state: ConnectionState state: ConnectionState,
} }
impl ProtocolHelper { impl ProtocolHelper {
pub fn new(client: Arc<ClientContext>) -> Self { pub fn new(client: Arc<ClientContext>) -> Self {
Self { Self {
state: client.state(), state: client.state(),
client client,
} }
} }
@ -31,12 +41,14 @@ impl ProtocolHelper {
pub fn leave_configuration(&self) -> Result<(), ServerError> { pub fn leave_configuration(&self) -> Result<(), ServerError> {
match self.state { match self.state {
ConnectionState::Configuration => { ConnectionState::Configuration => {
self.client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?; self.client
self.client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?; .write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
self.client
.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
self.client.set_state(ConnectionState::Play)?; self.client.set_state(ConnectionState::Play)?;
Ok(()) Ok(())
}, }
_ => Err(ServerError::UnexpectedState) _ => Err(ServerError::UnexpectedState),
} }
} }
@ -44,12 +56,14 @@ impl ProtocolHelper {
pub fn enter_configuration(&self) -> Result<(), ServerError> { pub fn enter_configuration(&self) -> Result<(), ServerError> {
match self.state { match self.state {
ConnectionState::Play => { ConnectionState::Play => {
self.client.write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?; self.client
self.client.read_packet(serverbound::play::ACKNOWLEDGE_CONFIGURATION)?; .write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?;
self.client
.read_packet(serverbound::play::ACKNOWLEDGE_CONFIGURATION)?;
self.client.set_state(ConnectionState::Configuration)?; self.client.set_state(ConnectionState::Configuration)?;
Ok(()) Ok(())
}, }
_ => Err(ServerError::UnexpectedState) _ => Err(ServerError::UnexpectedState),
} }
} }
@ -58,40 +72,42 @@ impl ProtocolHelper {
match self.state { match self.state {
ConnectionState::Play => { ConnectionState::Play => {
let time = SystemTime::now(); 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)?; self.client.read_packet(serverbound::play::PONG)?;
Ok(SystemTime::now().duration_since(time).unwrap()) Ok(SystemTime::now().duration_since(time).unwrap())
}, }
ConnectionState::Configuration => { ConnectionState::Configuration => {
let time = SystemTime::now(); 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)?; self.client.read_packet(serverbound::configuration::PONG)?;
Ok(SystemTime::now().duration_since(time).unwrap()) Ok(SystemTime::now().duration_since(time).unwrap())
}, }
_ => Err(ServerError::UnexpectedState) _ => Err(ServerError::UnexpectedState),
} }
} }
pub fn disconnect(&self, reason: TextComponent) -> Result<(), ServerError> { pub fn disconnect(&self, reason: TextComponent) -> Result<(), ServerError> {
let packet = match self.state { let packet = match self.state {
ConnectionState::Login => { ConnectionState::Login => {
let text = reason.as_json()?; let text = reason.as_json()?;
Packet::build(0x00, |p| p.write_string(&text))? Packet::build(0x00, |p| p.write_string(&text))?
}, }
ConnectionState::Configuration => { ConnectionState::Configuration => {
let mut packet = Packet::empty(0x02); let mut packet = Packet::empty(0x02);
packet.write_nbt(&reason)?; packet.write_nbt(&reason)?;
packet packet
}, }
ConnectionState::Play => { ConnectionState::Play => {
let mut packet = Packet::empty(0x1C); let mut packet = Packet::empty(0x1C);
packet.write_nbt(&reason)?; packet.write_nbt(&reason)?;
packet packet
}, }
_ => { _ => {
self.client.close(); self.client.close();
return Ok(()) return Ok(());
}, }
}; };
self.client.write_packet(&packet)?; self.client.write_packet(&packet)?;
Ok(()) Ok(())
@ -105,7 +121,9 @@ impl ProtocolHelper {
packet.write_string(id)?; packet.write_string(id)?;
self.client.write_packet(&packet)?; 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()?; packet.read_string()?;
let data = if packet.read_boolean()? { let data = if packet.read_boolean()? {
let n = packet.read_usize_varint()?; let n = packet.read_usize_varint()?;
@ -115,13 +133,18 @@ impl ProtocolHelper {
}; };
Ok(data) Ok(data)
}, }
_ => Err(ServerError::UnexpectedState) _ => Err(ServerError::UnexpectedState),
} }
} }
/// Returns login plugin response - (message_id, payload) /// Returns login plugin response - (message_id, payload)
pub fn send_login_plugin_request(&self, id: i32, channel: &str, data: &[u8]) -> Result<(i32, Option<Vec<u8>>), ServerError> { pub fn send_login_plugin_request(
&self,
id: i32,
channel: &str,
data: &[u8],
) -> Result<(i32, Option<Vec<u8>>), ServerError> {
match self.state { match self.state {
ConnectionState::Login => { ConnectionState::Login => {
let mut packet = Packet::empty(clientbound::login::PLUGIN_REQUEST); let mut packet = Packet::empty(clientbound::login::PLUGIN_REQUEST);
@ -130,7 +153,9 @@ impl ProtocolHelper {
packet.write_bytes(data)?; packet.write_bytes(data)?;
self.client.write_packet(&packet)?; 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 identifier = packet.read_varint()?;
let data = if packet.read_boolean()? { let data = if packet.read_boolean()? {
let mut data = Vec::new(); let mut data = Vec::new();
@ -141,20 +166,22 @@ impl ProtocolHelper {
}; };
Ok((identifier, data)) Ok((identifier, data))
}, }
_ => Err(ServerError::UnexpectedState) _ => Err(ServerError::UnexpectedState),
} }
} }
pub fn send_plugin_message(&self, channel: &str, data: &[u8]) -> Result<(), ServerError> { pub fn send_plugin_message(&self, channel: &str, data: &[u8]) -> Result<(), ServerError> {
let mut packet = match self.state { 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), ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE),
_ => return Err(ServerError::UnexpectedState) _ => return Err(ServerError::UnexpectedState),
}; };
packet.write_string(channel)?; packet.write_string(channel)?;
packet.write_bytes(data)?; packet.write_bytes(data)?;
self.client.write_packet(&packet)?; self.client.write_packet(&packet)?;
Ok(()) Ok(())
} }
} }

View File

@ -1,2 +1,2 @@
pub mod context; pub mod context;
pub mod helper; pub mod helper;

View File

@ -1,160 +1,177 @@
use std::{io::Read, sync::Arc}; 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 rust_mc_proto::{DataReader, DataWriter, Packet};
use crate::trigger_event; use crate::trigger_event;
use super::{id::*, play::handle_play_state, ConnectionState}; use super::{ConnectionState, id::*, play::handle_play_state};
pub fn handle_connection( pub fn handle_connection(
client: Arc<ClientContext>, // Контекст клиента client: Arc<ClientContext>, // Контекст клиента
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
// Чтение рукопожатия // Чтение рукопожатия
// Получение пакетов производится через client.conn(), // Получение пакетов производится через client.conn(),
// ВАЖНО: не помещать сам client.conn() в переменные, // ВАЖНО: не помещать сам client.conn() в переменные,
// он должен сразу убиваться иначе соединение гдето задедлочится // он должен сразу убиваться иначе соединение гдето задедлочится
let mut packet = client.read_packet(serverbound::handshake::HANDSHAKE)?; let mut packet = client.read_packet(serverbound::handshake::HANDSHAKE)?;
let protocol_version = packet.read_varint()?; // Получаем версия протокола, может быть отрицательным если наш клиент дэбил let protocol_version = packet.read_varint()?; // Получаем версия протокола, может быть отрицательным если наш клиент дэбил
let server_address = packet.read_string()?; // Получаем домен/адрес сервера к которому пытается подключиться клиент, например "play.example.com", а не айпи let server_address = packet.read_string()?; // Получаем домен/адрес сервера к которому пытается подключиться клиент, например "play.example.com", а не айпи
let server_port = packet.read_unsigned_short()?; // Все тоже самое что и с адресом сервера и все потому же и за тем же let server_port = packet.read_unsigned_short()?; // Все тоже самое что и с адресом сервера и все потому же и за тем же
let next_state = packet.read_varint()?; // Тип подключения: 1 для получения статуса и пинга, 2 и 3 для обычного подключения 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 { match next_state {
1 => { // Тип подключения - статус 1 => {
client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status // Тип подключения - статус
client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status
loop { loop {
// Чтение запроса // Чтение запроса
let mut packet = client.read_any_packet()?; let mut packet = client.read_any_packet()?;
match packet.id() { match packet.id() {
serverbound::status::REQUEST => { // Запрос статуса serverbound::status::REQUEST => {
let mut packet = Packet::empty(clientbound::status::RESPONSE); // Запрос статуса
let mut packet = Packet::empty(clientbound::status::RESPONSE);
// Дефолтный статус // Дефолтный статус
let mut status = "{ let mut status = "{
\"version\": { \"version\": {
\"name\": \"Error\", \"name\": \"Error\",
\"protocol\": 0 \"protocol\": 0
}, },
\"description\": {\"text\": \"Internal server error\"} \"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)?; client.write_packet(&packet)?;
}, }
serverbound::status::PING_REQUEST => { // Пинг serverbound::status::PING_REQUEST => {
// Раньше мы просто отправляли ему его-же пакет, но сейчас, // Пинг
// С приходом к власти констант айди-пакетов, нам приходится делать такое непотребство // Раньше мы просто отправляли ему его-же пакет, но сейчас,
let timestamp = packet.read_long()?; // С приходом к власти констант айди-пакетов, нам приходится делать такое непотребство
let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE); let timestamp = packet.read_long()?;
packet.write_long(timestamp)?; let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE);
client.write_packet(&packet)?; packet.write_long(timestamp)?;
}, client.write_packet(&packet)?;
_ => { }
return Err(ServerError::UnexpectedPacket); _ => {
} return Err(ServerError::UnexpectedPacket);
} }
} }
}, }
2 => { // Тип подключения - игра }
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login 2 => {
// Тип подключения - игра
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login
// Читаем пакет Login Start // Читаем пакет Login Start
let mut packet = client.read_packet(serverbound::login::START)?; let mut packet = client.read_packet(serverbound::login::START)?;
let name = packet.read_string()?; let name = packet.read_string()?;
let uuid = packet.read_uuid()?; 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 { if client.server.config.server.online_mode {
// TODO: encryption packets // TODO: encryption packets
} }
// Отправляем пакет Set Compression если сжатие указано // Отправляем пакет Set Compression если сжатие указано
if let Some(threshold) = client.server.config.server.compression_threshold { 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.write_packet(&Packet::build(clientbound::login::SET_COMPRESSION, |p| {
client.set_compression(Some(threshold)); // Устанавливаем сжатие на соединении p.write_usize_varint(threshold)
} })?)?;
client.set_compression(Some(threshold)); // Устанавливаем сжатие на соединении
}
// Отправка пакета Login Success // Отправка пакета Login Success
client.write_packet(&Packet::build(clientbound::login::SUCCESS, |p| { client.write_packet(&Packet::build(clientbound::login::SUCCESS, |p| {
p.write_uuid(&uuid)?; p.write_uuid(&uuid)?;
p.write_string(&name)?; p.write_string(&name)?;
p.write_varint(0) 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
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(); let identifier = packet.read_string()?;
packet.get_mut().read_to_end(&mut data).unwrap();
if identifier == "minecraft:brand" { let mut data = Vec::new();
break String::from_utf8_lossy(&data).to_string(); packet.get_mut().read_to_end(&mut data).unwrap();
} else {
trigger_event!(client, plugin_message, &identifier, &data);
}
};
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 mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information
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 { let locale = packet.read_string()?; // for example: en_us
brand, let view_distance = packet.read_signed_byte()?; // client-side render distance in chunks
locale, let chat_mode = packet.read_varint()?; // 0: enabled, 1: commands only, 2: hidden. See Chat#Client chat mode for more information.
view_distance, let chat_colors = packet.read_boolean()?; // this settings does nothing on client but can be used on serverside
chat_mode, let displayed_skin_parts = packet.read_byte()?; // bit mask https://minecraft.wiki/w/Java_Edition_protocol#Client_Information_(configuration)
chat_colors, let main_hand = packet.read_varint()?; // 0 for left and 1 for right
displayed_skin_parts, let enable_text_filtering = packet.read_boolean()?; // filtering text for profanity, always false for offline mode
main_hand, let allow_server_listings = packet.read_boolean()?; // allows showing player in server listings in status
enable_text_filtering, let particle_status = packet.read_varint()?; // 0 for all, 1 for decreased, 2 for minimal
allow_server_listings,
particle_status
});
// 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))?; // TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото
client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
// Дальше работаем с режимом игры client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
handle_play_state(client)?;
},
_ => { // Тип подключения не рукопожатный
return Err(ServerError::UnexpectedPacket);
}
}
Ok(()) // Дальше работаем с режимом игры
} handle_play_state(client)?;
}
_ => {
// Тип подключения не рукопожатный
return Err(ServerError::UnexpectedPacket);
}
}
Ok(())
}

View File

@ -1,8 +1,8 @@
/* /*
Generated with parse_ids.py Generated with parse_ids.py
*/ */
pub mod clientbound { pub mod clientbound {
pub mod status { pub mod status {
@ -172,7 +172,6 @@ pub mod clientbound {
pub const CUSTOM_REPORT_DETAILS: u8 = 0x81; pub const CUSTOM_REPORT_DETAILS: u8 = 0x81;
pub const SERVER_LINKS: u8 = 0x82; pub const SERVER_LINKS: u8 = 0x82;
} }
} }
pub mod serverbound { pub mod serverbound {
@ -270,6 +269,4 @@ pub mod serverbound {
pub const USE_ITEM_ON: u8 = 0x3E; pub const USE_ITEM_ON: u8 = 0x3E;
pub const USE_ITEM: u8 = 0x3F; pub const USE_ITEM: u8 = 0x3F;
} }
} }

View File

@ -1,7 +1,6 @@
pub mod handler;
pub mod id; pub mod id;
pub mod play; pub mod play;
pub mod handler;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ConnectionState { pub enum ConnectionState {
@ -9,6 +8,5 @@ pub enum ConnectionState {
Status, Status,
Login, Login,
Configuration, Configuration,
Play Play,
} }

View File

@ -1,16 +1,19 @@
use std::sync::Arc; 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( pub fn handle_play_state(
client: Arc<ClientContext>, // Контекст клиента client: Arc<ClientContext>, // Контекст клиента
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
// Отключение игрока с сообщением
client.protocol_helper().disconnect(TextComponent::rainbow(
"server is in developement suka".to_string(),
))?;
// Отключение игрока с сообщением // TODO: Сделать отправку пакетов Play
client.protocol_helper().disconnect(TextComponent::rainbow("server is in developement suka".to_string()))?;
// TODO: Сделать отправку пакетов Play Ok(())
}
Ok(())
}