merge commit

This commit is contained in:
MeexReay 2025-05-05 04:32:52 +03:00
commit 94bf69d420
15 changed files with 1537 additions and 1490 deletions

16
.vscode/settings.json vendored
View File

@ -1,5 +1,19 @@
{ {
"editor.fontFamily": "Fira Code", "editor.fontFamily": "Fira Code",
"editor.fontLigatures": true, "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"
]
} }

3
rustfmt.toml Normal file
View File

@ -0,0 +1,3 @@
# Пример настроек
tab_spaces = 2
hard_tabs = true

View File

@ -3,14 +3,14 @@ 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::{
ServerError, ServerError,
config::Config, config::Config,
context::ServerContext, context::ServerContext,
data::text_component::TextComponent, data::text_component::TextComponent,
event::{Listener, PacketHandler}, event::{Listener, PacketHandler},
player::context::ClientContext, player::context::ClientContext,
protocol::ConnectionState, protocol::ConnectionState,
start_server, start_server,
}; };
pub mod server; pub mod server;
@ -18,13 +18,13 @@ pub mod server;
struct ExampleListener; struct ExampleListener;
impl Listener for ExampleListener { impl Listener for ExampleListener {
fn on_status( fn on_status(
&self, &self,
client: Arc<ClientContext>, client: Arc<ClientContext>,
response: &mut String, response: &mut String,
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
*response = format!( *response = format!(
"{{ "{{
\"version\": {{ \"version\": {{
\"name\": \"idk\", \"name\": \"idk\",
\"protocol\": {} \"protocol\": {}
@ -43,124 +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_address,
client.handshake().unwrap().server_port client.handshake().unwrap().server_port
)) ))
.underlined(true) .underlined(true)
.build() .build()
]) ])
.build() .build()
]) ])
.build() .build()
.as_json()? .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!( debug!(
"{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", "{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes",
client.addr.clone(), client.addr.clone(),
packet.id(), packet.id(),
state, state,
packet.len() 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!( debug!(
"{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", "{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes",
client.addr.clone(), client.addr.clone(),
packet.id(), packet.id(),
state, state,
packet.len() 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

@ -5,48 +5,48 @@ 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")] #[serde(default = "default_host")]
pub host: String, pub host: String,
#[serde(default = "default_timeout")] #[serde(default = "default_timeout")]
pub timeout: u64, pub timeout: u64,
} }
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)] #[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
pub struct ServerConfig { pub struct ServerConfig {
#[serde(default)] #[serde(default)]
pub online_mode: bool, pub online_mode: bool,
#[serde(default = "default_compression")] #[serde(default = "default_compression")]
pub compression_threshold: Option<usize>, 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)] #[serde(default)]
pub bind: BindConfig, pub bind: BindConfig,
#[serde(default)] #[serde(default)]
pub server: ServerConfig, pub server: ServerConfig,
} }
fn default_host() -> String { fn default_host() -> String {
"127.0.0.1:25565".to_string() "127.0.0.1:25565".to_string()
} }
fn default_timeout() -> u64 { fn default_timeout() -> u64 {
5 5
} }
fn default_compression() -> Option<usize> { fn default_compression() -> Option<usize> {
Some(256) Some(256)
} }
impl Config { impl Config {
pub fn load_from_file(path: PathBuf) -> Option<Config> { pub fn load_from_file(path: PathBuf) -> Option<Config> {
if !fs::exists(&path).unwrap_or_default() { if !fs::exists(&path).unwrap_or_default() {
let table = Config::default(); let table = Config::default();
fs::create_dir_all(&path.parent()?).ok()?; fs::create_dir_all(&path.parent()?).ok()?;
fs::write(&path, toml::to_string_pretty(&table).ok()?).ok()?; fs::write(&path, toml::to_string_pretty(&table).ok()?).ok()?;
return Some(table); return Some(table);
} }
let content = fs::read_to_string(&path).ok()?; let content = fs::read_to_string(&path).ok()?;
let table = toml::from_str::<Config>(&content).ok()?; let table = toml::from_str::<Config>(&content).ok()?;
Some(table) Some(table)
} }
} }

View File

@ -5,87 +5,90 @@ use itertools::Itertools;
use uuid::Uuid; use uuid::Uuid;
use super::{ use super::{
config::Config, config::Config,
event::{Listener, PacketHandler}, event::{Listener, PacketHandler},
player::context::ClientContext, player::context::ClientContext,
}; };
// Контекст сервера // Контекст сервера
// Должен быть обернут в Arc для передачи между потоками // Должен быть обернут в Arc для передачи между потоками
pub struct ServerContext { 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 {
pub fn new(config: Arc<Config>) -> ServerContext { pub fn new(config: Arc<Config>) -> ServerContext {
ServerContext { 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 self
.iter() .clients
.find(|o| { .iter()
let info = o.player_info(); .find(|o| {
if let Some(info) = info { let info = o.player_info();
info.uuid == uuid if let Some(info) = info {
} else { info.uuid == uuid
false } else {
} false
}) }
.map(|o| o.clone()) })
} .map(|o| o.clone())
}
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 self
.iter() .clients
.find(|o| { .iter()
let info = o.player_info(); .find(|o| {
if let Some(info) = info { let info = o.player_info();
info.name == name if let Some(info) = info {
} else { info.name == name
false } else {
} false
}) }
.map(|o| o.clone()) })
} .map(|o| o.clone())
}
pub fn players(self: &Arc<Self>) -> Vec<Arc<ClientContext>> { pub fn players(self: &Arc<Self>) -> Vec<Arc<ClientContext>> {
self.clients self
.iter() .clients
.filter(|o| o.player_info().is_some()) .iter()
.map(|o| o.clone()) .filter(|o| o.player_info().is_some())
.collect() .map(|o| o.clone())
} .collect()
}
pub fn add_packet_handler(&mut self, handler: Box<dyn PacketHandler>) { pub fn add_packet_handler(&mut self, handler: Box<dyn PacketHandler>) {
self.handlers.push(handler); self.handlers.push(handler);
} }
pub fn add_listener(&mut self, listener: Box<dyn Listener>) { pub fn add_listener(&mut self, listener: Box<dyn Listener>) {
self.listeners.push(listener); self.listeners.push(listener);
} }
pub fn packet_handlers<F, K>(self: &Arc<Self>, sort_by: F) -> Vec<&Box<dyn PacketHandler>> pub fn packet_handlers<F, K>(self: &Arc<Self>, sort_by: F) -> Vec<&Box<dyn PacketHandler>>
where 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>(self: &Arc<Self>, sort_by: F) -> Vec<&Box<dyn Listener>> pub fn listeners<F, K>(self: &Arc<Self>, sort_by: F) -> Vec<&Box<dyn Listener>>
where 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

@ -9,42 +9,43 @@ pub mod text_component;
// Трейт для чтения NBT-совместимых приколов // Трейт для чтения NBT-совместимых приколов
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>;
} }
impl ReadWriteNBT<DynNBT> for Packet { impl ReadWriteNBT<DynNBT> for Packet {
fn read_nbt(&mut self) -> Result<DynNBT, ServerError> { fn read_nbt(&mut self) -> Result<DynNBT, 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() self
.read_to_end(&mut data) .get_mut()
.map_err(|_| ServerError::DeNbt)?; .read_to_end(&mut data)
let (remaining, value) = .map_err(|_| ServerError::DeNbt)?;
craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeNbt)?; let (remaining, value) = craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeNbt)?;
self.get_mut() self
.set_position(pos + (data.len() - remaining.len()) as u64); .get_mut()
Ok(value) .set_position(pos + (data.len() - remaining.len()) as u64);
} Ok(value)
}
fn write_nbt(&mut self, val: &DynNBT) -> Result<(), ServerError> { fn write_nbt(&mut self, val: &DynNBT) -> Result<(), ServerError> {
craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerNbt)?; craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerNbt)?;
Ok(()) Ok(())
} }
} }
pub trait ReadWritePosition: DataReader + DataWriter { pub trait ReadWritePosition: DataReader + DataWriter {
fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError>; fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError>;
fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError>; fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError>;
} }
impl ReadWritePosition for Packet { impl ReadWritePosition for Packet {
fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError> { fn read_position(&mut self) -> Result<(i64, i64, i64), ServerError> {
let val = self.read_long()?; let val = self.read_long()?;
Ok((val >> 38, val << 52 >> 52, val << 26 >> 38)) Ok((val >> 38, val << 52 >> 52, val << 26 >> 38))
} }
fn write_position(&mut self, x: i64, y: i64, z: i64) -> Result<(), ServerError> { 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))?) Ok(self.write_long(((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF))?)
} }
} }

View File

