rewrite on rust mc proto

This commit is contained in:
MeexReay 2025-05-01 16:42:12 +03:00
parent d55bc24c10
commit 86e519ed63
6 changed files with 265 additions and 157 deletions

146
Cargo.lock generated
View File

@ -2,6 +2,152 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
dependencies = [
"cfg-if",
]
[[package]]
name = "flate2"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rust_mc_proto"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "734168f5b9aef1991db4b11c0bcd71c0336b0a5ba98269f0df39b41b8463ac8c"
dependencies = [
"flate2",
"uuid",
]
[[package]]
name = "rust_minecraft_server"
version = "0.1.0"
dependencies = [
"rust_mc_proto",
"serde",
"serde_json",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "uuid"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9"

View File

@ -3,4 +3,7 @@ name = "rust_minecraft_server"
version = "0.1.0"
edition = "2024"
[dependencies]
[dependencies]
rust_mc_proto = "0.1.19"
serde = "1.0.219" # used in text component
serde_json = "1.0.140"

View File

@ -1,45 +0,0 @@
use std::ops::Index;
pub enum BufferError {
EndOfBuffer
}
pub struct Buffer {
bytes: Vec<u8>,
index: usize
}
impl Buffer {
pub fn new(bytes: Vec<u8>, index: usize) -> Self {
Buffer { bytes, index }
}
pub fn read(&self, size: usize) -> Result<Vec<u8>, BufferError> {
if self.index + size >= self.bytes.len() {return Err(BufferError::EndOfBuffer);}
// self.index += size;
Ok(self.bytes[self.index..self.index+size-1].to_vec())
}
pub fn read2(&mut self, size: usize) -> Result<Vec<u8>, BufferError> {
if self.index + size >= self.bytes.len() {return Err(BufferError::EndOfBuffer);}
self.index += size;
Ok(self.bytes[self.index..self.index+size-1].to_vec())
}
}
pub trait Sas {
fn ts(&mut self);
}
impl Sas for Buffer {
fn ts(&mut self) {
self.index += 1;
}
}

View File

@ -1,7 +1,6 @@
use std::{io::Read, net::{SocketAddr, TcpListener, TcpStream}};
use std::{error::Error, fmt::Display};
#[derive(Debug)]
pub enum ServerError {
ReadPacketError,
ConnectionClosedError,
@ -11,84 +10,10 @@ pub enum ServerError {
PacketIsEnd
}
pub struct Packet {
size: i32,
data: Vec<u8>
impl Display for ServerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("{:?}", self))
}
}
impl Packet {
pub fn read_from(socket: &Socket) -> Result<Self, ServerError> {
let (size, n) = socket.read_varint_size()?;
let data = socket.read((size - n as i32) as usize)?;
Ok(Packet { size, data })
}
}
pub struct Socket {
pub stream: TcpStream,
pub addr: SocketAddr
}
impl Socket {
pub fn read(&self, size: usize) -> Result<Vec<u8>, ServerError>{
let mut buf: Vec<u8> = vec![0; size];
match (&self.stream).read(&mut buf) {
Ok(n) => if n == size {
Ok(buf)
} else if n == 0 {
Err(ServerError::ConnectionClosedError)
} else {
buf.truncate(n);
buf.append(&mut self.read(size-n)?);
Ok(buf)
},
Err(_) => Err(ServerError::ReadError)
}
}
pub fn read_varint_size(&self) -> Result<(i32, u8), ServerError>{
let mut result = 0i32;
let mut offset = 0;
let mut byte: u8;
loop {
byte = self.read(1)?[0];
result |= ((byte & 0x7F) << offset) as i32;
if (byte & 0x80) == 0 {break;};
offset += 7;
if offset >= 32 {return Err(ServerError::VarIntIsTooBig)}
}
Ok((result, offset / 7))
}
pub fn read_varint(&self) -> Result<i32, ServerError>{
Ok(self.read_varint_size()?.0)
}
}
pub struct Server {
listener: TcpListener
}
impl Server {
pub fn new(addr: &str) -> Result<Self, ServerError> {
match TcpListener::bind(addr) {
Ok(listener) => Ok(Server { listener }),
Err(_) => Err(ServerError::BindError)
}
}
pub fn accept(&self) -> Socket {
loop {
match self.listener.accept() {
Ok((stream, addr)) => return Socket {stream, addr},
Err(_) => continue
}
}
}
}
impl Error for ServerError {}

View File

