rust_minecraft_server/src/server/player/helper.rs
2025-05-03 20:42:23 +03:00

232 lines
8.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::{
io::Read,
sync::Arc,
time::{Duration, SystemTime},
};
use rust_mc_proto::{DataReader, DataWriter, Packet};
use crate::server::{
ServerError,
data::{ReadWriteNBT, text_component::TextComponent},
protocol::{
id::{clientbound, serverbound},
*,
},
};
use super::context::ClientContext;
// Помощник в работе с протоколом
// Может быть использован где угодно, но сделан именно для листенеров и пакет хандлеров
// Через него удобно делать всякую одинаковую херь
// Возможно надо было бы сделать прям обязательный какойто структ через который только можно было отправлять пакеты ...
// ... но мне лень
// Пусть юзают подключение и отправляют пакеты через него если хотят
// Почему бы и нет если да
pub struct ProtocolHelper {
client: Arc<ClientContext>,
state: ConnectionState,
}
impl ProtocolHelper {
pub fn new(client: Arc<ClientContext>) -> Self {
Self {
state: client.state(),
client,
}
}
pub fn reset_chat(&self) -> Result<(), ServerError> {
match self.state {
ConnectionState::Configuration => {
self.client
.write_packet(&Packet::empty(clientbound::configuration::RESET_CHAT))?;
Ok(())
}
_ => Err(ServerError::UnexpectedState),
}
}
pub fn store_cookie(&self, id: &str, data: &[u8]) -> Result<(), ServerError> {
self.client.write_packet(&Packet::build(
match self.state {
ConnectionState::Configuration => clientbound::configuration::STORE_COOKIE,
ConnectionState::Play => clientbound::play::STORE_COOKIE,
_ => { return Err(ServerError::UnexpectedState) },
},
|p| {
p.write_string(id)?;
p.write_bytes(data)
},
)?)?;
Ok(())
}
/// Leave from Configuration to Play state
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.set_state(ConnectionState::Play)?;
Ok(())
}
_ => Err(ServerError::UnexpectedState),
}
}
/// Enter to Configuration from Play state
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.set_state(ConnectionState::Configuration)?;
Ok(())
}
_ => Err(ServerError::UnexpectedState),
}
}
/// Enter to Configuration from Play state
pub fn ping(&self) -> Result<Duration, ServerError> {
match self.state {
ConnectionState::Play => {
let time = SystemTime::now();
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.read_packet(serverbound::configuration::PONG)?;
Ok(SystemTime::now().duration_since(time).unwrap())
}
_ => Err(ServerError::UnexpectedState),
}
}
pub fn disconnect(&self, reason: TextComponent) -> Result<(), ServerError> {
let packet = match self.state {
ConnectionState::Login => {
let text = reason.as_json()?;
Packet::build(0x00, |p| p.write_string(&text))?
}
ConnectionState::Configuration => {
let mut packet = Packet::empty(0x02);
packet.write_nbt(&reason)?;
packet
}
ConnectionState::Play => {
let mut packet = Packet::empty(0x1C);
packet.write_nbt(&reason)?;
packet
}
_ => {
self.client.close();
return Ok(());
}
};
self.client.write_packet(&packet)?;
Ok(())
}
/// Returns cookie content
pub fn request_cookie(&self, id: &str) -> Result<Option<Vec<u8>>, ServerError> {
match self.state {
ConnectionState::Configuration => {
let mut packet = Packet::empty(clientbound::configuration::COOKIE_REQUEST);
packet.write_string(id)?;
self.client.write_packet(&packet)?;
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()?;
Some(packet.read_bytes(n)?)
} else {
None
};
Ok(data)
},
ConnectionState::Play => {
let mut packet = Packet::empty(clientbound::play::COOKIE_REQUEST);
packet.write_string(id)?;
self.client.write_packet(&packet)?;
let mut packet = self
.client
.read_packet(serverbound::play::COOKIE_RESPONSE)?;
packet.read_string()?;
let data = if packet.read_boolean()? {
let n = packet.read_usize_varint()?;
Some(packet.read_bytes(n)?)
} else {
None
};
Ok(data)
}
_ => 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> {
match self.state {
ConnectionState::Login => {
let mut packet = Packet::empty(clientbound::login::PLUGIN_REQUEST);
packet.write_varint(id)?;
packet.write_string(channel)?;
packet.write_bytes(data)?;
self.client.write_packet(&packet)?;
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();
packet.get_mut().read_to_end(&mut data).unwrap();
Some(data)
} else {
None
};
Ok((identifier, data))
}
_ => 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::Play => Packet::empty(clientbound::play::PLUGIN_MESSAGE),
_ => return Err(ServerError::UnexpectedState),
};
packet.write_string(channel)?;
packet.write_bytes(data)?;
self.client.write_packet(&packet)?;
Ok(())
}
}