@ -12,171 +12,173 @@ 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 {
pub text: String, pub text: String,
pub color: Option<String>, pub color: Option<String>,
pub bold: Option<bool>, pub bold: Option<bool>,
pub italic: Option<bool>, pub italic: Option<bool>,
pub underlined: Option<bool>, pub underlined: Option<bool>,
pub strikethrough: Option<bool>, pub strikethrough: Option<bool>,
pub obfuscated: Option<bool>, pub obfuscated: Option<bool>,
pub extra: Option<Vec<TextComponent>>, pub extra: Option<Vec<TextComponent>>,
// TODO: добавить все остальные стандартные поля для текст-компонента типа клик ивентов и сделать отдельный структ для транслейт компонент // TODO: добавить все остальные стандартные поля для текст-компонента типа клик ивентов и сделать отдельный структ для транслейт компонент
} }
impl TextComponent { impl TextComponent {
pub fn new(text: String) -> Self { pub fn new(text: String) -> Self {
Self { Self {
text, text,
color: None, color: None,
bold: None, bold: None,
italic: None, italic: None,
underlined: None, underlined: None,
strikethrough: None, strikethrough: None,
obfuscated: None, obfuscated: None,
extra: None, extra: None,
} }
} }
pub fn rainbow(text: String) -> TextComponent { pub fn rainbow(text: String) -> TextComponent {
if text.is_empty() { if text.is_empty() {
return TextComponent::new(text); return TextComponent::new(text);
} }
let children = text let children = text
.char_indices() .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);
let rgb: Srgb = hsl.into_color(); let rgb: Srgb = hsl.into_color();
let r = (rgb.red * 255.0).round() as u8; let r = (rgb.red * 255.0).round() as u8;
let g = (rgb.green * 255.0).round() as u8; let g = (rgb.green * 255.0).round() as u8;
let b = (rgb.blue * 255.0).round() as u8; let b = (rgb.blue * 255.0).round() as u8;
let mut component = TextComponent::new(c.to_string()); let mut component = TextComponent::new(c.to_string());
component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b)); component.color = Some(format!("#{:02X}{:02X}{:02X}", r, g, b));
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
} }
pub fn builder() -> TextComponentBuilder { pub fn builder() -> TextComponentBuilder {
TextComponentBuilder::new() TextComponentBuilder::new()
} }
pub fn as_json(self) -> Result<String, ServerError> { pub fn as_json(self) -> Result<String, ServerError> {
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<TextComponent, ServerError> { pub fn from_json(text: &str) -> Result<TextComponent, ServerError> {
serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent) serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent)
} }
} }
impl Default for TextComponent { impl Default for TextComponent {
fn default() -> Self { fn default() -> Self {
Self::new(String::new()) Self::new(String::new())
} }
} }
pub struct TextComponentBuilder { pub struct TextComponentBuilder {
text: String, text: String,
color: Option<String>, color: Option<String>,
bold: Option<bool>, bold: Option<bool>,
italic: Option<bool>, italic: Option<bool>,
underlined: Option<bool>, underlined: Option<bool>,
strikethrough: Option<bool>, strikethrough: Option<bool>,
obfuscated: Option<bool>, obfuscated: Option<bool>,
extra: Option<Vec<TextComponent>>, extra: Option<Vec<TextComponent>>,
} }
impl TextComponentBuilder { impl TextComponentBuilder {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
text: String::new(), text: String::new(),
color: None, color: None,
bold: None, bold: None,
italic: None, italic: None,
underlined: None, underlined: None,
strikethrough: None, strikethrough: None,
obfuscated: None, obfuscated: None,
extra: None, extra: None,
} }
} }
pub fn text(mut self, text: &str) -> Self { pub fn text(mut self, text: &str) -> Self {
self.text = text.to_string(); self.text = text.to_string();
self self
} }
pub fn color(mut self, color: &str) -> Self { pub fn color(mut self, color: &str) -> Self {
self.color = Some(color.to_string()); self.color = Some(color.to_string());
self self
} }
pub fn bold(mut self, bold: bool) -> Self { pub fn bold(mut self, bold: bool) -> Self {
self.bold = Some(bold); self.bold = Some(bold);
self self
} }
pub fn italic(mut self, italic: bool) -> Self { pub fn italic(mut self, italic: bool) -> Self {
self.italic = Some(italic); self.italic = Some(italic);
self self
} }
pub fn underlined(mut self, underlined: bool) -> Self { pub fn underlined(mut self, underlined: bool) -> Self {
self.underlined = Some(underlined); self.underlined = Some(underlined);
self self
} }
pub fn strikethrough(mut self, strikethrough: bool) -> Self { pub fn strikethrough(mut self, strikethrough: bool) -> Self {
self.strikethrough = Some(strikethrough); self.strikethrough = Some(strikethrough);
self self
} }
pub fn obfuscated(mut self, obfuscated: bool) -> Self { pub fn obfuscated(mut self, obfuscated: bool) -> Self {
self.obfuscated = Some(obfuscated); self.obfuscated = Some(obfuscated);
self self
} }
pub fn extra(mut self, extra: Vec<TextComponent>) -> Self { pub fn extra(mut self, extra: Vec<TextComponent>) -> Self {
self.extra = Some(extra); self.extra = Some(extra);
self self
} }
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,
} }
} }
} }
// Реализуем читалку-записывалку текст-компонентов для пакета // Реализуем читалку-записывалку текст-компонентов для пакета
impl ReadWriteNBT<TextComponent> for Packet { 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() self
.read_to_end(&mut data) .get_mut()
.map_err(|_| ServerError::DeTextComponent)?; .read_to_end(&mut data)
let (remaining, value) = .map_err(|_| ServerError::DeTextComponent)?;
craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?; let (remaining, value) =
self.get_mut() craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?;
.set_position(pos + (data.len() - remaining.len()) as u64); self
Ok(value) .get_mut()
} .set_position(pos + (data.len() - remaining.len()) as u64);
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).map_err(|_| ServerError::SerTextComponent)?; craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?;
Ok(()) Ok(())
} }
} }

View File

@ -37,12 +37,12 @@ macro_rules! trigger_event {
} }
pub trait Listener: Sync + Send { pub trait Listener: Sync + Send {
generate_handlers!(status, &mut String); generate_handlers!(status, &mut String);
generate_handlers!(plugin_message, &str, &[u8]); generate_handlers!(plugin_message, &str, &[u8]);
} }
pub trait PacketHandler: Sync + Send { 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

@ -17,89 +17,89 @@ pub mod protocol;
// Ошибки сервера // Ошибки сервера
#[derive(Debug)] #[derive(Debug)]
pub enum ServerError { pub enum ServerError {
UnexpectedPacket(u8), // Неожиданный пакет UnexpectedPacket(u8), // Неожиданный пакет
Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto
ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection
SerTextComponent, // Ошибка при сериализации текст-компонента SerTextComponent, // Ошибка при сериализации текст-компонента
DeTextComponent, // Ошибка при десериализации текст-компонента DeTextComponent, // Ошибка при десериализации текст-компонента
SerNbt, // Ошибка при сериализации nbt SerNbt, // Ошибка при сериализации nbt
DeNbt, // Ошибка при десериализации nbt DeNbt, // Ошибка при десериализации nbt
UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper) UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper)
Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки
} }
impl Display for ServerError { impl Display for ServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("{:?}", self)) f.write_str(&format!("{:?}", self))
} }
} }
impl Error for ServerError {} impl Error for ServerError {}
// Делаем чтобы ProtocolError мог переделываться в наш ServerError // Делаем чтобы ProtocolError мог переделываться в наш ServerError
impl From<ProtocolError> for ServerError { impl From<ProtocolError> for ServerError {
fn from(error: ProtocolError) -> ServerError { fn from(error: ProtocolError) -> ServerError {
match error { match error {
// Если просто закрыто соединение, переделываем в нашу ошибку этого // Если просто закрыто соединение, переделываем в нашу ошибку этого
ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed, ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed,
// Все остальное просто засовываем в обертку // Все остальное просто засовываем в обертку
error => ServerError::Protocol(error), 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!( error!(
"Не удалось забиндить сервер на {}", "Не удалось забиндить сервер на {}",
&server.config.bind.host &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 stream
.set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout))) .set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout)))
.ignore(); .ignore();
stream stream
.set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout))) .set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout)))
.ignore(); .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 будет нам врать // Используем адрес как ключ, врятли ipv4 будет нам врать
server.clients.insert(client.addr, client.clone()); server.clients.insert(client.addr, client.clone());
// Обработка подключения // Обработка подключения
// Если ошибка -> выводим // Если ошибка -> выводим
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:?}");
} }
}; };
// Удаляем клиента из списка клиентов // Удаляем клиента из списка клиентов
server.clients.remove(&client.addr); server.clients.remove(&client.addr);
info!("Отключение: {}", addr); info!("Отключение: {}", addr);
}); });
} }
} }

View File