@ -1,40 +1,114 @@
mod data;
use data::{Packet, Server, Socket};
use std::{error::Error, io::{Read, Write}, net::TcpListener, thread, time::Duration};
mod d;
use d::*;
use pohuy::Pohuy;
use rust_mc_proto::{DataReader, DataWriter, MinecraftConnection, Packet};
use std::thread;
pub mod pohuy;
pub mod data;
fn get_byte_size(i: i32) -> u8 {
for j in 1..4 {
if (i & -1 << (j * 7)) == 0 {
return j;
}
}; return 5;
}
// Сделать настройку хоста через конфиг
pub const HOST: &str = "127.0.0.1:25565";
fn main() {
println!("{}", get_byte_size(-2147483648));
let Ok(server) = TcpListener::bind(HOST) else {
println!("Не удалось забиндить сервер на {}", HOST);
return;
};
// let Ok(server) = Server::new("127.0.0.1:25565") else {
// println!("Не удалось забиндить сервер"); return;
// };
println!("Сервер запущен на {}", HOST);
// loop {
// let socket = server.accept();
// thread::spawn(move || { handle_connection(socket); });
// }
while let Ok((stream, addr)) = server.accept() {
thread::spawn(move || {
println!("Подключение: {}", addr);
// Установка таймаутов на чтение и запись
// По умолчанию пусть будет 5 секунд, надо будет сделать настройку через конфиг
stream.set_read_timeout(Some(Duration::from_secs(5))).pohuy();
stream.set_write_timeout(Some(Duration::from_secs(5))).pohuy();
// Обработка подключения
// Если ошибка -> похуй
handle_connection(MinecraftConnection::new(&stream)).pohuy();
println!("Отключение: {}", addr);
});
}
}
fn handle_connection(socket: Socket) {
let Ok(packet) = Packet::read_from(&socket) else {return;};
// пакет уже имеет свой размер (size) и данные (data)
// надо поместить пакет в очередь, обработать по шаблону и отдать обработчику
fn handle_connection(
mut conn: MinecraftConnection<impl Read + Write> // Подключение
) -> Result<(), Box<dyn Error>> {
// Чтение рукопожатия
let mut packet = conn.read_packet()?;
// fn on_keep_alive(socket: Socket, time: u64) {
// if time != self.time {
// socket.close()
// }
// }
if packet.id() != 0x00 { return Ok(()); } // Айди пакета не рукопожатное - выходим из функции
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 для обычного подключения
match next_state {
1 => { // Тип подключения - статус
loop {
// Чтение запроса
let packet = conn.read_packet()?;
match packet.id() {
0x00 => { // Запрос статуса
conn.write_packet(&Packet::build(0x00, |packet| {
// Отправка статуса
// В будущем это надо будет переделать чтобы это отправлялось через Listener'ы а не самим ядром сервера
// Хотя можно сделать и дефолтное значение через конфиг
packet.write_string(&format!(
// Пример статуса с дебаг-инфой
"{{
\"version\": {{
\"name\": \"1.21.5\",
\"protocol\": {protocol_version}
}},
\"players\": {{
\"max\": 100,
\"online\": 5,
\"sample\": [
{{
\"name\": \"thinkofdeath\",
\"id\": \"4566e69f-c907-48ee-8d71-d7ba5aa00d20\"
}}
]
}},
\"description\": {{
\"text\": \"pv: {protocol_version}, sp: {server_port}\nsa: {server_address}\"
}},
\"favicon\": \"data:image/png;base64,<data>\",
\"enforcesSecureChat\": false
}}"
))
})?)?;
},
0x01 => { // Пинг
conn.write_packet(&packet)?;
// Просто отправляем этот же пакет обратно
// ID такой-же, содержание тоже, так почему бы и нет?
},
_ => {
break;
}
}
}
},
2 | 3 => { // Тип подключения - игра
// Отключение игрока с сообщением
// Заглушка так сказать
conn.write_packet(&Packet::build(0x00, |packet| {
packet.write_string("{\"text\": \"This server is in developement!!\", \"color\": \"red\", \"bold\": true}")
})?)?;
// TODO: Чтение Configuration (возможно с примешиванием Listener'ов)
// TODO: Обработчик пакетов Play (тоже трейт), который уже будет дергать Listener'ы
},
_ => {} // Тип подключения не рукопожатный
}
Ok(())
}

5
src/pohuy.rs Normal file
View File

@ -0,0 +1,5 @@
pub trait Pohuy: Sized {
fn pohuy(self) -> () {}
}
impl<T, E> Pohuy for Result<T, E> {}