rustfmt
This commit is contained in:
parent
800e2ba5ca
commit
d0968d1c34
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,4 +1,5 @@
|
||||
{
|
||||
"editor.fontFamily": "Fira Code",
|
||||
"editor.fontLigatures": true
|
||||
"editor.fontLigatures": true,
|
||||
"editor.tabSize": 2,
|
||||
}
|
206
src/main.rs
206
src/main.rs
@ -3,18 +3,28 @@ use std::{env::args, path::PathBuf, sync::Arc};
|
||||
use log::{debug, error, info};
|
||||
use rust_mc_proto::Packet;
|
||||
use server::{
|
||||
config::Config, context::ServerContext, data::text_component::TextComponent, event::{Listener, PacketHandler}, player::context::ClientContext, protocol::ConnectionState, start_server, ServerError
|
||||
ServerError,
|
||||
config::Config,
|
||||
context::ServerContext,
|
||||
data::text_component::TextComponent,
|
||||
event::{Listener, PacketHandler},
|
||||
player::context::ClientContext,
|
||||
protocol::ConnectionState,
|
||||
start_server,
|
||||
};
|
||||
|
||||
pub mod server;
|
||||
|
||||
|
||||
struct ExampleListener;
|
||||
|
||||
impl Listener for ExampleListener {
|
||||
fn on_status(&self, client: Arc<ClientContext>, response: &mut String) -> Result<(), ServerError> {
|
||||
*response = format!(
|
||||
"{{
|
||||
fn on_status(
|
||||
&self,
|
||||
client: Arc<ClientContext>,
|
||||
response: &mut String,
|
||||
) -> Result<(), ServerError> {
|
||||
*response = format!(
|
||||
"{{
|
||||
\"version\": {{
|
||||
\"name\": \"idk\",
|
||||
\"protocol\": {}
|
||||
@ -33,112 +43,124 @@ impl Listener for ExampleListener {
|
||||
\"favicon\": \"data:image/png;base64,<data>\",
|
||||
\"enforcesSecureChat\": false
|
||||
}}",
|
||||
client.handshake().unwrap().protocol_version,
|
||||
TextComponent::builder()
|
||||
.text("Hello World! ")
|
||||
.extra(vec![
|
||||
TextComponent::builder()
|
||||
.text("Protocol: ")
|
||||
.color("gold")
|
||||
.extra(vec![
|
||||
TextComponent::builder()
|
||||
.text(&client.handshake().unwrap().protocol_version.to_string())
|
||||
.underlined(true)
|
||||
.build()
|
||||
])
|
||||
.build(),
|
||||
TextComponent::builder()
|
||||
.text("\nServer Addr: ")
|
||||
.color("green")
|
||||
.extra(vec![
|
||||
TextComponent::builder()
|
||||
.text(&format!("{}:{}",
|
||||
client.handshake().unwrap().server_address,
|
||||
client.handshake().unwrap().server_port
|
||||
))
|
||||
.underlined(true)
|
||||
.build()
|
||||
])
|
||||
.build()
|
||||
])
|
||||
.build()
|
||||
.as_json()?
|
||||
);
|
||||
client.handshake().unwrap().protocol_version,
|
||||
TextComponent::builder()
|
||||
.text("Hello World! ")
|
||||
.extra(vec![
|
||||
TextComponent::builder()
|
||||
.text("Protocol: ")
|
||||
.color("gold")
|
||||
.extra(vec![
|
||||
TextComponent::builder()
|
||||
.text(&client.handshake().unwrap().protocol_version.to_string())
|
||||
.underlined(true)
|
||||
.build()
|
||||
])
|
||||
.build(),
|
||||
TextComponent::builder()
|
||||
.text("\nServer Addr: ")
|
||||
.color("green")
|
||||
.extra(vec![
|
||||
TextComponent::builder()
|
||||
.text(&format!(
|
||||
"{}:{}",
|
||||
client.handshake().unwrap().server_address,
|
||||
client.handshake().unwrap().server_port
|
||||
))
|
||||
.underlined(true)
|
||||
.build()
|
||||
])
|
||||
.build()
|
||||
])
|
||||
.build()
|
||||
.as_json()?
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ExamplePacketHandler;
|
||||
|
||||
impl PacketHandler for ExamplePacketHandler {
|
||||
fn on_incoming_packet(
|
||||
&self,
|
||||
client: Arc<ClientContext>,
|
||||
packet: &mut Packet,
|
||||
_: &mut bool,
|
||||
state: ConnectionState
|
||||
) -> Result<(), ServerError> {
|
||||
debug!("{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len());
|
||||
fn on_incoming_packet(
|
||||
&self,
|
||||
client: Arc<ClientContext>,
|
||||
packet: &mut Packet,
|
||||
_: &mut bool,
|
||||
state: ConnectionState,
|
||||
) -> Result<(), ServerError> {
|
||||
debug!(
|
||||
"{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes",
|
||||
client.addr.clone(),
|
||||
packet.id(),
|
||||
state,
|
||||
packet.len()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_outcoming_packet(
|
||||
&self,
|
||||
client: Arc<ClientContext>,
|
||||
packet: &mut Packet,
|
||||
_: &mut bool,
|
||||
state: ConnectionState
|
||||
) -> Result<(), ServerError> {
|
||||
debug!("{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len());
|
||||
fn on_outcoming_packet(
|
||||
&self,
|
||||
client: Arc<ClientContext>,
|
||||
packet: &mut Packet,
|
||||
_: &mut bool,
|
||||
state: ConnectionState,
|
||||
) -> Result<(), ServerError> {
|
||||
debug!(
|
||||
"{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes",
|
||||
client.addr.clone(),
|
||||
packet.id(),
|
||||
state,
|
||||
packet.len()
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
// Инициализируем логи
|
||||
// Чтобы читать debug-логи, юзаем `RUST_LOG=debug cargo run`
|
||||
colog::init();
|
||||
// Инициализируем логи
|
||||
// Чтобы читать debug-логи, юзаем `RUST_LOG=debug cargo run`
|
||||
colog::init();
|
||||
|
||||
// Получение аргументов
|
||||
let exec = args().next().expect("Неизвестная система");
|
||||
let args = args().skip(1).collect::<Vec<String>>();
|
||||
// Получение аргументов
|
||||
let exec = args().next().expect("Неизвестная система");
|
||||
let args = args().skip(1).collect::<Vec<String>>();
|
||||
|
||||
if args.len() > 1 {
|
||||
info!("Использование: {exec} [путь до файла конфигурации]");
|
||||
return;
|
||||
}
|
||||
if args.len() > 1 {
|
||||
info!("Использование: {exec} [путь до файла конфигурации]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Берем путь из аргумента либо по дефолту берем "./server.toml"
|
||||
let config_path = PathBuf::from(args.get(0).unwrap_or(&"server.toml".to_string()));
|
||||
// Берем путь из аргумента либо по дефолту берем "./server.toml"
|
||||
let config_path = PathBuf::from(args.get(0).unwrap_or(&"server.toml".to_string()));
|
||||
|
||||
// Чтение конфига, если ошибка - выводим
|
||||
let config = match Config::load_from_file(config_path) {
|
||||
Some(config) => config,
|
||||
None => {
|
||||
error!("Ошибка чтения конфигурации");
|
||||
return;
|
||||
},
|
||||
};
|
||||
// Чтение конфига, если ошибка - выводим
|
||||
let config = match Config::load_from_file(config_path) {
|
||||
Some(config) => config,
|
||||
None => {
|
||||
error!("Ошибка чтения конфигурации");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Делаем немутабельную потокобезопасную ссылку на конфиг
|
||||
// Впринципе можно и просто клонировать сам конфиг в каждый сука поток ебать того рот ебать блять
|
||||
// но мы этого делать не будем чтобы не было мемори лик лишнего
|
||||
let config = Arc::new(config);
|
||||
// Делаем немутабельную потокобезопасную ссылку на конфиг
|
||||
// Впринципе можно и просто клонировать сам конфиг в каждый сука поток ебать того рот ебать блять
|
||||
// но мы этого делать не будем чтобы не было мемори лик лишнего
|
||||
let config = Arc::new(config);
|
||||
|
||||
// Создаем контекст сервера
|
||||
// Передается во все подключения
|
||||
let mut server = ServerContext::new(config);
|
||||
// Создаем контекст сервера
|
||||
// Передается во все подключения
|
||||
let mut server = ServerContext::new(config);
|
||||
|
||||
server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера
|
||||
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
|
||||
server.add_listener(Box::new(ExampleListener)); // Добавляем пример листенера
|
||||
server.add_packet_handler(Box::new(ExamplePacketHandler)); // Добавляем пример пакет хандлера
|
||||
|
||||
// Бетонируем сервер контекст от изменений
|
||||
let server = Arc::new(server);
|
||||
// Бетонируем сервер контекст от изменений
|
||||
let server = Arc::new(server);
|
||||
|
||||
// Запускаем сервер из специально отведенной под это дело функцией
|
||||
start_server(server);
|
||||
// Запускаем сервер из специально отведенной под это дело функцией
|
||||
start_server(server);
|
||||
}
|
||||
|
@ -3,28 +3,39 @@ use std::{fs, path::PathBuf};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_default::DefaultFromSerde;
|
||||
|
||||
|
||||
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
|
||||
pub struct BindConfig {
|
||||
#[serde(default = "default_host")] pub host: String,
|
||||
#[serde(default = "default_timeout")] pub timeout: u64,
|
||||
#[serde(default = "default_host")]
|
||||
pub host: String,
|
||||
#[serde(default = "default_timeout")]
|
||||
pub timeout: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
|
||||
pub struct ServerConfig {
|
||||
#[serde(default)] pub online_mode: bool,
|
||||
#[serde(default = "default_compression")] pub compression_threshold: Option<usize>,
|
||||
#[serde(default)]
|
||||
pub online_mode: bool,
|
||||
#[serde(default = "default_compression")]
|
||||
pub compression_threshold: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, DefaultFromSerde, Serialize, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
#[serde(default)] pub bind: BindConfig,
|
||||
#[serde(default)] pub server: ServerConfig,
|
||||
#[serde(default)]
|
||||
pub bind: BindConfig,
|
||||
#[serde(default)]
|
||||
pub server: ServerConfig,
|
||||
}
|
||||
|
||||
fn default_host() -> String { "127.0.0.1:25565".to_string() }
|
||||
fn default_timeout() -> u64 { 5 }
|
||||
fn default_compression() -> Option<usize> { Some(256) }
|
||||
fn default_host() -> String {
|
||||
"127.0.0.1:25565".to_string()
|
||||
}
|
||||
fn default_timeout() -> u64 {
|
||||
5
|
||||
}
|
||||
fn default_compression() -> Option<usize> {
|
||||
Some(256)
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn load_from_file(path: PathBuf) -> Option<Config> {
|
||||
|
@ -4,7 +4,11 @@ use dashmap::DashMap;
|
||||
use itertools::Itertools;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{config::Config, event::{Listener, PacketHandler}, player::context::ClientContext};
|
||||
use super::{
|
||||
config::Config,
|
||||
event::{Listener, PacketHandler},
|
||||
player::context::ClientContext,
|
||||
};
|
||||
|
||||
// Контекст сервера
|
||||
// Должен быть обернут в Arc для передачи между потоками
|
||||
@ -12,7 +16,7 @@ pub struct ServerContext {
|
||||
pub config: Arc<Config>,
|
||||
pub clients: DashMap<SocketAddr, Arc<ClientContext>>,
|
||||
listeners: Vec<Box<dyn Listener>>,
|
||||
handlers: Vec<Box<dyn PacketHandler>>
|
||||
handlers: Vec<Box<dyn PacketHandler>>,
|
||||
}
|
||||
|
||||
impl ServerContext {
|
||||
@ -21,12 +25,13 @@ impl ServerContext {
|
||||
config,
|
||||
listeners: Vec::new(),
|
||||
handlers: Vec::new(),
|
||||
clients: DashMap::new()
|
||||
clients: DashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_player_by_uuid(self: &Arc<Self>, uuid: Uuid) -> Option<Arc<ClientContext>> {
|
||||
self.clients.iter()
|
||||
self.clients
|
||||
.iter()
|
||||
.find(|o| {
|
||||
let info = o.player_info();
|
||||
if let Some(info) = info {
|
||||
@ -39,7 +44,8 @@ impl ServerContext {
|
||||
}
|
||||
|
||||
pub fn get_player_by_name(self: &Arc<Self>, name: &str) -> Option<Arc<ClientContext>> {
|
||||
self.clients.iter()
|
||||
self.clients
|
||||
.iter()
|
||||
.find(|o| {
|
||||
let info = o.player_info();
|
||||
if let Some(info) = info {
|
||||
@ -52,7 +58,8 @@ impl ServerContext {
|
||||
}
|
||||
|
||||
pub fn players(self: &Arc<Self>) -> Vec<Arc<ClientContext>> {
|
||||
self.clients.iter()
|
||||
self.clients
|
||||
.iter()
|
||||
.filter(|o| o.player_info().is_some())
|
||||
.map(|o| o.clone())
|
||||
.collect()
|
||||
@ -66,24 +73,18 @@ impl ServerContext {
|
||||
self.listeners.push(listener);
|
||||
}
|
||||
|
||||
pub fn packet_handlers<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
|
||||
K: Ord,
|
||||
F: FnMut(&&Box<dyn PacketHandler>) -> K
|
||||
F: FnMut(&&Box<dyn PacketHandler>) -> K,
|
||||
{
|
||||
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
|
||||
K: Ord,
|
||||
F: FnMut(&&Box<dyn Listener>) -> K
|
||||
F: FnMut(&&Box<dyn Listener>) -> K,
|
||||
{
|
||||
self.listeners.iter().sorted_by_key(sort_by).collect_vec()
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ use crate::server::ServerError;
|
||||
|
||||
use super::ReadWriteNBT;
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
#[skip_serializing_none]
|
||||
pub struct TextComponent {
|
||||
@ -34,7 +33,7 @@ impl TextComponent {
|
||||
underlined: None,
|
||||
strikethrough: None,
|
||||
obfuscated: None,
|
||||
extra: None
|
||||
extra: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +42,8 @@ impl TextComponent {
|
||||
return TextComponent::new(text);
|
||||
}
|
||||
|
||||
let children = text.char_indices()
|
||||
let children = text
|
||||
.char_indices()
|
||||
.map(|(i, c)| {
|
||||
let hue = (i as f32) / (text.chars().count() as f32) * 360.0;
|
||||
let hsl = Hsl::new(hue, 1.0, 0.5);
|
||||
@ -67,13 +67,11 @@ impl TextComponent {
|
||||
}
|
||||
|
||||
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> {
|
||||
serde_json::from_str(text)
|
||||
.map_err(|_| ServerError::DeTextComponent)
|
||||
serde_json::from_str(text).map_err(|_| ServerError::DeTextComponent)
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +155,7 @@ impl TextComponentBuilder {
|
||||
underlined: self.underlined,
|
||||
strikethrough: self.strikethrough,
|
||||
obfuscated: self.obfuscated,
|
||||
extra: self.extra
|
||||
extra: self.extra,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -167,15 +165,18 @@ impl ReadWriteNBT<TextComponent> for Packet {
|
||||
fn read_nbt(&mut self) -> Result<TextComponent, ServerError> {
|
||||
let mut data = Vec::new();
|
||||
let pos = self.get_ref().position();
|
||||
self.get_mut().read_to_end(&mut data).map_err(|_| ServerError::DeTextComponent)?;
|
||||
let (remaining, value) = craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?;
|
||||
self.get_mut().set_position(pos + (data.len() - remaining.len()) as u64);
|
||||
self.get_mut()
|
||||
.read_to_end(&mut data)
|
||||
.map_err(|_| ServerError::DeTextComponent)?;
|
||||
let (remaining, value) =
|
||||
craftflow_nbt::from_slice(&data).map_err(|_| ServerError::DeTextComponent)?;
|
||||
self.get_mut()
|
||||
.set_position(pos + (data.len() - remaining.len()) as u64);
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> {
|
||||
craftflow_nbt::to_writer(self.get_mut(), val)
|
||||
.map_err(|_| ServerError::SerTextComponent)?;
|
||||
craftflow_nbt::to_writer(self.get_mut(), val).map_err(|_| ServerError::SerTextComponent)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -8,10 +8,10 @@ use protocol::handler::handle_connection;
|
||||
use rust_mc_proto::{MinecraftConnection, ProtocolError};
|
||||
|
||||
pub mod config;
|
||||
pub mod context;
|
||||
pub mod data;
|
||||
pub mod event;
|
||||
pub mod player;
|
||||
pub mod context;
|
||||
pub mod protocol;
|
||||
|
||||
// Ошибки сервера
|
||||
@ -19,11 +19,11 @@ pub mod protocol;
|
||||
pub enum ServerError {
|
||||
UnexpectedPacket, // Неожиданный пакет
|
||||
Protocol(ProtocolError), // Ошибка в протоколе при работе с rust_mc_proto
|
||||
ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection
|
||||
SerTextComponent, // Ошибка при сериализации текст-компонента
|
||||
DeTextComponent, // Ошибка при десериализации текст-компонента
|
||||
UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper)
|
||||
Other(String) // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки
|
||||
ConnectionClosed, // Соединение закрыто, единственная ошибка которая не логируется у handle_connection
|
||||
SerTextComponent, // Ошибка при сериализации текст-компонента
|
||||
DeTextComponent, // Ошибка при десериализации текст-компонента
|
||||
UnexpectedState, // Указывает на то что этот пакет не может быть отправлен в данном режиме (в основном через ProtocolHelper)
|
||||
Other(String), // Другая ошибка, либо очень специфичная, либо хз, лучше не использовать и создавать новое поле ошибки
|
||||
}
|
||||
|
||||
impl Display for ServerError {
|
||||
@ -39,21 +39,20 @@ impl From<ProtocolError> for ServerError {
|
||||
fn from(error: ProtocolError) -> ServerError {
|
||||
match error {
|
||||
// Если просто закрыто соединение, переделываем в нашу ошибку этого
|
||||
ProtocolError::ConnectionClosedError => {
|
||||
ServerError::ConnectionClosed
|
||||
},
|
||||
ProtocolError::ConnectionClosedError => ServerError::ConnectionClosed,
|
||||
// Все остальное просто засовываем в обертку
|
||||
error => {
|
||||
ServerError::Protocol(error)
|
||||
},
|
||||
error => ServerError::Protocol(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_server(server: Arc<ServerContext>) {
|
||||
// Биндим сервер где надо
|
||||
let Ok(listener) = TcpListener::bind(&server.config.bind.host) else {
|
||||
error!("Не удалось забиндить сервер на {}", &server.config.bind.host);
|
||||
let Ok(listener) = TcpListener::bind(&server.config.bind.host) else {
|
||||
error!(
|
||||
"Не удалось забиндить сервер на {}",
|
||||
&server.config.bind.host
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
@ -67,8 +66,12 @@ pub fn start_server(server: Arc<ServerContext>) {
|
||||
|
||||
// Установка таймаутов на чтение и запись
|
||||
// По умолчанию пусть будет 5 секунд, надо будет сделать настройку через конфиг
|
||||
stream.set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout))).ignore();
|
||||
stream.set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout))).ignore();
|
||||
stream
|
||||
.set_read_timeout(Some(Duration::from_secs(server.config.bind.timeout)))
|
||||
.ignore();
|
||||
stream
|
||||
.set_write_timeout(Some(Duration::from_secs(server.config.bind.timeout)))
|
||||
.ignore();
|
||||
|
||||
// Оборачиваем стрим в майнкрафт конекшн лично для нашего удовольствия
|
||||
let conn = MinecraftConnection::new(stream);
|
||||
@ -84,11 +87,11 @@ pub fn start_server(server: Arc<ServerContext>) {
|
||||
// Обработка подключения
|
||||
// Если ошибка -> выводим
|
||||
match handle_connection(client.clone()) {
|
||||
Ok(_) => {},
|
||||
Err(ServerError::ConnectionClosed) => {},
|
||||
Ok(_) => {}
|
||||
Err(ServerError::ConnectionClosed) => {}
|
||||
Err(error) => {
|
||||
error!("Ошибка подключения: {error:?}");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Удаляем клиента из списка клиентов
|
||||
|
@ -1,9 +1,13 @@
|
||||
use std::{hash::Hash, net::{SocketAddr, TcpStream}, sync::{Arc, RwLock}};
|
||||
use std::{
|
||||
hash::Hash,
|
||||
net::{SocketAddr, TcpStream},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use rust_mc_proto::{MinecraftConnection, Packet};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::server::{context::ServerContext, protocol::ConnectionState, ServerError};
|
||||
use crate::server::{ServerError, context::ServerContext, protocol::ConnectionState};
|
||||
|
||||
use super::helper::ProtocolHelper;
|
||||
|
||||
@ -16,7 +20,7 @@ pub struct ClientContext {
|
||||
handshake: RwLock<Option<Handshake>>,
|
||||
client_info: RwLock<Option<ClientInfo>>,
|
||||
player_info: RwLock<Option<PlayerInfo>>,
|
||||
state: RwLock<ConnectionState>
|
||||
state: RwLock<ConnectionState>,
|
||||
}
|
||||
|
||||
// Реализуем сравнение через адрес
|
||||
@ -36,10 +40,7 @@ impl Hash for ClientContext {
|
||||
impl Eq for ClientContext {}
|
||||
|
||||
impl ClientContext {
|
||||
pub fn new(
|
||||
server: Arc<ServerContext>,
|
||||
conn: MinecraftConnection<TcpStream>
|
||||
) -> ClientContext {
|
||||
pub fn new(server: Arc<ServerContext>, conn: MinecraftConnection<TcpStream>) -> ClientContext {
|
||||
ClientContext {
|
||||
server,
|
||||
addr: conn.get_ref().peer_addr().unwrap(),
|
||||
@ -47,7 +48,7 @@ impl ClientContext {
|
||||
handshake: RwLock::new(None),
|
||||
client_info: RwLock::new(None),
|
||||
player_info: RwLock::new(None),
|
||||
state: RwLock::new(ConnectionState::Handshake)
|
||||
state: RwLock::new(ConnectionState::Handshake),
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,9 +67,11 @@ impl ClientContext {
|
||||
pub fn set_state(self: &Arc<Self>, state: ConnectionState) -> Result<(), ServerError> {
|
||||
*self.state.write().unwrap() = state.clone();
|
||||
|
||||
for handler in self.server.packet_handlers(
|
||||
|o| o.on_state_priority()
|
||||
).iter() {
|
||||
for handler in self
|
||||
.server
|
||||
.packet_handlers(|o| o.on_state_priority())
|
||||
.iter()
|
||||
{
|
||||
handler.on_state(self.clone(), state.clone())?;
|
||||
}
|
||||
|
||||
@ -95,10 +98,17 @@ impl ClientContext {
|
||||
let state = self.state();
|
||||
let mut packet = packet.clone();
|
||||
let mut cancelled = false;
|
||||
for handler in self.server.packet_handlers(
|
||||
|o| o.on_outcoming_packet_priority()
|
||||
).iter() {
|
||||
handler.on_outcoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?;
|
||||
for handler in self
|
||||
.server
|
||||
.packet_handlers(|o| o.on_outcoming_packet_priority())
|
||||
.iter()
|
||||
{
|
||||
handler.on_outcoming_packet(
|
||||
self.clone(),
|
||||
&mut packet,
|
||||
&mut cancelled,
|
||||
state.clone(),
|
||||
)?;
|
||||
packet.get_mut().set_position(0);
|
||||
}
|
||||
if !cancelled {
|
||||
@ -115,10 +125,17 @@ impl ClientContext {
|
||||
loop {
|
||||
let mut packet = conn.read_packet()?;
|
||||
let mut cancelled = false;
|
||||
for handler in self.server.packet_handlers(
|
||||
|o| o.on_incoming_packet_priority()
|
||||
).iter() {
|
||||
handler.on_incoming_packet(self.clone(), &mut packet, &mut cancelled, state.clone())?;
|
||||
for handler in self
|
||||
.server
|
||||
.packet_handlers(|o| o.on_incoming_packet_priority())
|
||||
.iter()
|
||||
{
|
||||
handler.on_incoming_packet(
|
||||
self.clone(),
|
||||
&mut packet,
|
||||
&mut cancelled,
|
||||
state.clone(),
|
||||
)?;
|
||||
packet.get_mut().set_position(0);
|
||||
}
|
||||
if !cancelled {
|
||||
@ -167,11 +184,11 @@ pub struct ClientInfo {
|
||||
pub main_hand: i32,
|
||||
pub enable_text_filtering: bool,
|
||||
pub allow_server_listings: bool,
|
||||
pub particle_status: i32
|
||||
pub particle_status: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerInfo {
|
||||
pub name: String,
|
||||
pub uuid: Uuid
|
||||
pub uuid: Uuid,
|
||||
}
|
||||
|
@ -1,12 +1,22 @@
|
||||
use std::{io::Read, sync::Arc, time::{Duration, SystemTime}};
|
||||
use std::{
|
||||
io::Read,
|
||||
sync::Arc,
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
||||
|
||||
use crate::server::{data::{text_component::TextComponent, ReadWriteNBT}, protocol::{id::{clientbound, serverbound}, *}, ServerError};
|
||||
use crate::server::{
|
||||
ServerError,
|
||||
data::{ReadWriteNBT, text_component::TextComponent},
|
||||
protocol::{
|
||||
id::{clientbound, serverbound},
|
||||
*,
|
||||
},
|
||||
};
|
||||
|
||||
use super::context::ClientContext;
|
||||
|
||||
|
||||
// Помощник в работе с протоколом
|
||||
// Может быть использован где угодно, но сделан именно для листенеров и пакет хандлеров
|
||||
// Через него удобно делать всякую одинаковую херь
|
||||
@ -16,14 +26,14 @@ use super::context::ClientContext;
|
||||
// Почему бы и нет если да
|
||||
pub struct ProtocolHelper {
|
||||
client: Arc<ClientContext>,
|
||||
state: ConnectionState
|
||||
state: ConnectionState,
|
||||
}
|
||||
|
||||
impl ProtocolHelper {
|
||||
pub fn new(client: Arc<ClientContext>) -> Self {
|
||||
Self {
|
||||
state: client.state(),
|
||||
client
|
||||
client,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,12 +41,14 @@ impl ProtocolHelper {
|
||||
pub fn leave_configuration(&self) -> Result<(), ServerError> {
|
||||
match self.state {
|
||||
ConnectionState::Configuration => {
|
||||
self.client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
|
||||
self.client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
|
||||
self.client
|
||||
.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
|
||||
self.client
|
||||
.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
|
||||
self.client.set_state(ConnectionState::Play)?;
|
||||
Ok(())
|
||||
},
|
||||
_ => Err(ServerError::UnexpectedState)
|
||||
}
|
||||
_ => Err(ServerError::UnexpectedState),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,12 +56,14 @@ impl ProtocolHelper {
|
||||
pub fn enter_configuration(&self) -> Result<(), ServerError> {
|
||||
match self.state {
|
||||
ConnectionState::Play => {
|
||||
self.client.write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?;
|
||||
self.client.read_packet(serverbound::play::ACKNOWLEDGE_CONFIGURATION)?;
|
||||
self.client
|
||||
.write_packet(&Packet::empty(clientbound::play::START_CONFIGURATION))?;
|
||||
self.client
|
||||
.read_packet(serverbound::play::ACKNOWLEDGE_CONFIGURATION)?;
|
||||
self.client.set_state(ConnectionState::Configuration)?;
|
||||
Ok(())
|
||||
},
|
||||
_ => Err(ServerError::UnexpectedState)
|
||||
}
|
||||
_ => Err(ServerError::UnexpectedState),
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,17 +72,19 @@ impl ProtocolHelper {
|
||||
match self.state {
|
||||
ConnectionState::Play => {
|
||||
let time = SystemTime::now();
|
||||
self.client.write_packet(&Packet::empty(clientbound::play::PING))?;
|
||||
self.client
|
||||
.write_packet(&Packet::empty(clientbound::play::PING))?;
|
||||
self.client.read_packet(serverbound::play::PONG)?;
|
||||
Ok(SystemTime::now().duration_since(time).unwrap())
|
||||
},
|
||||
}
|
||||
ConnectionState::Configuration => {
|
||||
let time = SystemTime::now();
|
||||
self.client.write_packet(&Packet::empty(clientbound::configuration::PING))?;
|
||||
self.client
|
||||
.write_packet(&Packet::empty(clientbound::configuration::PING))?;
|
||||
self.client.read_packet(serverbound::configuration::PONG)?;
|
||||
Ok(SystemTime::now().duration_since(time).unwrap())
|
||||
},
|
||||
_ => Err(ServerError::UnexpectedState)
|
||||
}
|
||||
_ => Err(ServerError::UnexpectedState),
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,21 +93,21 @@ impl ProtocolHelper {
|
||||
ConnectionState::Login => {
|
||||
let text = reason.as_json()?;
|
||||
Packet::build(0x00, |p| p.write_string(&text))?
|
||||
},
|
||||
}
|
||||
ConnectionState::Configuration => {
|
||||
let mut packet = Packet::empty(0x02);
|
||||
packet.write_nbt(&reason)?;
|
||||
packet
|
||||
},
|
||||
}
|
||||
ConnectionState::Play => {
|
||||
let mut packet = Packet::empty(0x1C);
|
||||
packet.write_nbt(&reason)?;
|
||||
packet
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
self.client.close();
|
||||
return Ok(())
|
||||
},
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
self.client.write_packet(&packet)?;
|
||||
Ok(())
|
||||
@ -105,7 +121,9 @@ impl ProtocolHelper {
|
||||
packet.write_string(id)?;
|
||||
self.client.write_packet(&packet)?;
|
||||
|
||||
let mut packet = self.client.read_packet(serverbound::configuration::COOKIE_RESPONSE)?;
|
||||
let mut packet = self
|
||||
.client
|
||||
.read_packet(serverbound::configuration::COOKIE_RESPONSE)?;
|
||||
packet.read_string()?;
|
||||
let data = if packet.read_boolean()? {
|
||||
let n = packet.read_usize_varint()?;
|
||||
@ -115,13 +133,18 @@ impl ProtocolHelper {
|
||||
};
|
||||
|
||||
Ok(data)
|
||||
},
|
||||
_ => Err(ServerError::UnexpectedState)
|
||||
}
|
||||
_ => Err(ServerError::UnexpectedState),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns login plugin response - (message_id, payload)
|
||||
pub fn send_login_plugin_request(&self, id: i32, channel: &str, data: &[u8]) -> Result<(i32, Option<Vec<u8>>), ServerError> {
|
||||
pub fn send_login_plugin_request(
|
||||
&self,
|
||||
id: i32,
|
||||
channel: &str,
|
||||
data: &[u8],
|
||||
) -> Result<(i32, Option<Vec<u8>>), ServerError> {
|
||||
match self.state {
|
||||
ConnectionState::Login => {
|
||||
let mut packet = Packet::empty(clientbound::login::PLUGIN_REQUEST);
|
||||
@ -130,7 +153,9 @@ impl ProtocolHelper {
|
||||
packet.write_bytes(data)?;
|
||||
self.client.write_packet(&packet)?;
|
||||
|
||||
let mut packet = self.client.read_packet(serverbound::login::PLUGIN_RESPONSE)?;
|
||||
let mut packet = self
|
||||
.client
|
||||
.read_packet(serverbound::login::PLUGIN_RESPONSE)?;
|
||||
let identifier = packet.read_varint()?;
|
||||
let data = if packet.read_boolean()? {
|
||||
let mut data = Vec::new();
|
||||
@ -141,16 +166,18 @@ impl ProtocolHelper {
|
||||
};
|
||||
|
||||
Ok((identifier, data))
|
||||
},
|
||||
_ => Err(ServerError::UnexpectedState)
|
||||
}
|
||||
_ => Err(ServerError::UnexpectedState),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_plugin_message(&self, channel: &str, data: &[u8]) -> Result<(), ServerError> {
|
||||
let mut packet = match self.state {
|
||||
ConnectionState::Configuration => Packet::empty(clientbound::configuration::PLUGIN_MESSAGE),
|
||||
ConnectionState::Configuration => {
|
||||
Packet::empty(clientbound::configuration::PLUGIN_MESSAGE)
|
||||
}
|
||||
ConnectionState::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE),
|
||||
_ => return Err(ServerError::UnexpectedState)
|
||||
_ => return Err(ServerError::UnexpectedState),
|
||||
};
|
||||
packet.write_string(channel)?;
|
||||
packet.write_bytes(data)?;
|
||||
|
@ -1,160 +1,177 @@
|
||||
use std::{io::Read, sync::Arc};
|
||||
|
||||
use crate::server::{player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo}, ServerError};
|
||||
use crate::server::{
|
||||
ServerError,
|
||||
player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo},
|
||||
};
|
||||
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
||||
|
||||
use crate::trigger_event;
|
||||
|
||||
use super::{id::*, play::handle_play_state, ConnectionState};
|
||||
|
||||
use super::{ConnectionState, id::*, play::handle_play_state};
|
||||
|
||||
pub fn handle_connection(
|
||||
client: Arc<ClientContext>, // Контекст клиента
|
||||
client: Arc<ClientContext>, // Контекст клиента
|
||||
) -> Result<(), ServerError> {
|
||||
// Чтение рукопожатия
|
||||
// Получение пакетов производится через client.conn(),
|
||||
// ВАЖНО: не помещать сам client.conn() в переменные,
|
||||
// он должен сразу убиваться иначе соединение гдето задедлочится
|
||||
let mut packet = client.read_packet(serverbound::handshake::HANDSHAKE)?;
|
||||
// Чтение рукопожатия
|
||||
// Получение пакетов производится через client.conn(),
|
||||
// ВАЖНО: не помещать сам client.conn() в переменные,
|
||||
// он должен сразу убиваться иначе соединение гдето задедлочится
|
||||
let mut packet = client.read_packet(serverbound::handshake::HANDSHAKE)?;
|
||||
|
||||
let protocol_version = packet.read_varint()?; // Получаем версия протокола, может быть отрицательным если наш клиент дэбил
|
||||
let server_address = packet.read_string()?; // Получаем домен/адрес сервера к которому пытается подключиться клиент, например "play.example.com", а не айпи
|
||||
let server_port = packet.read_unsigned_short()?; // Все тоже самое что и с адресом сервера и все потому же и за тем же
|
||||
let next_state = packet.read_varint()?; // Тип подключения: 1 для получения статуса и пинга, 2 и 3 для обычного подключения
|
||||
let protocol_version = packet.read_varint()?; // Получаем версия протокола, может быть отрицательным если наш клиент дэбил
|
||||
let server_address = packet.read_string()?; // Получаем домен/адрес сервера к которому пытается подключиться клиент, например "play.example.com", а не айпи
|
||||
let server_port = packet.read_unsigned_short()?; // Все тоже самое что и с адресом сервера и все потому же и за тем же
|
||||
let next_state = packet.read_varint()?; // Тип подключения: 1 для получения статуса и пинга, 2 и 3 для обычного подключения
|
||||
|
||||
client.set_handshake(Handshake { protocol_version, server_address, server_port });
|
||||
client.set_handshake(Handshake {
|
||||
protocol_version,
|
||||
server_address,
|
||||
server_port,
|
||||
});
|
||||
|
||||
match next_state {
|
||||
1 => { // Тип подключения - статус
|
||||
client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status
|
||||
match next_state {
|
||||
1 => {
|
||||
// Тип подключения - статус
|
||||
client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status
|
||||
|
||||
loop {
|
||||
// Чтение запроса
|
||||
let mut packet = client.read_any_packet()?;
|
||||
loop {
|
||||
// Чтение запроса
|
||||
let mut packet = client.read_any_packet()?;
|
||||
|
||||
match packet.id() {
|
||||
serverbound::status::REQUEST => { // Запрос статуса
|
||||
let mut packet = Packet::empty(clientbound::status::RESPONSE);
|
||||
match packet.id() {
|
||||
serverbound::status::REQUEST => {
|
||||
// Запрос статуса
|
||||
let mut packet = Packet::empty(clientbound::status::RESPONSE);
|
||||
|
||||
// Дефолтный статус
|
||||
let mut status = "{
|
||||
// Дефолтный статус
|
||||
let mut status = "{
|
||||
\"version\": {
|
||||
\"name\": \"Error\",
|
||||
\"protocol\": 0
|
||||
},
|
||||
\"description\": {\"text\": \"Internal server error\"}
|
||||
}".to_string();
|
||||
}"
|
||||
.to_string();
|
||||
|
||||
// Опрос всех листенеров
|
||||
trigger_event!(client, status, &mut status);
|
||||
// Опрос всех листенеров
|
||||
trigger_event!(client, status, &mut status);
|
||||
|
||||
// Отправка статуса
|
||||
packet.write_string(&status)?;
|
||||
// Отправка статуса
|
||||
packet.write_string(&status)?;
|
||||
|
||||
client.write_packet(&packet)?;
|
||||
},
|
||||
serverbound::status::PING_REQUEST => { // Пинг
|
||||
// Раньше мы просто отправляли ему его-же пакет, но сейчас,
|
||||
// С приходом к власти констант айди-пакетов, нам приходится делать такое непотребство
|
||||
let timestamp = packet.read_long()?;
|
||||
let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE);
|
||||
packet.write_long(timestamp)?;
|
||||
client.write_packet(&packet)?;
|
||||
},
|
||||
_ => {
|
||||
return Err(ServerError::UnexpectedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
2 => { // Тип подключения - игра
|
||||
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login
|
||||
client.write_packet(&packet)?;
|
||||
}
|
||||
serverbound::status::PING_REQUEST => {
|
||||
// Пинг
|
||||
// Раньше мы просто отправляли ему его-же пакет, но сейчас,
|
||||
// С приходом к власти констант айди-пакетов, нам приходится делать такое непотребство
|
||||
let timestamp = packet.read_long()?;
|
||||
let mut packet = Packet::empty(clientbound::status::PONG_RESPONSE);
|
||||
packet.write_long(timestamp)?;
|
||||
client.write_packet(&packet)?;
|
||||
}
|
||||
_ => {
|
||||
return Err(ServerError::UnexpectedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// Тип подключения - игра
|
||||
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login
|
||||
|
||||
// Читаем пакет Login Start
|
||||
let mut packet = client.read_packet(serverbound::login::START)?;
|
||||
// Читаем пакет Login Start
|
||||
let mut packet = client.read_packet(serverbound::login::START)?;
|
||||
|
||||
let name = packet.read_string()?;
|
||||
let uuid = packet.read_uuid()?;
|
||||
let name = packet.read_string()?;
|
||||
let uuid = packet.read_uuid()?;
|
||||
|
||||
client.set_player_info(PlayerInfo { name: name.clone(), uuid: uuid.clone() });
|
||||
client.set_player_info(PlayerInfo {
|
||||
name: name.clone(),
|
||||
uuid: uuid.clone(),
|
||||
});
|
||||
|
||||
if client.server.config.server.online_mode {
|
||||
// TODO: encryption packets
|
||||
}
|
||||
if client.server.config.server.online_mode {
|
||||
// TODO: encryption packets
|
||||
}
|
||||
|
||||
// Отправляем пакет Set Compression если сжатие указано
|
||||
if let Some(threshold) = client.server.config.server.compression_threshold {
|
||||
client.write_packet(&Packet::build(clientbound::login::SET_COMPRESSION, |p| p.write_usize_varint(threshold))?)?;
|
||||
client.set_compression(Some(threshold)); // Устанавливаем сжатие на соединении
|
||||
}
|
||||
// Отправляем пакет Set Compression если сжатие указано
|
||||
if let Some(threshold) = client.server.config.server.compression_threshold {
|
||||
client.write_packet(&Packet::build(clientbound::login::SET_COMPRESSION, |p| {
|
||||
p.write_usize_varint(threshold)
|
||||
})?)?;
|
||||
client.set_compression(Some(threshold)); // Устанавливаем сжатие на соединении
|
||||
}
|
||||
|
||||
// Отправка пакета Login Success
|
||||
client.write_packet(&Packet::build(clientbound::login::SUCCESS, |p| {
|
||||
p.write_uuid(&uuid)?;
|
||||
p.write_string(&name)?;
|
||||
p.write_varint(0)
|
||||
})?)?;
|
||||
// Отправка пакета Login Success
|
||||
client.write_packet(&Packet::build(clientbound::login::SUCCESS, |p| {
|
||||
p.write_uuid(&uuid)?;
|
||||
p.write_string(&name)?;
|
||||
p.write_varint(0)
|
||||
})?)?;
|
||||
|
||||
client.read_packet(serverbound::login::ACKNOWLEDGED)?; // Пакет Login Acknowledged
|
||||
client.read_packet(serverbound::login::ACKNOWLEDGED)?; // Пакет Login Acknowledged
|
||||
|
||||
client.set_state(ConnectionState::Configuration)?; // Мы перешли в режим Configuration
|
||||
client.set_state(ConnectionState::Configuration)?; // Мы перешли в режим Configuration
|
||||
|
||||
// Получение бренда клиента из Serverbound Plugin Message
|
||||
// Identifier канала откуда берется бренд: minecraft:brand
|
||||
let brand = loop {
|
||||
let mut packet = client.read_packet(serverbound::configuration::PLUGIN_MESSAGE)?; // Пакет Serverbound Plugin Message
|
||||
// Получение бренда клиента из Serverbound Plugin Message
|
||||
// Identifier канала откуда берется бренд: minecraft:brand
|
||||
let brand = loop {
|
||||
let mut packet = client.read_packet(serverbound::configuration::PLUGIN_MESSAGE)?; // Пакет Serverbound Plugin Message
|
||||
|
||||
let identifier = packet.read_string()?;
|
||||
let identifier = packet.read_string()?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.get_mut().read_to_end(&mut data).unwrap();
|
||||
let mut data = Vec::new();
|
||||
packet.get_mut().read_to_end(&mut data).unwrap();
|
||||
|
||||
if identifier == "minecraft:brand" {
|
||||
break String::from_utf8_lossy(&data).to_string();
|
||||
} else {
|
||||
trigger_event!(client, plugin_message, &identifier, &data);
|
||||
}
|
||||
};
|
||||
if identifier == "minecraft:brand" {
|
||||
break String::from_utf8_lossy(&data).to_string();
|
||||
} else {
|
||||
trigger_event!(client, plugin_message, &identifier, &data);
|
||||
}
|
||||
};
|
||||
|
||||
let mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information
|
||||
let mut packet = client.read_packet(serverbound::configuration::CLIENT_INFORMATION)?; // Пакет Client Information
|
||||
|
||||
let locale = packet.read_string()?; // for example: en_us
|
||||
let view_distance = packet.read_signed_byte()?; // client-side render distance in chunks
|
||||
let chat_mode = packet.read_varint()?; // 0: enabled, 1: commands only, 2: hidden. See Chat#Client chat mode for more information.
|
||||
let chat_colors = packet.read_boolean()?; // this settings does nothing on client but can be used on serverside
|
||||
let displayed_skin_parts = packet.read_byte()?; // bit mask https://minecraft.wiki/w/Java_Edition_protocol#Client_Information_(configuration)
|
||||
let main_hand = packet.read_varint()?; // 0 for left and 1 for right
|
||||
let enable_text_filtering = packet.read_boolean()?; // filtering text for profanity, always false for offline mode
|
||||
let allow_server_listings = packet.read_boolean()?; // allows showing player in server listings in status
|
||||
let particle_status = packet.read_varint()?; // 0 for all, 1 for decreased, 2 for minimal
|
||||
let locale = packet.read_string()?; // for example: en_us
|
||||
let view_distance = packet.read_signed_byte()?; // client-side render distance in chunks
|
||||
let chat_mode = packet.read_varint()?; // 0: enabled, 1: commands only, 2: hidden. See Chat#Client chat mode for more information.
|
||||
let chat_colors = packet.read_boolean()?; // this settings does nothing on client but can be used on serverside
|
||||
let displayed_skin_parts = packet.read_byte()?; // bit mask https://minecraft.wiki/w/Java_Edition_protocol#Client_Information_(configuration)
|
||||
let main_hand = packet.read_varint()?; // 0 for left and 1 for right
|
||||
let enable_text_filtering = packet.read_boolean()?; // filtering text for profanity, always false for offline mode
|
||||
let allow_server_listings = packet.read_boolean()?; // allows showing player in server listings in status
|
||||
let particle_status = packet.read_varint()?; // 0 for all, 1 for decreased, 2 for minimal
|
||||
|
||||
client.set_client_info(ClientInfo {
|
||||
brand,
|
||||
locale,
|
||||
view_distance,
|
||||
chat_mode,
|
||||
chat_colors,
|
||||
displayed_skin_parts,
|
||||
main_hand,
|
||||
enable_text_filtering,
|
||||
allow_server_listings,
|
||||
particle_status
|
||||
});
|
||||
client.set_client_info(ClientInfo {
|
||||
brand,
|
||||
locale,
|
||||
view_distance,
|
||||
chat_mode,
|
||||
chat_colors,
|
||||
displayed_skin_parts,
|
||||
main_hand,
|
||||
enable_text_filtering,
|
||||
allow_server_listings,
|
||||
particle_status,
|
||||
});
|
||||
|
||||
// TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото
|
||||
// TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото
|
||||
|
||||
client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
|
||||
client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
|
||||
client.write_packet(&Packet::empty(clientbound::configuration::FINISH))?;
|
||||
client.read_packet(serverbound::configuration::ACKNOWLEDGE_FINISH)?;
|
||||
|
||||
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
|
||||
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
|
||||
|
||||
// Дальше работаем с режимом игры
|
||||
handle_play_state(client)?;
|
||||
},
|
||||
_ => { // Тип подключения не рукопожатный
|
||||
return Err(ServerError::UnexpectedPacket);
|
||||
}
|
||||
}
|
||||
// Дальше работаем с режимом игры
|
||||
handle_play_state(client)?;
|
||||
}
|
||||
_ => {
|
||||
// Тип подключения не рукопожатный
|
||||
return Err(ServerError::UnexpectedPacket);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
/*
|
||||
|
||||
Generated with parse_ids.py
|
||||
Generated with parse_ids.py
|
||||
|
||||
*/
|
||||
*/
|
||||
|
||||
pub mod clientbound {
|
||||
pub mod status {
|
||||
@ -172,7 +172,6 @@ pub mod clientbound {
|
||||
pub const CUSTOM_REPORT_DETAILS: u8 = 0x81;
|
||||
pub const SERVER_LINKS: u8 = 0x82;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub mod serverbound {
|
||||
@ -270,6 +269,4 @@ pub mod serverbound {
|
||||
pub const USE_ITEM_ON: u8 = 0x3E;
|
||||
pub const USE_ITEM: u8 = 0x3F;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
pub mod handler;
|
||||
pub mod id;
|
||||
pub mod play;
|
||||
pub mod handler;
|
||||
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConnectionState {
|
||||
@ -9,6 +8,5 @@ pub enum ConnectionState {
|
||||
Status,
|
||||
Login,
|
||||
Configuration,
|
||||
Play
|
||||
Play,
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,19 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::server::{data::text_component::TextComponent, player::context::ClientContext, ServerError};
|
||||
use crate::server::{
|
||||
ServerError, data::text_component::TextComponent, player::context::ClientContext,
|
||||
};
|
||||
|
||||
// Отдельная функция для работы с самой игрой
|
||||
pub fn handle_play_state(
|
||||
client: Arc<ClientContext>, // Контекст клиента
|
||||
client: Arc<ClientContext>, // Контекст клиента
|
||||
) -> Result<(), ServerError> {
|
||||
// Отключение игрока с сообщением
|
||||
client.protocol_helper().disconnect(TextComponent::rainbow(
|
||||
"server is in developement suka".to_string(),
|
||||
))?;
|
||||
|
||||
// Отключение игрока с сообщением
|
||||
client.protocol_helper().disconnect(TextComponent::rainbow("server is in developement suka".to_string()))?;
|
||||
// TODO: Сделать отправку пакетов Play
|
||||
|
||||
// TODO: Сделать отправку пакетов Play
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user