@ -1,7 +1,13 @@
use std::{ use std::{
collections::VecDeque, hash::Hash, net::{SocketAddr, TcpStream}, sync::{ collections::VecDeque,
atomic::{AtomicBool, Ordering}, Arc, Mutex, RwLock hash::Hash,
}, thread, time::Duration net::{SocketAddr, TcpStream},
sync::{
Arc, Mutex, RwLock,
atomic::{AtomicBool, Ordering},
},
thread,
time::Duration,
}; };
use rust_mc_proto::{MinecraftConnection, Packet}; use rust_mc_proto::{MinecraftConnection, Packet};
@ -14,305 +20,288 @@ use super::helper::ProtocolHelper;
// Клиент контекст // Клиент контекст
// Должен быть обернут в Arc для передачи между потоками // Должен быть обернут в Arc для передачи между потоками
pub struct ClientContext { pub struct ClientContext {
pub server: Arc<ServerContext>, pub server: Arc<ServerContext>,
pub addr: SocketAddr, pub addr: SocketAddr,
conn: RwLock<MinecraftConnection<TcpStream>>, conn: RwLock<MinecraftConnection<TcpStream>>,
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>,
packet_buffer: Mutex<VecDeque<Packet>>, packet_buffer: Mutex<VecDeque<Packet>>,
read_loop: AtomicBool, read_loop: AtomicBool,
is_alive: AtomicBool, is_alive: AtomicBool,
position: RwLock<(f64, f64, f64)>, position: RwLock<(f64, f64, f64)>,
velocity: RwLock<(f64, f64, f64)>, velocity: RwLock<(f64, f64, f64)>,
rotation: RwLock<(f32, f32)>, rotation: RwLock<(f32, f32)>,
} }
// Реализуем сравнение через адрес // Реализуем сравнение через адрес
// IPv4 не должен обманывать, иначе у нас случится коллапс // IPv4 не должен обманывать, иначе у нас случится коллапс
impl PartialEq for ClientContext { impl PartialEq for ClientContext {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.addr == other.addr self.addr == other.addr
} }
} }
impl Hash for ClientContext { impl Hash for ClientContext {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.addr.hash(state); self.addr.hash(state);
} }
} }
impl Eq for ClientContext {} impl Eq for ClientContext {}
impl ClientContext { impl ClientContext {
pub fn new(server: Arc<ServerContext>, conn: MinecraftConnection<TcpStream>) -> ClientContext { pub fn new(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(),
conn: RwLock::new(conn), conn: RwLock::new(conn),
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),
packet_buffer: Mutex::new(VecDeque::new()), packet_buffer: Mutex::new(VecDeque::new()),
read_loop: AtomicBool::new(false), read_loop: AtomicBool::new(false),
is_alive: AtomicBool::new(true), is_alive: AtomicBool::new(true),
position: RwLock::new((0.0, 0.0, 0.0)), position: RwLock::new((0.0, 0.0, 0.0)),
velocity: RwLock::new((0.0, 0.0, 0.0)), velocity: RwLock::new((0.0, 0.0, 0.0)),
rotation: RwLock::new((0.0, 0.0)) rotation: RwLock::new((0.0, 0.0)),
} }
} }
pub fn set_handshake(self: &Arc<Self>, handshake: Handshake) { pub fn set_handshake(self: &Arc<Self>, handshake: Handshake) {
*self.handshake.write().unwrap() = Some(handshake); *self.handshake.write().unwrap() = Some(handshake);
} }
pub fn set_client_info(self: &Arc<Self>, client_info: ClientInfo) { pub fn set_client_info(self: &Arc<Self>, client_info: ClientInfo) {
*self.client_info.write().unwrap() = Some(client_info); *self.client_info.write().unwrap() = Some(client_info);
} }
pub fn set_player_info(self: &Arc<Self>, player_info: PlayerInfo) { pub fn set_player_info(self: &Arc<Self>, player_info: PlayerInfo) {
*self.player_info.write().unwrap() = Some(player_info); *self.player_info.write().unwrap() = Some(player_info);
} }
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 for handler in self
.server .server
.packet_handlers(|o| o.on_state_priority()) .packet_handlers(|o| o.on_state_priority())
.iter() .iter()
{ {
handler.on_state(self.clone(), state.clone())?; handler.on_state(self.clone(), state.clone())?;
} }
Ok(()) Ok(())
} }
pub fn handshake(self: &Arc<Self>) -> Option<Handshake> { pub fn handshake(self: &Arc<Self>) -> Option<Handshake> {
self.handshake.read().unwrap().clone() self.handshake.read().unwrap().clone()
} }
pub fn client_info(self: &Arc<Self>) -> Option<ClientInfo> { pub fn client_info(self: &Arc<Self>) -> Option<ClientInfo> {
self.client_info.read().unwrap().clone() self.client_info.read().unwrap().clone()
} }
pub fn player_info(self: &Arc<Self>) -> Option<PlayerInfo> { pub fn player_info(self: &Arc<Self>) -> Option<PlayerInfo> {
self.player_info.read().unwrap().clone() self.player_info.read().unwrap().clone()
} }
pub fn state(self: &Arc<Self>) -> ConnectionState { pub fn state(self: &Arc<Self>) -> ConnectionState {
self.state.read().unwrap().clone() self.state.read().unwrap().clone()
} }
pub fn set_position(self: &Arc<Self>, position: (f64, f64, f64)) { pub fn set_position(self: &Arc<Self>, position: (f64, f64, f64)) {
*self.position.write().unwrap() = position; *self.position.write().unwrap() = position;
} }
pub fn set_velocity(self: &Arc<Self>, velocity: (f64, f64, f64)) { pub fn set_velocity(self: &Arc<Self>, velocity: (f64, f64, f64)) {
*self.velocity.write().unwrap() = velocity; *self.velocity.write().unwrap() = velocity;
} }
pub fn set_rotation(self: &Arc<Self>, rotation: (f32, f32)) { pub fn set_rotation(self: &Arc<Self>, rotation: (f32, f32)) {
*self.rotation.write().unwrap() = rotation; *self.rotation.write().unwrap() = rotation;
} }
pub fn position(self: &Arc<Self>) -> (f64, f64, f64) { pub fn position(self: &Arc<Self>) -> (f64, f64, f64) {
self.position.read().unwrap().clone() self.position.read().unwrap().clone()
} }
pub fn velocity(self: &Arc<Self>) -> (f64, f64, f64) { pub fn velocity(self: &Arc<Self>) -> (f64, f64, f64) {
self.velocity.read().unwrap().clone() self.velocity.read().unwrap().clone()
} }
pub fn rotation(self: &Arc<Self>) -> (f32, f32) { pub fn rotation(self: &Arc<Self>) -> (f32, f32) {
self.rotation.read().unwrap().clone() self.rotation.read().unwrap().clone()
} }
pub fn write_packet(self: &Arc<Self>, packet: &Packet) -> Result<(), ServerError> { pub fn write_packet(self: &Arc<Self>, packet: &Packet) -> Result<(), ServerError> {
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 for handler in self
.server .server
.packet_handlers(|o| o.on_outcoming_packet_priority()) .packet_handlers(|o| o.on_outcoming_packet_priority())
.iter() .iter()
{ {
handler.on_outcoming_packet( handler.on_outcoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?;
self.clone(), packet.get_mut().set_position(0);
&mut packet, }
&mut cancelled, if !cancelled {
state.clone(), match self.conn.write().unwrap().write_packet(&packet) {
)?; Ok(_) => {}
packet.get_mut().set_position(0); Err(e) => {
} self.is_alive.store(false, Ordering::SeqCst);
if !cancelled { return Err(e.into());
match self.conn.write().unwrap().write_packet(&packet) { }
Ok(_) => {}, };
Err(e) => { }
self.is_alive.store(false, Ordering::SeqCst); Ok(())
return Err(e.into()); }
}
};
}
Ok(())
}
pub fn run_read_loop( pub fn run_read_loop(self: &Arc<Self>) -> Result<(), ServerError> {
self: &Arc<Self> self.read_loop.store(true, Ordering::SeqCst);
) -> 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() { while self.is_alive() {
let mut packet = match conn.read_packet() { let mut packet = match conn.read_packet() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
self.is_alive.store(false, Ordering::SeqCst); self.is_alive.store(false, Ordering::SeqCst);
return Err(e.into()); return Err(e.into());
} }
}; };
let mut cancelled = false; let mut cancelled = false;
let state = self.state(); let state = self.state();
for handler in self for handler in self
.server .server
.packet_handlers(|o| o.on_incoming_packet_priority()) .packet_handlers(|o| o.on_incoming_packet_priority())
.iter() .iter()
{ {
handler.on_incoming_packet( handler.on_incoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?;
self.clone(), packet.get_mut().set_position(0);
&mut packet, }
&mut cancelled, if !cancelled {
state.clone(), self.packet_buffer.lock().unwrap().push_back(packet);
)?; }
packet.get_mut().set_position(0); }
}
if !cancelled {
self.packet_buffer.lock().unwrap().push_back(packet);
}
}
Ok(()) Ok(())
} }
/// Please avoid using of this bullshit /// Please avoid using of this bullshit
pub fn read_any_packet(self: &Arc<Self>) -> Result<Packet, ServerError> { pub fn read_any_packet(self: &Arc<Self>) -> Result<Packet, ServerError> {
if self.read_loop.load(Ordering::SeqCst) { if self.read_loop.load(Ordering::SeqCst) {
loop { loop {
if let Some(packet) = self.packet_buffer.lock().unwrap().pop_front() { if let Some(packet) = self.packet_buffer.lock().unwrap().pop_front() {
return Ok(packet); return Ok(packet);
} }
thread::sleep(Duration::from_millis(4)); thread::sleep(Duration::from_millis(4));
} }
} else { } else {
let state = self.state(); let state = self.state();
loop { loop {
let mut packet = match self.conn.write().unwrap().read_packet() { let mut packet = match self.conn.write().unwrap().read_packet() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
self.is_alive.store(false, Ordering::SeqCst); self.is_alive.store(false, Ordering::SeqCst);
return Err(e.into()); return Err(e.into());
} }
}; };
let mut cancelled = false; let mut cancelled = false;
for handler in self for handler in self
.server .server
.packet_handlers(|o| o.on_incoming_packet_priority()) .packet_handlers(|o| o.on_incoming_packet_priority())
.iter() .iter()
{ {
handler.on_incoming_packet( handler.on_incoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?;
self.clone(), packet.get_mut().set_position(0);
&mut packet, }
&mut cancelled, if !cancelled {
state.clone(), break Ok(packet);
)?; }
packet.get_mut().set_position(0); }
} }
if !cancelled { }
break Ok(packet);
}
}
}
}
pub fn read_packet(self: &Arc<Self>, ids: &[u8]) -> Result<Packet, ServerError> { pub fn read_packet(self: &Arc<Self>, ids: &[u8]) -> Result<Packet, ServerError> {
if self.read_loop.load(Ordering::SeqCst) { if self.read_loop.load(Ordering::SeqCst) {
loop { loop {
{ {
let mut locked = self.packet_buffer.lock().unwrap(); let mut locked = self.packet_buffer.lock().unwrap();
for (i, packet) in locked.clone().iter().enumerate() { for (i, packet) in locked.clone().iter().enumerate() {
if ids.contains(&packet.id()) { if ids.contains(&packet.id()) {
locked.remove(i); locked.remove(i);
return Ok(packet.clone()); return Ok(packet.clone());
} }
} }
} }
thread::sleep(Duration::from_millis(4)); thread::sleep(Duration::from_millis(4));
} }
} else { } else {
let packet = match self.read_any_packet() { let packet = match self.read_any_packet() {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
self.is_alive.store(false, Ordering::SeqCst); self.is_alive.store(false, Ordering::SeqCst);
return Err(e); return Err(e);
} }
}; };
if ids.contains(&packet.id()) { if ids.contains(&packet.id()) {
Err(ServerError::UnexpectedPacket(packet.id())) Err(ServerError::UnexpectedPacket(packet.id()))
} else { } else {
Ok(packet) Ok(packet)
} }
} }
} }
pub fn push_packet_back(self: &Arc<Self>, packet: Packet){ pub fn push_packet_back(self: &Arc<Self>, packet: Packet) {
self.packet_buffer.lock().unwrap().push_back(packet) self.packet_buffer.lock().unwrap().push_back(packet)
} }
pub fn close(self: &Arc<Self>) { pub fn close(self: &Arc<Self>) {
self.conn.write().unwrap().close(); self.conn.write().unwrap().close();
} }
pub fn set_compression(self: &Arc<Self>, threshold: Option<usize>) { pub fn set_compression(self: &Arc<Self>, threshold: Option<usize>) {
self.conn.write().unwrap().set_compression(threshold); self.conn.write().unwrap().set_compression(threshold);
} }
pub fn is_alive(self: &Arc<Self>) -> bool { pub fn is_alive(self: &Arc<Self>) -> bool {
self.is_alive.load(Ordering::SeqCst) self.is_alive.load(Ordering::SeqCst)
} }
pub fn protocol_helper(self: &Arc<Self>) -> ProtocolHelper { pub fn protocol_helper(self: &Arc<Self>) -> ProtocolHelper {
ProtocolHelper::new(self.clone()) ProtocolHelper::new(self.clone())
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Handshake { pub struct Handshake {
pub protocol_version: i32, pub protocol_version: i32,
pub server_address: String, pub server_address: String,
pub server_port: u16, pub server_port: u16,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct ClientInfo { pub struct ClientInfo {
pub brand: String, pub brand: String,
pub locale: String, pub locale: String,
pub view_distance: i8, pub view_distance: i8,
pub chat_mode: i32, pub chat_mode: i32,
pub chat_colors: bool, pub chat_colors: bool,
pub displayed_skin_parts: u8, pub displayed_skin_parts: u8,
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,18 +1,18 @@
use std::{ use std::{
io::Read, io::Read,
sync::Arc, sync::Arc,
time::{Duration, SystemTime}, time::{Duration, SystemTime},
}; };
use rust_mc_proto::{DataReader, DataWriter, Packet}; use rust_mc_proto::{DataReader, DataWriter, Packet};
use crate::server::{ use crate::server::{
ServerError, ServerError,
data::{ReadWriteNBT, text_component::TextComponent}, data::{ReadWriteNBT, text_component::TextComponent},
protocol::{ protocol::{
id::{clientbound, serverbound}, id::{clientbound, serverbound},
*, *,
}, },
}; };
use super::context::ClientContext; use super::context::ClientContext;
@ -25,207 +25,214 @@ 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,
} }
} }
pub fn reset_chat(&self) -> Result<(), ServerError> { pub fn reset_chat(&self) -> Result<(), ServerError> {
match self.state { match self.state {
ConnectionState::Configuration => { ConnectionState::Configuration => {
self.client self
.write_packet(&Packet::empty(clientbound::configuration::RESET_CHAT))?; .client
Ok(()) .write_packet(&Packet::empty(clientbound::configuration::RESET_CHAT))?;
} Ok(())
_ => Err(ServerError::UnexpectedState), }
} _ => Err(ServerError::UnexpectedState),
} }
}
pub fn store_cookie(&self, id: &str, data: &[u8]) -> Result<(), ServerError> { pub fn store_cookie(&self, id: &str, data: &[u8]) -> Result<(), ServerError> {
self.client.write_packet(&Packet::build( self.client.write_packet(&Packet::build(
match self.state { match self.state {
ConnectionState::Configuration => clientbound::configuration::STORE_COOKIE, ConnectionState::Configuration => clientbound::configuration::STORE_COOKIE,
ConnectionState::Play => clientbound::play::STORE_COOKIE, ConnectionState::Play => clientbound::play::STORE_COOKIE,
_ => return Err(ServerError::UnexpectedState), _ => return Err(ServerError::UnexpectedState),
}, },
|p| { |p| {
p.write_string(id)?; p.write_string(id)?;
p.write_bytes(data) p.write_bytes(data)
}, },
)?)?; )?)?;
Ok(()) Ok(())
} }
/// Leave from Configuration to Play state /// Leave from Configuration to Play state
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 self
.write_packet(&Packet::empty(clientbound::configuration::FINISH))?; .client
self.client .write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
.read_packet(&[serverbound::configuration::ACKNOWLEDGE_FINISH])?; self
self.client.set_state(ConnectionState::Play)?; .client
Ok(()) .read_packet(&[serverbound::configuration::ACKNOWLEDGE_FINISH])?;
} self.client.set_state(ConnectionState::Play)?;
_ => Err(ServerError::UnexpectedState), Ok(())
} }
} _ => Err(ServerError::UnexpectedState),
}
}
/// Enter to Configuration from Play state /// Enter to Configuration from Play state
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 self
.write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?; .client
self.client .write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?;
.read_packet(&[serverbound::play::ACKNOWLEDGE_CONFIGURATION])?; self
self.client.set_state(ConnectionState::Configuration)?; .client
Ok(()) .read_packet(&[serverbound::play::ACKNOWLEDGE_CONFIGURATION])?;
} self.client.set_state(ConnectionState::Configuration)?;
_ => Err(ServerError::UnexpectedState), Ok(())
} }
} _ => Err(ServerError::UnexpectedState),
}
}
/// Enter to Configuration from Play state /// Enter to Configuration from Play state
pub fn ping(&self) -> Result<Duration, ServerError> { pub fn ping(&self) -> Result<Duration, ServerError> {
match self.state { match self.state {
ConnectionState::Play => { ConnectionState::Play => {
let time = SystemTime::now(); let time = SystemTime::now();
self.client self
.write_packet(&Packet::empty(clientbound::play::PING))?; .client
self.client.read_packet(&[serverbound::play::PONG])?; .write_packet(&Packet::empty(clientbound::play::PING))?;
Ok(SystemTime::now().duration_since(time).unwrap()) self.client.read_packet(&[serverbound::play::PONG])?;
} Ok(SystemTime::now().duration_since(time).unwrap())
ConnectionState::Configuration => { }
let time = SystemTime::now(); ConnectionState::Configuration => {
self.client let time = SystemTime::now();
.write_packet(&Packet::empty(clientbound::configuration::PING))?; self
self.client.read_packet(&[serverbound::configuration::PONG])?; .client
Ok(SystemTime::now().duration_since(time).unwrap()) .write_packet(&Packet::empty(clientbound::configuration::PING))?;
} self
_ => Err(ServerError::UnexpectedState), .client
} .read_packet(&[serverbound::configuration::PONG])?;
} Ok(SystemTime::now().duration_since(time).unwrap())
}
_ => 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(())
} }
/// Returns cookie content /// Returns cookie content
pub fn request_cookie(&self, id: &str) -> Result<Option<Vec<u8>>, ServerError> { pub fn request_cookie(&self, id: &str) -> Result<Option<Vec<u8>>, ServerError> {
match self.state { match self.state {
ConnectionState::Configuration => { ConnectionState::Configuration => {
let mut packet = Packet::empty(clientbound::configuration::COOKIE_REQUEST); let mut packet = Packet::empty(clientbound::configuration::COOKIE_REQUEST);
packet.write_string(id)?; packet.write_string(id)?;
self.client.write_packet(&packet)?; self.client.write_packet(&packet)?;
let mut packet = self let mut packet = self
.client .client
.read_packet(&[serverbound::configuration::COOKIE_RESPONSE])?; .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()?;
Some(packet.read_bytes(n)?) Some(packet.read_bytes(n)?)
} else { } else {
None None
}; };
Ok(data) Ok(data)
} }
ConnectionState::Play => { ConnectionState::Play => {
let mut packet = Packet::empty(clientbound::play::COOKIE_REQUEST); let mut packet = Packet::empty(clientbound::play::COOKIE_REQUEST);
packet.write_string(id)?; packet.write_string(id)?;
self.client.write_packet(&packet)?; self.client.write_packet(&packet)?;
let mut packet = self let mut packet = self
.client .client
.read_packet(&[serverbound::play::COOKIE_RESPONSE])?; .read_packet(&[serverbound::play::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()?;
Some(packet.read_bytes(n)?) Some(packet.read_bytes(n)?)
} else { } else {
None None
}; };
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( pub fn send_login_plugin_request(
&self, &self,
id: i32, id: i32,
channel: &str, channel: &str,
data: &[u8], data: &[u8],
) -> Result<(i32, Option<Vec<u8>>), ServerError> { ) -> 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);
packet.write_varint(id)?; packet.write_varint(id)?;
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)?;
let mut packet = self let mut packet = self
.client .client
.read_packet(&[serverbound::login::PLUGIN_RESPONSE])?; .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();
packet.get_mut().read_to_end(&mut data).unwrap(); packet.get_mut().read_to_end(&mut data).unwrap();
Some(data) Some(data)
} else { } else {
None None
}; };
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 => { ConnectionState::Configuration => Packet::empty(clientbound::configuration::PLUGIN_MESSAGE),
Packet::empty(clientbound::configuration::PLUGIN_MESSAGE) ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE),
} _ => return Err(ServerError::UnexpectedState),
ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE), };
_ => return Err(ServerError::UnexpectedState), packet.write_string(channel)?;
}; packet.write_bytes(data)?;
packet.write_string(channel)?; self.client.write_packet(&packet)?;
packet.write_bytes(data)?; Ok(())
self.client.write_packet(&packet)?; }
Ok(())
}
} }

