allow packet cancellation

This commit is contained in:
MeexReay 2025-05-02 20:38:44 +03:00
parent 8a1aa4b31f
commit 5d5167347b
3 changed files with 68 additions and 54 deletions

View File

@ -76,6 +76,7 @@ impl PacketHandler for ExamplePacketHandler {
&self, &self,
client: Arc<ClientContext>, client: Arc<ClientContext>,
packet: &mut Packet, packet: &mut Packet,
cancelled: &mut bool,
state: ConnectionState state: ConnectionState
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
debug!("{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len()); debug!("{} -> S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len());
@ -87,6 +88,7 @@ impl PacketHandler for ExamplePacketHandler {
&self, &self,
client: Arc<ClientContext>, client: Arc<ClientContext>,
packet: &mut Packet, packet: &mut Packet,
cancelled: &mut bool,
state: ConnectionState state: ConnectionState
) -> Result<(), ServerError> { ) -> Result<(), ServerError> {
debug!("{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len()); debug!("{} <- S\t| 0x{:02x}\t| {:?}\t| {} bytes", client.addr.clone(), packet.id(), state, packet.len());

View File

@ -17,52 +17,68 @@ macro_rules! generate_handlers {
}; };
} }
// Пример использования: /// Отправляет пакет клиенту и проходит по пакет ханлдерам
// let packet_dst = trigger_packet!(packet_src, client, Handshake, incoming); /// Пример использования:
// │ │ │ │ ///
// ┌────────────────────┼───────────┼────────┼──────────┘ /// write_packet!(client, Handshake, packet);
// │ │ │ │ ///
// │ ┌─────┼───────────┘ │ /// `Handshake` это режим подключения (типы ConnectionState)
// │ │ │ │
// │ │ │ └──────────────────┐
// │ ▼ └───────────┐ │
// Сделается вот такой вызов на всех packet_handler'ах: ▼ ▼ ▼
// handler.on_incoming_packet(client.clone(), &mut packet, ConnectionState::Handshake)
// packet_src можно заменить на получение пакета, например: trigger_packet!(client.conn().read_packet()?, client, Handshake, incoming);
// В packet_dst будет лежать обратботанный пакет, прошедший через все хандлеры
// TODO: сделать чтобы можно было ваще отключить обработку
#[macro_export] #[macro_export]
macro_rules! trigger_packet { macro_rules! write_packet {
($packet:expr, $client:ident, $state:ident, $bound:ident) => { ($client:expr, $state:ident, $packet:expr) => {
{ {
paste::paste! { let mut packet = $packet;
let mut packet = $packet; let mut cancelled = false;
for handler in $client.server.packet_handlers( for handler in $client.server.packet_handlers(
|o| o.[<on_ $bound _packet_priority>]() |o| o.on_outcoming_packet_priority()
).iter() { ).iter() {
handler.[<on_ $bound _packet>]($client.clone(), &mut packet, crate::server::protocol::ConnectionState::$state)?; handler.on_outcoming_packet($client.clone(), &mut packet, &mut cancelled, crate::server::protocol::ConnectionState::$state)?;
}
packet.get_mut().set_position(0); packet.get_mut().set_position(0);
packet }
if !cancelled {
$client.conn().write_packet(&packet)?;
} }
} }
}; };
} }
// Честно ни разу не проверял работу этого дерьма /// Читает пакет от клиента и проходит по пакет ханлдерам
// Пример использования: /// Пример использования:
// trigger_event!(client, status, $mut response, state); ///
// Сделается вот такой вызов на всех листенерах: /// let packet = read_packet!(client, Handshake);
// listener.on_status(client.clone(), &mut response, state); ///
/// `Handshake` это режим подключения (типы ConnectionState)
#[macro_export]
macro_rules! read_packet {
($client:expr, $state:ident) => {
loop {
let mut packet = $client.conn().read_packet()?;
let mut cancelled = false;
for handler in $client.server.packet_handlers(
|o| o.on_incoming_packet_priority()
).iter() {
handler.on_incoming_packet($client.clone(), &mut packet, &mut cancelled, crate::server::protocol::ConnectionState::$state)?;
packet.get_mut().set_position(0);
}
if !cancelled {
break packet;
}
}
};
}
/// Пример использования:
///
/// trigger_event!(client, status, &mut response, state);
#[macro_export] #[macro_export]
macro_rules! trigger_event { macro_rules! trigger_event {
($client:ident, $event:ident, $(, $arg_ty:ty)* $(,)?) => {{ ($client:ident, $event:ident $(, $arg_ty:expr)* $(,)?) => {{
paste::paste! { paste::paste! {
for handler in $client.server.listeners( for handler in $client.server.listeners(
|o| o.[<on_ $event _priority>]() |o| o.[<on_ $event _priority>]()
).iter() { ).iter() {
handler.[<on_ $event>]( handler.[<on_ $event>](
$client.clone(), $client.clone()
$(, $arg_ty)* $(, $arg_ty)*
)?; )?;
} }
@ -76,7 +92,7 @@ pub trait Listener: Sync + Send {
} }
pub trait PacketHandler: Sync + Send { pub trait PacketHandler: Sync + Send {
generate_handlers!(incoming_packet, &mut Packet, ConnectionState); generate_handlers!(incoming_packet, &mut Packet, &mut bool, ConnectionState);
generate_handlers!(outcoming_packet, &mut Packet, ConnectionState); generate_handlers!(outcoming_packet, &mut Packet, &mut bool, ConnectionState);
generate_handlers!(state, ConnectionState); generate_handlers!(state, ConnectionState);
} }

View File

@ -4,7 +4,7 @@ use super::{player::context::{ClientContext, ClientInfo, Handshake, PlayerInfo},
use log::error; use log::error;
use rust_mc_proto::{DataReader, DataWriter, Packet}; use rust_mc_proto::{DataReader, DataWriter, Packet};
use crate::trigger_packet; use crate::{trigger_event, write_packet, read_packet};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ConnectionState { pub enum ConnectionState {
@ -22,7 +22,7 @@ pub fn handle_connection(
// Получение пакетов производится через client.conn(), // Получение пакетов производится через client.conn(),
// ВАЖНО: не помещать сам client.conn() в переменные, // ВАЖНО: не помещать сам client.conn() в переменные,
// он должен сразу убиваться иначе соединение гдето задедлочится // он должен сразу убиваться иначе соединение гдето задедлочится
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Handshake, incoming); let mut packet = read_packet!(client, Handshake);
if packet.id() != 0x00 { if packet.id() != 0x00 {
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет рукопожатия"))); return Err(ServerError::UnknownPacket(format!("Неизвестный пакет рукопожатия")));
@ -46,7 +46,7 @@ pub fn handle_connection(
loop { loop {
// Чтение запроса // Чтение запроса
let packet = trigger_packet!(client.conn().read_packet()?, client, Status, incoming); let packet = read_packet!(client, Status);
match packet.id() { match packet.id() {
0x00 => { // Запрос статуса 0x00 => { // Запрос статуса
@ -62,19 +62,15 @@ pub fn handle_connection(
}".to_string(); }".to_string();
// Опрос всех листенеров // Опрос всех листенеров
for listener in client.server.listeners( // Цикл по листенерам trigger_event!(client, status, &mut status);
|o| o.on_status_priority() // Сортировка по приоритетности
).iter() {
listener.on_status(client.clone(), &mut status)?; // Вызов метода листенера
}
// Отправка статуса // Отправка статуса
packet.write_string(&status)?; packet.write_string(&status)?;
client.conn().write_packet(&trigger_packet!(packet, client, Status, outcoming))?; write_packet!(client, Status, packet);
}, },
0x01 => { // Пинг 0x01 => { // Пинг
client.conn().write_packet(&trigger_packet!(packet, client, Status, outcoming))?; write_packet!(client, Status, packet);
// Просто отправляем этот же пакет обратно // Просто отправляем этот же пакет обратно
// ID такой-же, содержание тоже, так почему бы и нет? // ID такой-же, содержание тоже, так почему бы и нет?
}, },
@ -88,7 +84,7 @@ pub fn handle_connection(
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login
// Читаем пакет Login Start // Читаем пакет Login Start
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Login, incoming); let mut packet = read_packet!(client, Login);
let name = packet.read_string()?; let name = packet.read_string()?;
let uuid = packet.read_uuid()?; let uuid = packet.read_uuid()?;
@ -104,18 +100,18 @@ pub fn handle_connection(
// Отправляем пакет 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.conn().write_packet(&trigger_packet!(Packet::build(0x03, |p| p.write_usize_varint(threshold))?, client, Login, outcoming))?; write_packet!(client, Login, Packet::build(0x03, |p| p.write_usize_varint(threshold))?);
client.conn().set_compression(Some(threshold)); // Устанавливаем сжатие на соединении client.conn().set_compression(Some(threshold)); // Устанавливаем сжатие на соединении
} }
// Отправка пакета Login Success // Отправка пакета Login Success
client.conn().write_packet(&trigger_packet!(Packet::build(0x02, |p| { write_packet!(client, Login, Packet::build(0x02, |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, Login, outcoming))?; })?);
let packet = trigger_packet!(client.conn().read_packet()?, client, Login, incoming); let packet = read_packet!(client, Login);
if packet.id() != 0x03 { if packet.id() != 0x03 {
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Login Acknowledged"))); return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Login Acknowledged")));
@ -126,7 +122,7 @@ pub fn handle_connection(
// Получение бренда клиента из Serverbound Plugin Message // Получение бренда клиента из Serverbound Plugin Message
// Identifier канала откуда берется бренд: minecraft:brand // Identifier канала откуда берется бренд: minecraft:brand
let brand = loop { let brand = loop {
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Configuration, incoming); let mut packet = read_packet!(client, Configuration);
if packet.id() == 0x02 { // Пакет Serverbound Plugin Message if packet.id() == 0x02 { // Пакет Serverbound Plugin Message
let identifier = packet.read_string()?; let identifier = packet.read_string()?;
@ -146,7 +142,7 @@ pub fn handle_connection(
// debug!("brand: {brand}"); // debug!("brand: {brand}");
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Configuration, incoming); let mut packet = read_packet!(client, Configuration);
// Пакет Client Information // Пакет Client Information
if packet.id() != 0x00 { if packet.id() != 0x00 {
@ -188,9 +184,9 @@ pub fn handle_connection(
// TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото // TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото
client.conn().write_packet(&trigger_packet!(Packet::empty(0x03), client, Configuration, outcoming))?; write_packet!(client, Configuration, Packet::empty(0x03));
let packet = trigger_packet!(client.conn().read_packet()?, client, Configuration, incoming); let packet = read_packet!(client, Configuration);
if packet.id() != 0x03 { if packet.id() != 0x03 {
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Acknowledge Finish Configuration"))); return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Acknowledge Finish Configuration")));
@ -200,12 +196,12 @@ pub fn handle_connection(
// Отключение игрока с сообщением // Отключение игрока с сообщением
// Отправляет в формате NBT TAG_String (https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/NBT#Specification:string_tag) // Отправляет в формате NBT TAG_String (https://minecraft.wiki/w/Minecraft_Wiki:Projects/wiki.vg_merge/NBT#Specification:string_tag)
client.conn().write_packet(&trigger_packet!(Packet::build(0x1C, |p| { write_packet!(client, Play, Packet::build(0x1C, |p| {
let message = "server is in developmenet lol".to_string(); let message = "server is in developmenet lol".to_string();
p.write_byte(0x08)?; // NBT Type Name (TAG_String) p.write_byte(0x08)?; // NBT Type Name (TAG_String)
p.write_unsigned_short(message.len() as u16)?; // String length in unsigned short p.write_unsigned_short(message.len() as u16)?; // String length in unsigned short
p.write_bytes(message.as_bytes()) p.write_bytes(message.as_bytes())
})?, client, Play, outcoming))?; })?);
// TODO: Сделать отправку пакетов Play // TODO: Сделать отправку пакетов Play
}, },