View File

@ -1,189 +1,189 @@
use std::{io::Read, sync::Arc}; use std::{io::Read, sync::Arc};
use crate::server::{ use crate::server::{
ServerError, ServerError,
player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo}, 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::{ use super::{
ConnectionState, ConnectionState,
id::*, id::*,
play::{handle_configuration_state, handle_play_state}, play::{handle_configuration_state, 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 { client.set_handshake(Handshake {
protocol_version, protocol_version,
server_address, server_address,
server_port, 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 timestamp = packet.read_long()?;
let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE); let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE);
packet.write_long(timestamp)?; packet.write_long(timestamp)?;
client.write_packet(&packet)?; client.write_packet(&packet)?;
} }
id => { id => {
return Err(ServerError::UnexpectedPacket(id)); return Err(ServerError::UnexpectedPacket(id));
} }
} }
} }
} }
2 => { 2 => {
// Тип подключения - игра // Тип подключения - игра
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login 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 { client.set_player_info(PlayerInfo {
name: name.clone(), name: name.clone(),
uuid: uuid.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| { client.write_packet(&Packet::build(clientbound::login::SET_COMPRESSION, |p| {
p.write_usize_varint(threshold) p.write_usize_varint(threshold)
})?)?; })?)?;
client.set_compression(Some(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 // Получение бренда клиента из Serverbound Plugin Message
// Identifier канала откуда берется бренд: minecraft:brand // Identifier канала откуда берется бренд: minecraft:brand
let brand = loop { let brand = loop {
let mut packet = client.read_packet(&[serverbound::configuration::PLUGIN_MESSAGE])?; // Пакет Serverbound Plugin Message 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(); let mut data = Vec::new();
packet.get_mut().read_to_end(&mut data).unwrap(); packet.get_mut().read_to_end(&mut data).unwrap();
if identifier == "minecraft:brand" { if identifier == "minecraft:brand" {
break String::from_utf8_lossy(&data).to_string(); break String::from_utf8_lossy(&data).to_string();
} else { } else {
trigger_event!(client, plugin_message, &identifier, &data); 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 locale = packet.read_string()?; // for example: en_us
let view_distance = packet.read_signed_byte()?; // client-side render distance in chunks 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_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 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 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 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 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 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 particle_status = packet.read_varint()?; // 0 for all, 1 for decreased, 2 for minimal
client.set_client_info(ClientInfo { client.set_client_info(ClientInfo {
brand, brand,
locale, locale,
view_distance, view_distance,
chat_mode, chat_mode,
chat_colors, chat_colors,
displayed_skin_parts, displayed_skin_parts,
main_hand, main_hand,
enable_text_filtering, enable_text_filtering,
allow_server_listings, allow_server_listings,
particle_status, particle_status,
}); });
client.write_packet(&Packet::build( client.write_packet(&Packet::build(
clientbound::configuration::PLUGIN_MESSAGE, clientbound::configuration::PLUGIN_MESSAGE,
|p| { |p| {
p.write_string("minecraft:brand")?; p.write_string("minecraft:brand")?;
p.write_string("rust_minecraft_server") 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.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
client.read_packet(&[serverbound::configuration::ACKNOWLEDGE_FINISH])?; client.read_packet(&[serverbound::configuration::ACKNOWLEDGE_FINISH])?;
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
// Дальше работаем с режимом игры // Дальше работаем с режимом игры
handle_play_state(client)?; handle_play_state(client)?;
} }
_ => { _ => {
// Тип подключения не рукопожатный // Тип подключения не рукопожатный
return Err(ServerError::UnexpectedState); return Err(ServerError::UnexpectedState);
} }
} }
Ok(()) Ok(())
} }

View File

@ -5,268 +5,268 @@ Generated with parse_ids.py
*/ */
pub mod clientbound { pub mod clientbound {
pub mod status { pub mod status {
pub const RESPONSE: u8 = 0x00; pub const RESPONSE: u8 = 0x00;
pub const PONG_RESPONSE: u8 = 0x01; pub const PONG_RESPONSE: u8 = 0x01;
} }
pub mod login { pub mod login {
pub const DISCONNECT: u8 = 0x00; pub const DISCONNECT: u8 = 0x00;
pub const ENCRYPTION_REQUEST: u8 = 0x01; pub const ENCRYPTION_REQUEST: u8 = 0x01;
pub const SUCCESS: u8 = 0x02; pub const SUCCESS: u8 = 0x02;
pub const SET_COMPRESSION: u8 = 0x03; pub const SET_COMPRESSION: u8 = 0x03;
pub const PLUGIN_REQUEST: u8 = 0x04; pub const PLUGIN_REQUEST: u8 = 0x04;
pub const COOKIE_REQUEST: u8 = 0x05; pub const COOKIE_REQUEST: u8 = 0x05;
} }
pub mod configuration { pub mod configuration {
pub const COOKIE_REQUEST: u8 = 0x00; pub const COOKIE_REQUEST: u8 = 0x00;
pub const PLUGIN_MESSAGE: u8 = 0x01; pub const PLUGIN_MESSAGE: u8 = 0x01;
pub const DISCONNECT: u8 = 0x02; pub const DISCONNECT: u8 = 0x02;
pub const FINISH: u8 = 0x03; pub const FINISH: u8 = 0x03;
pub const KEEP_ALIVE: u8 = 0x04; pub const KEEP_ALIVE: u8 = 0x04;
pub const PING: u8 = 0x05; pub const PING: u8 = 0x05;
pub const RESET_CHAT: u8 = 0x06; pub const RESET_CHAT: u8 = 0x06;
pub const REGISTRY_DATA: u8 = 0x07; pub const REGISTRY_DATA: u8 = 0x07;
pub const REMOVE_RESOURCE_PACK: u8 = 0x08; pub const REMOVE_RESOURCE_PACK: u8 = 0x08;
pub const ADD_RESOURCE_PACK: u8 = 0x09; pub const ADD_RESOURCE_PACK: u8 = 0x09;
pub const STORE_COOKIE: u8 = 0x0A; pub const STORE_COOKIE: u8 = 0x0A;
pub const TRANSFER: u8 = 0x0B; pub const TRANSFER: u8 = 0x0B;
pub const FEATURE_FLAGS: u8 = 0x0C; pub const FEATURE_FLAGS: u8 = 0x0C;
pub const UPDATE_TAGS: u8 = 0x0D; pub const UPDATE_TAGS: u8 = 0x0D;
pub const KNOWN_PACKS: u8 = 0x0E; pub const KNOWN_PACKS: u8 = 0x0E;
pub const CUSTOM_REPORT_DETAILS: u8 = 0x0F; pub const CUSTOM_REPORT_DETAILS: u8 = 0x0F;
pub const SERVER_LINKS: u8 = 0x10; pub const SERVER_LINKS: u8 = 0x10;
} }
pub mod play { pub mod play {
pub const BUNDLE_DELIMITER: u8 = 0x00; pub const BUNDLE_DELIMITER: u8 = 0x00;
pub const SPAWN_ENTITY: u8 = 0x01; pub const SPAWN_ENTITY: u8 = 0x01;
pub const ENTITY_ANIMATION: u8 = 0x02; pub const ENTITY_ANIMATION: u8 = 0x02;
pub const AWARD_STATISTICS: u8 = 0x03; pub const AWARD_STATISTICS: u8 = 0x03;
pub const ACKNOWLEDGE_BLOCK_CHANGE: u8 = 0x04; pub const ACKNOWLEDGE_BLOCK_CHANGE: u8 = 0x04;
pub const SET_BLOCK_DESTROY_STAGE: u8 = 0x05; pub const SET_BLOCK_DESTROY_STAGE: u8 = 0x05;
pub const BLOCK_ENTITY_DATA: u8 = 0x06; pub const BLOCK_ENTITY_DATA: u8 = 0x06;
pub const BLOCK_ACTION: u8 = 0x07; pub const BLOCK_ACTION: u8 = 0x07;
pub const BLOCK_UPDATE: u8 = 0x08; pub const BLOCK_UPDATE: u8 = 0x08;
pub const BOSS_BAR: u8 = 0x09; pub const BOSS_BAR: u8 = 0x09;
pub const CHANGE_DIFFICULTY: u8 = 0x0A; pub const CHANGE_DIFFICULTY: u8 = 0x0A;
pub const CHUNK_BATCH_FINISHED: u8 = 0x0B; pub const CHUNK_BATCH_FINISHED: u8 = 0x0B;
pub const CHUNK_BATCH_START: u8 = 0x0C; pub const CHUNK_BATCH_START: u8 = 0x0C;
pub const CHUNK_BIOMES: u8 = 0x0D; pub const CHUNK_BIOMES: u8 = 0x0D;
pub const CLEAR_TITLES: u8 = 0x0E; pub const CLEAR_TITLES: u8 = 0x0E;
pub const COMMAND_SUGGESTIONS_RESPONSE: u8 = 0x0F; pub const COMMAND_SUGGESTIONS_RESPONSE: u8 = 0x0F;
pub const COMMANDS: u8 = 0x10; pub const COMMANDS: u8 = 0x10;
pub const CLOSE_CONTAINER: u8 = 0x11; pub const CLOSE_CONTAINER: u8 = 0x11;
pub const SET_CONTAINER_CONTENT: u8 = 0x12; pub const SET_CONTAINER_CONTENT: u8 = 0x12;
pub const SET_CONTAINER_PROPERTY: u8 = 0x13; pub const SET_CONTAINER_PROPERTY: u8 = 0x13;
pub const SET_CONTAINER_SLOT: u8 = 0x14; pub const SET_CONTAINER_SLOT: u8 = 0x14;
pub const COOKIE_REQUEST: u8 = 0x15; pub const COOKIE_REQUEST: u8 = 0x15;
pub const SET_COOLDOWN: u8 = 0x16; pub const SET_COOLDOWN: u8 = 0x16;
pub const CHAT_SUGGESTIONS: u8 = 0x17; pub const CHAT_SUGGESTIONS: u8 = 0x17;
pub const PLUGIN_MESSAGE: u8 = 0x18; pub const PLUGIN_MESSAGE: u8 = 0x18;
pub const DAMAGE_EVENT: u8 = 0x19; pub const DAMAGE_EVENT: u8 = 0x19;
pub const DEBUG_SAMPLE: u8 = 0x1A; pub const DEBUG_SAMPLE: u8 = 0x1A;
pub const DELETE_MESSAGE: u8 = 0x1B; pub const DELETE_MESSAGE: u8 = 0x1B;
pub const DISCONNECT: u8 = 0x1C; pub const DISCONNECT: u8 = 0x1C;
pub const DISGUISED_CHAT_MESSAGE: u8 = 0x1D; pub const DISGUISED_CHAT_MESSAGE: u8 = 0x1D;
pub const ENTITY_EVENT: u8 = 0x1E; pub const ENTITY_EVENT: u8 = 0x1E;
pub const TELEPORT_ENTITY: u8 = 0x1F; pub const TELEPORT_ENTITY: u8 = 0x1F;
pub const EXPLOSION: u8 = 0x20; pub const EXPLOSION: u8 = 0x20;
pub const UNLOAD_CHUNK: u8 = 0x21; pub const UNLOAD_CHUNK: u8 = 0x21;
pub const GAME_EVENT: u8 = 0x22; pub const GAME_EVENT: u8 = 0x22;
pub const OPEN_HORSE_SCREEN: u8 = 0x23; pub const OPEN_HORSE_SCREEN: u8 = 0x23;
pub const HURT_ANIMATION: u8 = 0x24; pub const HURT_ANIMATION: u8 = 0x24;
pub const INITIALIZE_WORLD_BORDER: u8 = 0x25; pub const INITIALIZE_WORLD_BORDER: u8 = 0x25;
pub const KEEP_ALIVE: u8 = 0x26; pub const KEEP_ALIVE: u8 = 0x26;
pub const CHUNK_DATA_AND_UPDATE_LIGHT: u8 = 0x27; pub const CHUNK_DATA_AND_UPDATE_LIGHT: u8 = 0x27;
pub const WORLD_EVENT: u8 = 0x28; pub const WORLD_EVENT: u8 = 0x28;
pub const PARTICLE: u8 = 0x29; pub const PARTICLE: u8 = 0x29;
pub const UPDATE_LIGHT: u8 = 0x2A; pub const UPDATE_LIGHT: u8 = 0x2A;
pub const LOGIN: u8 = 0x2B; pub const LOGIN: u8 = 0x2B;
pub const MAP_DATA: u8 = 0x2C; pub const MAP_DATA: u8 = 0x2C;
pub const MERCHANT_OFFERS: u8 = 0x2D; pub const MERCHANT_OFFERS: u8 = 0x2D;
pub const UPDATE_ENTITY_POSITION: u8 = 0x2E; pub const UPDATE_ENTITY_POSITION: u8 = 0x2E;
pub const UPDATE_ENTITY_POSITION_AND_ROTATION: u8 = 0x2F; pub const UPDATE_ENTITY_POSITION_AND_ROTATION: u8 = 0x2F;
pub const MOVE_MINECART_ALONG_TRACK: u8 = 0x30; pub const MOVE_MINECART_ALONG_TRACK: u8 = 0x30;
pub const UPDATE_ENTITY_ROTATION: u8 = 0x31; pub const UPDATE_ENTITY_ROTATION: u8 = 0x31;
pub const MOVE_VEHICLE: u8 = 0x32; pub const MOVE_VEHICLE: u8 = 0x32;
pub const OPEN_BOOK: u8 = 0x33; pub const OPEN_BOOK: u8 = 0x33;
pub const OPEN_SCREEN: u8 = 0x34; pub const OPEN_SCREEN: u8 = 0x34;
pub const OPEN_SIGN_EDITOR: u8 = 0x35; pub const OPEN_SIGN_EDITOR: u8 = 0x35;
pub const PING: u8 = 0x36; pub const PING: u8 = 0x36;
pub const PING_RESPONSE: u8 = 0x37; pub const PING_RESPONSE: u8 = 0x37;
pub const PLACE_GHOST_RECIPE: u8 = 0x38; pub const PLACE_GHOST_RECIPE: u8 = 0x38;
pub const PLAYER_ABILITIES: u8 = 0x39; pub const PLAYER_ABILITIES: u8 = 0x39;
pub const PLAYER_CHAT_MESSAGE: u8 = 0x3A; pub const PLAYER_CHAT_MESSAGE: u8 = 0x3A;
pub const END_COMBAT: u8 = 0x3B; pub const END_COMBAT: u8 = 0x3B;
pub const ENTER_COMBAT: u8 = 0x3C; pub const ENTER_COMBAT: u8 = 0x3C;
pub const COMBAT_DEATH: u8 = 0x3D; pub const COMBAT_DEATH: u8 = 0x3D;
pub const PLAYER_INFO_REMOVE: u8 = 0x3E; pub const PLAYER_INFO_REMOVE: u8 = 0x3E;
pub const PLAYER_INFO_UPDATE: u8 = 0x3F; pub const PLAYER_INFO_UPDATE: u8 = 0x3F;
pub const LOOK_AT: u8 = 0x40; pub const LOOK_AT: u8 = 0x40;
pub const SYNCHRONIZE_PLAYER_POSITION: u8 = 0x41; pub const SYNCHRONIZE_PLAYER_POSITION: u8 = 0x41;
pub const PLAYER_ROTATION: u8 = 0x42; pub const PLAYER_ROTATION: u8 = 0x42;
pub const RECIPE_BOOK_ADD: u8 = 0x43; pub const RECIPE_BOOK_ADD: u8 = 0x43;
pub const RECIPE_BOOK_REMOVE: u8 = 0x44; pub const RECIPE_BOOK_REMOVE: u8 = 0x44;
pub const RECIPE_BOOK_SETTINGS: u8 = 0x45; pub const RECIPE_BOOK_SETTINGS: u8 = 0x45;
pub const REMOVE_ENTITIES: u8 = 0x46; pub const REMOVE_ENTITIES: u8 = 0x46;
pub const REMOVE_ENTITY_EFFECT: u8 = 0x47; pub const REMOVE_ENTITY_EFFECT: u8 = 0x47;
pub const RESET_SCORE: u8 = 0x48; pub const RESET_SCORE: u8 = 0x48;
pub const REMOVE_RESOURCE_PACK: u8 = 0x49; pub const REMOVE_RESOURCE_PACK: u8 = 0x49;
pub const ADD_RESOURCE_PACK: u8 = 0x4A; pub const ADD_RESOURCE_PACK: u8 = 0x4A;
pub const RESPAWN: u8 = 0x4B; pub const RESPAWN: u8 = 0x4B;
pub const SET_HEAD_ROTATION: u8 = 0x4C; pub const SET_HEAD_ROTATION: u8 = 0x4C;
pub const UPDATE_SECTION_BLOCKS: u8 = 0x4D; pub const UPDATE_SECTION_BLOCKS: u8 = 0x4D;
pub const SELECT_ADVANCEMENTS_TAB: u8 = 0x4E; pub const SELECT_ADVANCEMENTS_TAB: u8 = 0x4E;
pub const SERVER_DATA: u8 = 0x4F; pub const SERVER_DATA: u8 = 0x4F;
pub const SET_ACTION_BAR_TEXT: u8 = 0x50; pub const SET_ACTION_BAR_TEXT: u8 = 0x50;
pub const SET_BORDER_CENTER: u8 = 0x51; pub const SET_BORDER_CENTER: u8 = 0x51;
pub const SET_BORDER_LERP_SIZE: u8 = 0x52; pub const SET_BORDER_LERP_SIZE: u8 = 0x52;
pub const SET_BORDER_SIZE: u8 = 0x53; pub const SET_BORDER_SIZE: u8 = 0x53;
pub const SET_BORDER_WARNING_DELAY: u8 = 0x54; pub const SET_BORDER_WARNING_DELAY: u8 = 0x54;
pub const SET_BORDER_WARNING_DISTANCE: u8 = 0x55; pub const SET_BORDER_WARNING_DISTANCE: u8 = 0x55;
pub const SET_CAMERA: u8 = 0x56; pub const SET_CAMERA: u8 = 0x56;
pub const SET_CENTER_CHUNK: u8 = 0x57; pub const SET_CENTER_CHUNK: u8 = 0x57;
pub const SET_RENDER_DISTANCE: u8 = 0x58; pub const SET_RENDER_DISTANCE: u8 = 0x58;
pub const SET_CURSOR_ITEM: u8 = 0x59; pub const SET_CURSOR_ITEM: u8 = 0x59;
pub const SET_DEFAULT_SPAWN_POSITION: u8 = 0x5A; pub const SET_DEFAULT_SPAWN_POSITION: u8 = 0x5A;
pub const DISPLAY_OBJECTIVE: u8 = 0x5B; pub const DISPLAY_OBJECTIVE: u8 = 0x5B;
pub const SET_ENTITY_METADATA: u8 = 0x5C; pub const SET_ENTITY_METADATA: u8 = 0x5C;
pub const LINK_ENTITIES: u8 = 0x5D; pub const LINK_ENTITIES: u8 = 0x5D;
pub const SET_ENTITY_VELOCITY: u8 = 0x5E; pub const SET_ENTITY_VELOCITY: u8 = 0x5E;
pub const SET_EQUIPMENT: u8 = 0x5F; pub const SET_EQUIPMENT: u8 = 0x5F;
pub const SET_EXPERIENCE: u8 = 0x60; pub const SET_EXPERIENCE: u8 = 0x60;
pub const SET_HEALTH: u8 = 0x61; pub const SET_HEALTH: u8 = 0x61;
pub const SET_HELD_ITEM: u8 = 0x62; pub const SET_HELD_ITEM: u8 = 0x62;
pub const UPDATE_OBJECTIVES: u8 = 0x63; pub const UPDATE_OBJECTIVES: u8 = 0x63;
pub const SET_PASSENGERS: u8 = 0x64; pub const SET_PASSENGERS: u8 = 0x64;
pub const SET_PLAYER_INVENTORY_SLOT: u8 = 0x65; pub const SET_PLAYER_INVENTORY_SLOT: u8 = 0x65;
pub const UPDATE_TEAMS: u8 = 0x66; pub const UPDATE_TEAMS: u8 = 0x66;
pub const UPDATE_SCORE: u8 = 0x67; pub const UPDATE_SCORE: u8 = 0x67;
pub const SET_SIMULATION_DISTANCE: u8 = 0x68; pub const SET_SIMULATION_DISTANCE: u8 = 0x68;
pub const SET_SUBTITLE_TEXT: u8 = 0x69; pub const SET_SUBTITLE_TEXT: u8 = 0x69;
pub const UPDATE_TIME: u8 = 0x6A; pub const UPDATE_TIME: u8 = 0x6A;
pub const SET_TITLE_TEXT: u8 = 0x6B; pub const SET_TITLE_TEXT: u8 = 0x6B;
pub const SET_TITLE_ANIMATION_TIMES: u8 = 0x6C; pub const SET_TITLE_ANIMATION_TIMES: u8 = 0x6C;
pub const ENTITY_SOUND_EFFECT: u8 = 0x6D; pub const ENTITY_SOUND_EFFECT: u8 = 0x6D;
pub const SOUND_EFFECT: u8 = 0x6E; pub const SOUND_EFFECT: u8 = 0x6E;
pub const START_CONFIGURATION: u8 = 0x6F; pub const START_CONFIGURATION: u8 = 0x6F;
pub const STOP_SOUND: u8 = 0x70; pub const STOP_SOUND: u8 = 0x70;
pub const STORE_COOKIE: u8 = 0x71; pub const STORE_COOKIE: u8 = 0x71;
pub const SYSTEM_CHAT_MESSAGE: u8 = 0x72; pub const SYSTEM_CHAT_MESSAGE: u8 = 0x72;
pub const SET_TAB_LIST_HEADER_AND_FOOTER: u8 = 0x73; pub const SET_TAB_LIST_HEADER_AND_FOOTER: u8 = 0x73;
pub const TAG_QUERY_RESPONSE: u8 = 0x74; pub const TAG_QUERY_RESPONSE: u8 = 0x74;
pub const PICKUP_ITEM: u8 = 0x75; pub const PICKUP_ITEM: u8 = 0x75;
pub const SYNCHRONIZE_VEHICLE_POSITION: u8 = 0x76; pub const SYNCHRONIZE_VEHICLE_POSITION: u8 = 0x76;
pub const TEST_INSTANCE_BLOCK_STATUS: u8 = 0x77; pub const TEST_INSTANCE_BLOCK_STATUS: u8 = 0x77;
pub const SET_TICKING_STATE: u8 = 0x78; pub const SET_TICKING_STATE: u8 = 0x78;
pub const STEP_TICK: u8 = 0x79; pub const STEP_TICK: u8 = 0x79;
pub const TRANSFER: u8 = 0x7A; pub const TRANSFER: u8 = 0x7A;
pub const UPDATE_ADVANCEMENTS: u8 = 0x7B; pub const UPDATE_ADVANCEMENTS: u8 = 0x7B;
pub const UPDATE_ATTRIBUTES: u8 = 0x7C; pub const UPDATE_ATTRIBUTES: u8 = 0x7C;
pub const ENTITY_EFFECT: u8 = 0x7D; pub const ENTITY_EFFECT: u8 = 0x7D;
pub const UPDATE_RECIPES: u8 = 0x7E; pub const UPDATE_RECIPES: u8 = 0x7E;
pub const UPDATE_TAGS: u8 = 0x7F; pub const UPDATE_TAGS: u8 = 0x7F;
pub const PROJECTILE_POWER: u8 = 0x80; pub const PROJECTILE_POWER: u8 = 0x80;
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 {
pub mod handshake { pub mod handshake {
pub const HANDSHAKE: u8 = 0x00; pub const HANDSHAKE: u8 = 0x00;
} }
pub mod status { pub mod status {
pub const REQUEST: u8 = 0x00; pub const REQUEST: u8 = 0x00;
pub const PING_REQUEST: u8 = 0x01; pub const PING_REQUEST: u8 = 0x01;
} }
pub mod login { pub mod login {
pub const START: u8 = 0x00; pub const START: u8 = 0x00;
pub const ENCRYPTION_RESPONSE: u8 = 0x01; pub const ENCRYPTION_RESPONSE: u8 = 0x01;
pub const PLUGIN_RESPONSE: u8 = 0x02; pub const PLUGIN_RESPONSE: u8 = 0x02;
pub const ACKNOWLEDGED: u8 = 0x03; pub const ACKNOWLEDGED: u8 = 0x03;
pub const COOKIE_RESPONSE: u8 = 0x04; pub const COOKIE_RESPONSE: u8 = 0x04;
} }
pub mod configuration { pub mod configuration {
pub const CLIENT_INFORMATION: u8 = 0x00; pub const CLIENT_INFORMATION: u8 = 0x00;
pub const COOKIE_RESPONSE: u8 = 0x01; pub const COOKIE_RESPONSE: u8 = 0x01;
pub const PLUGIN_MESSAGE: u8 = 0x02; pub const PLUGIN_MESSAGE: u8 = 0x02;
pub const ACKNOWLEDGE_FINISH: u8 = 0x03; pub const ACKNOWLEDGE_FINISH: u8 = 0x03;
pub const KEEP_ALIVE: u8 = 0x04; pub const KEEP_ALIVE: u8 = 0x04;
pub const PONG: u8 = 0x05; pub const PONG: u8 = 0x05;
pub const RESOURCE_PACK_RESPONSE: u8 = 0x06; pub const RESOURCE_PACK_RESPONSE: u8 = 0x06;
pub const KNOWN_PACKS: u8 = 0x07; pub const KNOWN_PACKS: u8 = 0x07;
} }
pub mod play { pub mod play {
pub const CONFIRM_TELEPORTATION: u8 = 0x00; pub const CONFIRM_TELEPORTATION: u8 = 0x00;
pub const QUERY_BLOCK_ENTITY_TAG: u8 = 0x01; pub const QUERY_BLOCK_ENTITY_TAG: u8 = 0x01;
pub const BUNDLE_ITEM_SELECTED: u8 = 0x02; pub const BUNDLE_ITEM_SELECTED: u8 = 0x02;
pub const CHANGE_DIFFICULTY: u8 = 0x03; pub const CHANGE_DIFFICULTY: u8 = 0x03;
pub const ACKNOWLEDGE_MESSAGE: u8 = 0x04; pub const ACKNOWLEDGE_MESSAGE: u8 = 0x04;
pub const CHAT_COMMAND: u8 = 0x05; pub const CHAT_COMMAND: u8 = 0x05;
pub const SIGNED_CHAT_COMMAND: u8 = 0x06; pub const SIGNED_CHAT_COMMAND: u8 = 0x06;
pub const CHAT_MESSAGE: u8 = 0x07; pub const CHAT_MESSAGE: u8 = 0x07;
pub const PLAYER_SESSION: u8 = 0x08; pub const PLAYER_SESSION: u8 = 0x08;
pub const CHUNK_BATCH_RECEIVED: u8 = 0x09; pub const CHUNK_BATCH_RECEIVED: u8 = 0x09;
pub const CLIENT_STATUS: u8 = 0x0A; pub const CLIENT_STATUS: u8 = 0x0A;
pub const CLIENT_TICK_END: u8 = 0x0B; pub const CLIENT_TICK_END: u8 = 0x0B;
pub const CLIENT_INFORMATION: u8 = 0x0C; pub const CLIENT_INFORMATION: u8 = 0x0C;
pub const COMMAND_SUGGESTIONS_REQUEST: u8 = 0x0D; pub const COMMAND_SUGGESTIONS_REQUEST: u8 = 0x0D;
pub const ACKNOWLEDGE_CONFIGURATION: u8 = 0x0E; pub const ACKNOWLEDGE_CONFIGURATION: u8 = 0x0E;
pub const CLICK_CONTAINER_BUTTON: u8 = 0x0F; pub const CLICK_CONTAINER_BUTTON: u8 = 0x0F;
pub const CLICK_CONTAINER: u8 = 0x10; pub const CLICK_CONTAINER: u8 = 0x10;
pub const CLOSE_CONTAINER: u8 = 0x11; pub const CLOSE_CONTAINER: u8 = 0x11;
pub const CHANGE_CONTAINER_SLOT_STATE: u8 = 0x12; pub const CHANGE_CONTAINER_SLOT_STATE: u8 = 0x12;
pub const COOKIE_RESPONSE: u8 = 0x13; pub const COOKIE_RESPONSE: u8 = 0x13;
pub const PLUGIN_MESSAGE: u8 = 0x14; pub const PLUGIN_MESSAGE: u8 = 0x14;
pub const DEBUG_SAMPLE_SUBSCRIPTION: u8 = 0x15; pub const DEBUG_SAMPLE_SUBSCRIPTION: u8 = 0x15;
pub const EDIT_BOOK: u8 = 0x16; pub const EDIT_BOOK: u8 = 0x16;
pub const QUERY_ENTITY_TAG: u8 = 0x17; pub const QUERY_ENTITY_TAG: u8 = 0x17;
pub const INTERACT: u8 = 0x18; pub const INTERACT: u8 = 0x18;
pub const JIGSAW_GENERATE: u8 = 0x19; pub const JIGSAW_GENERATE: u8 = 0x19;
pub const KEEP_ALIVE: u8 = 0x1A; pub const KEEP_ALIVE: u8 = 0x1A;
pub const LOCK_DIFFICULTY: u8 = 0x1B; pub const LOCK_DIFFICULTY: u8 = 0x1B;
pub const SET_PLAYER_POSITION: u8 = 0x1C; pub const SET_PLAYER_POSITION: u8 = 0x1C;
pub const SET_PLAYER_POSITION_AND_ROTATION: u8 = 0x1D; pub const SET_PLAYER_POSITION_AND_ROTATION: u8 = 0x1D;
pub const SET_PLAYER_ROTATION: u8 = 0x1E; pub const SET_PLAYER_ROTATION: u8 = 0x1E;
pub const SET_PLAYER_MOVEMENT_FLAGS: u8 = 0x1F; pub const SET_PLAYER_MOVEMENT_FLAGS: u8 = 0x1F;
pub const MOVE_VEHICLE: u8 = 0x20; pub const MOVE_VEHICLE: u8 = 0x20;
pub const PADDLE_BOAT: u8 = 0x21; pub const PADDLE_BOAT: u8 = 0x21;
pub const PICK_ITEM_FROM_BLOCK: u8 = 0x22; pub const PICK_ITEM_FROM_BLOCK: u8 = 0x22;
pub const PICK_ITEM_FROM_ENTITY: u8 = 0x23; pub const PICK_ITEM_FROM_ENTITY: u8 = 0x23;
pub const PING_REQUEST: u8 = 0x24; pub const PING_REQUEST: u8 = 0x24;
pub const PLACE_RECIPE: u8 = 0x25; pub const PLACE_RECIPE: u8 = 0x25;
pub const PLAYER_ABILITIES: u8 = 0x26; pub const PLAYER_ABILITIES: u8 = 0x26;
pub const PLAYER_ACTION: u8 = 0x27; pub const PLAYER_ACTION: u8 = 0x27;
pub const PLAYER_COMMAND: u8 = 0x28; pub const PLAYER_COMMAND: u8 = 0x28;
pub const PLAYER_INPUT: u8 = 0x29; pub const PLAYER_INPUT: u8 = 0x29;
pub const PLAYER_LOADED: u8 = 0x2A; pub const PLAYER_LOADED: u8 = 0x2A;
pub const PONG: u8 = 0x2B; pub const PONG: u8 = 0x2B;
pub const CHANGE_RECIPE_BOOK_SETTINGS: u8 = 0x2C; pub const CHANGE_RECIPE_BOOK_SETTINGS: u8 = 0x2C;
pub const SET_SEEN_RECIPE: u8 = 0x2D; pub const SET_SEEN_RECIPE: u8 = 0x2D;
pub const RENAME_ITEM: u8 = 0x2E; pub const RENAME_ITEM: u8 = 0x2E;
pub const RESOURCE_PACK_RESPONSE: u8 = 0x2F; pub const RESOURCE_PACK_RESPONSE: u8 = 0x2F;
pub const SEEN_ADVANCEMENTS: u8 = 0x30; pub const SEEN_ADVANCEMENTS: u8 = 0x30;
pub const SELECT_TRADE: u8 = 0x31; pub const SELECT_TRADE: u8 = 0x31;
pub const SET_BEACON_EFFECT: u8 = 0x32; pub const SET_BEACON_EFFECT: u8 = 0x32;
pub const SET_HELD_ITEM: u8 = 0x33; pub const SET_HELD_ITEM: u8 = 0x33;
pub const PROGRAM_COMMAND_BLOCK: u8 = 0x34; pub const PROGRAM_COMMAND_BLOCK: u8 = 0x34;
pub const PROGRAM_COMMAND_BLOCK_MINECART: u8 = 0x35; pub const PROGRAM_COMMAND_BLOCK_MINECART: u8 = 0x35;
pub const SET_CREATIVE_MODE_SLOT: u8 = 0x36; pub const SET_CREATIVE_MODE_SLOT: u8 = 0x36;
pub const PROGRAM_JIGSAW_BLOCK: u8 = 0x37; pub const PROGRAM_JIGSAW_BLOCK: u8 = 0x37;
pub const PROGRAM_STRUCTURE_BLOCK: u8 = 0x38; pub const PROGRAM_STRUCTURE_BLOCK: u8 = 0x38;
pub const SET_TEST_BLOCK: u8 = 0x39; pub const SET_TEST_BLOCK: u8 = 0x39;
pub const UPDATE_SIGN: u8 = 0x3A; pub const UPDATE_SIGN: u8 = 0x3A;
pub const SWING_ARM: u8 = 0x3B; pub const SWING_ARM: u8 = 0x3B;
pub const TELEPORT_TO_ENTITY: u8 = 0x3C; pub const TELEPORT_TO_ENTITY: u8 = 0x3C;
pub const TEST_INSTANCE_BLOCK_ACTION: u8 = 0x3D; pub const TEST_INSTANCE_BLOCK_ACTION: u8 = 0x3D;
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

@ -4,9 +4,9 @@ pub mod play;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ConnectionState { pub enum ConnectionState {
Handshake, Handshake,
Status, Status,
Login, Login,
Configuration, Configuration,
Play, Play,
} }

View File

@ -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::*; use super::id::*;
pub fn send_update_tags(client: Arc<ClientContext>) -> Result<(), ServerError> { pub fn send_update_tags(client: Arc<ClientContext>) -> Result<(), ServerError> {
// TODO: rewrite this hardcode bullshit // TODO: rewrite this hardcode bullshit
client.write_packet(&Packet::from_bytes( client.write_packet(&Packet::from_bytes(
clientbound::configuration::UPDATE_TAGS, clientbound::configuration::UPDATE_TAGS,
include_bytes!("update-tags.bin"), include_bytes!("update-tags.bin"),
)) ))
} }
pub fn send_registry_data(client: Arc<ClientContext>) -> Result<(), ServerError> { pub fn send_registry_data(client: Arc<ClientContext>) -> 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) { while let Ok(mut packet) = read_packet(&mut registry_data, None) {
packet.set_id(clientbound::configuration::REGISTRY_DATA); packet.set_id(clientbound::configuration::REGISTRY_DATA);
client.write_packet(&packet)?; client.write_packet(&packet)?;
} }
Ok(()) Ok(())
} }
// Добавки в Configuration стейт чтобы все работало // Добавки в Configuration стейт чтобы все работало
pub fn handle_configuration_state( pub fn handle_configuration_state(
client: Arc<ClientContext>, // Контекст клиента client: Arc<ClientContext>, // Контекст клиента
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS); let mut packet = Packet::empty(clientbound::configuration::FEATURE_FLAGS);
packet.write_varint(1)?; packet.write_varint(1)?;
packet.write_string("minecraft:vanilla")?; packet.write_string("minecraft:vanilla")?;
client.write_packet(&packet)?; client.write_packet(&packet)?;
let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS); let mut packet = Packet::empty(clientbound::configuration::KNOWN_PACKS);
packet.write_varint(1)?; packet.write_varint(1)?;
packet.write_string("minecraft")?; packet.write_string("minecraft")?;
packet.write_string("core")?; packet.write_string("core")?;
packet.write_string("1.21.5")?; packet.write_string("1.21.5")?;
client.write_packet(&packet)?; client.write_packet(&packet)?;
client.read_packet(&[serverbound::configuration::KNOWN_PACKS])?; client.read_packet(&[serverbound::configuration::KNOWN_PACKS])?;
send_registry_data(client.clone())?; send_registry_data(client.clone())?;
send_update_tags(client.clone()) send_update_tags(client.clone())
} }
pub fn send_login(client: Arc<ClientContext>) -> Result<(), ServerError> { pub fn send_login(client: Arc<ClientContext>) -> Result<(), ServerError> {
// Отправка пакета Login // Отправка пакета Login
let mut packet = Packet::empty(clientbound::play::LOGIN); let mut packet = Packet::empty(clientbound::play::LOGIN);
packet.write_int(0)?; // Entity ID packet.write_int(0)?; // Entity ID
packet.write_boolean(false)?; // Is hardcore packet.write_boolean(false)?; // Is hardcore
packet.write_varint(4)?; // Dimension Names packet.write_varint(4)?; // Dimension Names
packet.write_string("minecraft:overworld")?; packet.write_string("minecraft:overworld")?;
packet.write_string("minecraft:nether")?; packet.write_string("minecraft:nether")?;
packet.write_string("minecraft:the_end")?; packet.write_string("minecraft:the_end")?;
packet.write_string("minecraft:overworld_caves")?; packet.write_string("minecraft:overworld_caves")?;
packet.write_varint(0)?; // Max Players packet.write_varint(0)?; // Max Players
packet.write_varint(8)?; // View Distance packet.write_varint(8)?; // View Distance
packet.write_varint(5)?; // Simulation Distance packet.write_varint(5)?; // Simulation Distance
packet.write_boolean(false)?; // Reduced Debug Info packet.write_boolean(false)?; // Reduced Debug Info
packet.write_boolean(true)?; // Enable respawn screen packet.write_boolean(true)?; // Enable respawn screen
packet.write_boolean(false)?; // Do limited crafting packet.write_boolean(false)?; // Do limited crafting
packet.write_varint(0)?; // Dimension Type packet.write_varint(0)?; // Dimension Type
packet.write_string("minecraft:overworld")?; // Dimension Name packet.write_string("minecraft:overworld")?; // Dimension Name
packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed packet.write_long(0x0f38f26ad09c3e20)?; // Hashed seed
packet.write_byte(0)?; // Game mode packet.write_byte(0)?; // Game mode
packet.write_signed_byte(-1)?; // Previous Game mode packet.write_signed_byte(-1)?; // Previous Game mode
packet.write_boolean(false)?; // Is Debug packet.write_boolean(false)?; // Is Debug
packet.write_boolean(true)?; // Is Flat packet.write_boolean(true)?; // Is Flat
packet.write_boolean(false)?; // Has death location packet.write_boolean(false)?; // Has death location
packet.write_varint(20)?; // Portal cooldown packet.write_varint(20)?; // Portal cooldown
packet.write_varint(60)?; // Sea level 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<ClientContext>, event: u8, value: f32) -> Result<(), ServerError> { pub fn send_game_event(
let mut packet = Packet::empty(clientbound::play::GAME_EVENT); client: Arc<ClientContext>,
event: u8,
value: f32,
) -> Result<(), ServerError> {
let mut packet = Packet::empty(clientbound::play::GAME_EVENT);
packet.write_byte(event)?; packet.write_byte(event)?;
packet.write_float(value)?; packet.write_float(value)?;
client.write_packet(&packet) client.write_packet(&packet)
} }
pub fn sync_player_pos( pub fn sync_player_pos(
client: Arc<ClientContext>, client: Arc<ClientContext>,
x: f64, x: f64,
y: f64, y: f64,
z: f64, z: f64,
vel_x: f64, vel_x: f64,
vel_y: f64, vel_y: f64,
vel_z: f64, vel_z: f64,
yaw: f32, yaw: f32,
pitch: f32, pitch: f32,
flags: i32 flags: i32,
) -> Result<(), ServerError> { ) -> 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_varint(timestamp)?;
packet.write_double(x)?; packet.write_double(x)?;
packet.write_double(y)?; packet.write_double(y)?;
packet.write_double(z)?; packet.write_double(z)?;
packet.write_double(vel_x)?; packet.write_double(vel_x)?;
packet.write_double(vel_y)?; packet.write_double(vel_y)?;
packet.write_double(vel_z)?; packet.write_double(vel_z)?;
packet.write_float(yaw)?; packet.write_float(yaw)?;
packet.write_float(pitch)?; packet.write_float(pitch)?;
packet.write_int(flags)?; packet.write_int(flags)?;
client.write_packet(&packet)?; client.write_packet(&packet)?;
Ok(()) Ok(())
} }
pub fn set_center_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> { pub fn set_center_chunk(client: Arc<ClientContext>, 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(x)?;
packet.write_varint(z)?; packet.write_varint(z)?;
client.write_packet(&packet) client.write_packet(&packet)
} }
pub fn send_example_chunk(client: Arc<ClientContext>, x: i32, z: i32) -> Result<(), ServerError> { pub fn send_example_chunk(client: Arc<ClientContext>, 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(x)?;
packet.write_int(z)?; packet.write_int(z)?;
// heightmap // heightmap
packet.write_varint(1)?; // heightmaps count packet.write_varint(1)?; // heightmaps count
packet.write_varint(0)?; // MOTION_BLOCKING packet.write_varint(0)?; // MOTION_BLOCKING
packet.write_varint(256)?; // Length of the following long array (16 * 16 = 256) packet.write_varint(256)?; // Length of the following long array (16 * 16 = 256)
for _ in 0..256 { for _ in 0..256 {
packet.write_long(0)?; // height - 0 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 { for _ in 0..4 {
chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks chunk_data.write_short(4096)?; // non-air blocks count, 16 * 16 * 16 = 4096 stone blocks
// blocks paletted container // blocks paletted container
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format 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) chunk_data.write_varint(1)?; // block state id in the registry (1 for stone)
// biomes palleted container // biomes palleted container
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
chunk_data.write_varint(27)?; // biome id in the registry chunk_data.write_varint(27)?; // biome id in the registry
} }
// air chunk sections // air chunk sections
for _ in 0..20 { for _ in 0..20 {
chunk_data.write_short(0)?; // non-air blocks count, 0 chunk_data.write_short(0)?; // non-air blocks count, 0
// blocks paletted container // blocks paletted container
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format 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) chunk_data.write_varint(0)?; // block state id in the registry (0 for air)
// biomes palleted container // biomes palleted container
chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format chunk_data.write_byte(0)?; // Bits Per Entry, use Single valued palette format
chunk_data.write_varint(27)?; // biome id in the registry chunk_data.write_varint(27)?; // biome id in the registry
} }
packet.write_usize_varint(chunk_data.len())?; packet.write_usize_varint(chunk_data.len())?;
packet.write_bytes(&chunk_data)?; 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)?; client.write_packet(&packet)?;
packet.write_byte(0)?;
packet.write_byte(0)?;
packet.write_byte(0)?;
packet.write_byte(0)?;
packet.write_byte(0)?;
Ok(())
client.write_packet(&packet)?;
Ok(())
} }
pub fn send_keep_alive(client: Arc<ClientContext>) -> Result<(), ServerError> { pub fn send_keep_alive(client: Arc<ClientContext>) -> 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); let mut packet = Packet::empty(clientbound::play::KEEP_ALIVE);
packet.write_long(timestamp)?; packet.write_long(timestamp)?;
client.write_packet(&packet)?; 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()?; let timestamp2 = packet.read_long()?;
if timestamp2 != timestamp { if timestamp2 != timestamp {
// Послать клиента нахуй // Послать клиента нахуй
@ -218,103 +236,113 @@ pub fn send_keep_alive(client: Arc<ClientContext>) -> Result<(), ServerError> {
println!("KeepAlive Ok") println!("KeepAlive Ok")
} }
Ok(()) Ok(())
} }
pub fn send_system_message(client: Arc<ClientContext>, message: TextComponent, is_action_bar: bool) -> Result<(), ServerError> { pub fn send_system_message(
let mut packet = Packet::empty(clientbound::play::SYSTEM_CHAT_MESSAGE); client: Arc<ClientContext>,
packet.write_nbt(&message)?; message: TextComponent,
packet.write_boolean(is_action_bar)?; is_action_bar: bool,
client.write_packet(&packet) ) -> 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( pub fn handle_play_state(
client: Arc<ClientContext>, // Контекст клиента client: Arc<ClientContext>, // Контекст клиента
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
thread::spawn({
let client = client.clone();
thread::spawn({ move || {
let client = client.clone(); let _ = client.run_read_loop();
client.close();
}
});
move || { send_login(client.clone())?;
let _ = client.run_read_loop(); sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?;
client.close(); 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())?; thread::spawn({
sync_player_pos(client.clone(), 8.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0)?; let client = client.clone();
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({ move || -> Result<(), ServerError> {
let client = client.clone(); while client.is_alive() {
let mut packet = client.read_any_packet()?;
move || -> Result<(), ServerError> { match packet.id() {
while client.is_alive() { serverbound::play::SET_PLAYER_POSITION => {
let mut packet = client.read_any_packet()?; let x = packet.read_double()?;
let y = packet.read_double()?;
let z = packet.read_double()?;
let _ = packet.read_byte()?; // flags
match packet.id() { client.set_position((x, y, z));
serverbound::play::SET_PLAYER_POSITION => { }
let x = packet.read_double()?; serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => {
let y = packet.read_double()?; let x = packet.read_double()?;
let z = packet.read_double()?; let y = packet.read_double()?;
let _ = packet.read_byte()?; // flags 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_position((x, y, z));
}, client.set_rotation((yaw, pitch));
serverbound::play::SET_PLAYER_POSITION_AND_ROTATION => { }
let x = packet.read_double()?; serverbound::play::SET_PLAYER_ROTATION => {
let y = packet.read_double()?; let yaw = packet.read_float()?;
let z = packet.read_double()?; let pitch = packet.read_float()?;
let yaw = packet.read_float()?; let _ = packet.read_byte()?; // flags
let pitch = packet.read_float()?;
let _ = packet.read_byte()?; // flags
client.set_position((x, y, z)); client.set_rotation((yaw, pitch));
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.push_packet_back(packet); client.push_packet_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}"); println!("{ticks_alive}");
if ticks_alive % 200 == 0 { // 10 secs timer if ticks_alive % 200 == 0 {
send_keep_alive(client.clone())?; // 10 secs timer
} send_keep_alive(client.clone())?;
}
if ticks_alive % 20 == 0 { // 1 sec timer if ticks_alive % 20 == 0 {
let (x, y, z) = client.position(); // 1 sec timer
let (x, y, z) = client.position();
send_system_message(client.clone(), send_system_message(
TextComponent::rainbow(format!( client.clone(),
"Pos: {} {} {}", x as i64, y as i64, z as i64 TextComponent::rainbow(format!("Pos: {} {} {}", x as i64, y as i64, z as i64)),
)), false)?; 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 thread::sleep(Duration::from_millis(50)); // 1 tick
ticks_alive += 1; ticks_alive += 1;
} }
println!("Client die"); println!("Client die");
Ok(()) Ok(())
} }