added state field to client context
created protocol helper read+write nbt trait moved event behaviour to new module added event on state change to packet handler
This commit is contained in:
parent
ca7eb4e350
commit
1c3c3e0f63
67
Cargo.lock
generated
67
Cargo.lock
generated
@ -121,12 +121,6 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
|
checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.20"
|
version = "1.2.20"
|
||||||
@ -194,6 +188,17 @@ version = "0.8.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "craftflow-nbt"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03a2d5312462b00f8420ace884a696f243be136ada9f50bf5f3d9858ff0c8e8e"
|
||||||
|
dependencies = [
|
||||||
|
"cesu8",
|
||||||
|
"serde",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.4.2"
|
version = "1.4.2"
|
||||||
@ -309,18 +314,6 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
|
checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fastnbt"
|
|
||||||
version = "2.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7d4a73a95dc65551ccd98e1ecd1adb5d1ba5361146963b31f481ca42fc0520a3"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"cesu8",
|
|
||||||
"serde",
|
|
||||||
"serde_bytes",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
@ -569,6 +562,12 @@ dependencies = [
|
|||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "paste"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf"
|
name = "phf"
|
||||||
version = "0.11.3"
|
version = "0.11.3"
|
||||||
@ -718,11 +717,12 @@ name = "rust_minecraft_server"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colog",
|
"colog",
|
||||||
|
"craftflow-nbt",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"fastnbt",
|
|
||||||
"itertools",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"palette",
|
"palette",
|
||||||
|
"paste",
|
||||||
"rust_mc_proto",
|
"rust_mc_proto",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_default",
|
"serde_default",
|
||||||
@ -759,15 +759,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_bytes"
|
|
||||||
version = "0.11.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_default"
|
name = "serde_default"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -877,6 +868,26 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.41"
|
version = "0.3.41"
|
||||||
|
@ -12,8 +12,9 @@ serde_default = "0.2.0"
|
|||||||
toml = "0.8.22"
|
toml = "0.8.22"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
palette = "0.7.6"
|
palette = "0.7.6"
|
||||||
fastnbt = "2.5.0"
|
craftflow-nbt = "2.1.0"
|
||||||
colog = "1.3.0"
|
colog = "1.3.0"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
uuid = "1.16.0"
|
uuid = "1.16.0"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
|
paste = "1.0.15"
|
||||||
|
@ -2,10 +2,10 @@ use std::{hash::Hash, net::{SocketAddr, TcpStream}, sync::{Arc, RwLock, RwLockWr
|
|||||||
|
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rust_mc_proto::{MinecraftConnection, Packet};
|
use rust_mc_proto::MinecraftConnection;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{config::Config, data::ServerError, player::{ClientInfo, Handshake, PlayerInfo}};
|
use crate::{config::Config, data::ServerError, event::{ConnectionState, Listener, PacketHandler}, player::{ClientInfo, Handshake, PlayerInfo, ProtocolHelper}};
|
||||||
|
|
||||||
pub struct ServerContext {
|
pub struct ServerContext {
|
||||||
pub config: Arc<Config>,
|
pub config: Arc<Config>,
|
||||||
@ -90,11 +90,12 @@ impl ServerContext {
|
|||||||
|
|
||||||
pub struct ClientContext {
|
pub struct ClientContext {
|
||||||
pub server: Arc<ServerContext>,
|
pub server: Arc<ServerContext>,
|
||||||
pub conn: RwLock<MinecraftConnection<TcpStream>>,
|
|
||||||
pub addr: SocketAddr,
|
pub addr: SocketAddr,
|
||||||
pub handshake: RwLock<Option<Handshake>>,
|
conn: RwLock<MinecraftConnection<TcpStream>>,
|
||||||
pub client_info: RwLock<Option<ClientInfo>>,
|
handshake: RwLock<Option<Handshake>>,
|
||||||
pub player_info: RwLock<Option<PlayerInfo>>
|
client_info: RwLock<Option<ClientInfo>>,
|
||||||
|
player_info: RwLock<Option<PlayerInfo>>,
|
||||||
|
state: RwLock<ConnectionState>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for ClientContext {
|
impl PartialEq for ClientContext {
|
||||||
@ -111,7 +112,6 @@ impl Hash for ClientContext {
|
|||||||
|
|
||||||
impl Eq for ClientContext {}
|
impl Eq for ClientContext {}
|
||||||
|
|
||||||
|
|
||||||
impl ClientContext {
|
impl ClientContext {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
server: Arc<ServerContext>,
|
server: Arc<ServerContext>,
|
||||||
@ -123,7 +123,8 @@ impl ClientContext {
|
|||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +140,18 @@ impl ClientContext {
|
|||||||
*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> {
|
||||||
|
*self.state.write().unwrap() = state.clone();
|
||||||
|
|
||||||
|
for handler in self.server.packet_handlers(
|
||||||
|
|o| o.on_state_priority()
|
||||||
|
).iter() {
|
||||||
|
handler.on_state(self.clone(), state.clone())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
}
|
}
|
||||||
@ -151,60 +164,15 @@ impl ClientContext {
|
|||||||
self.player_info.read().unwrap().clone()
|
self.player_info.read().unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn state(self: &Arc<Self>) -> ConnectionState {
|
||||||
|
self.state.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn conn(self: &Arc<Self>) -> RwLockWriteGuard<'_, MinecraftConnection<TcpStream>> {
|
pub fn conn(self: &Arc<Self>) -> RwLockWriteGuard<'_, MinecraftConnection<TcpStream>> {
|
||||||
self.conn.write().unwrap()
|
self.conn.write().unwrap()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Listener: Sync + Send {
|
pub fn protocol_helper(self: &Arc<Self>) -> ProtocolHelper {
|
||||||
fn on_status_priority(&self) -> i8 { 0 }
|
ProtocolHelper::new(self.clone())
|
||||||
fn on_status(&self, _: Arc<ClientContext>, _: &mut String) -> Result<(), ServerError> { Ok(()) }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub trait PacketHandler: Sync + Send {
|
|
||||||
fn on_incoming_packet_priority(&self) -> i8 { 0 }
|
|
||||||
fn on_incoming_packet(&self, _: Arc<ClientContext>, _: &mut Packet, _: ConnectionState) -> Result<(), ServerError> { Ok(()) }
|
|
||||||
|
|
||||||
fn on_outcoming_packet_priority(&self) -> i8 { 0 }
|
|
||||||
fn on_outcoming_packet(&self, _: Arc<ClientContext>, _: &mut Packet, _: ConnectionState) -> Result<(), ServerError> { Ok(()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum ConnectionState {
|
|
||||||
Handshake,
|
|
||||||
Status,
|
|
||||||
Login,
|
|
||||||
Configuration,
|
|
||||||
Play
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! call_handlers {
|
|
||||||
($packet:expr, $client:ident, $state:ident, incoming) => {
|
|
||||||
{
|
|
||||||
use crate::context::ConnectionState;
|
|
||||||
let mut packet = $packet;
|
|
||||||
for handler in $client.server.packet_handlers(
|
|
||||||
|o| o.on_incoming_packet_priority()
|
|
||||||
).iter() {
|
|
||||||
handler.on_incoming_packet($client.clone(), &mut packet, ConnectionState::$state)?;
|
|
||||||
}
|
|
||||||
packet.get_mut().set_position(0);
|
|
||||||
packet
|
|
||||||
}
|
|
||||||
};
|
|
||||||
($packet:expr, $client:ident, $state:ident, outcoming) => {
|
|
||||||
{
|
|
||||||
use crate::context::ConnectionState;
|
|
||||||
let mut packet = $packet;
|
|
||||||
for handler in $client.server.packet_handlers(
|
|
||||||
|o| o.on_outcoming_packet_priority()
|
|
||||||
).iter() {
|
|
||||||
handler.on_outcoming_packet($client.clone(), &mut packet, ConnectionState::$state)?;
|
|
||||||
}
|
|
||||||
packet.get_mut().set_position(0);
|
|
||||||
packet
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
45
src/data.rs
45
src/data.rs
@ -1,8 +1,8 @@
|
|||||||
use std::{error::Error, fmt::Display};
|
use std::{error::Error, fmt::Display, io::Read};
|
||||||
use palette::{Hsl, IntoColor, Srgb};
|
use palette::{Hsl, IntoColor, Srgb};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use rust_mc_proto::ProtocolError;
|
use rust_mc_proto::{DataReader, Packet, ProtocolError};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
|
|
||||||
// Ошибки сервера
|
// Ошибки сервера
|
||||||
@ -12,7 +12,8 @@ pub enum ServerError {
|
|||||||
Protocol(ProtocolError),
|
Protocol(ProtocolError),
|
||||||
ConnectionClosed,
|
ConnectionClosed,
|
||||||
SerTextComponent,
|
SerTextComponent,
|
||||||
DeTextComponent
|
DeTextComponent,
|
||||||
|
UnexpectedState
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ServerError {
|
impl Display for ServerError {
|
||||||
@ -94,16 +95,6 @@ impl TextComponent {
|
|||||||
TextComponentBuilder::new()
|
TextComponentBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_nbt(self) -> Result<Vec<u8>, ServerError> {
|
|
||||||
fastnbt::to_bytes(&self)
|
|
||||||
.map_err(|_| ServerError::SerTextComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_nbt(bytes: &[u8]) -> Result<TextComponent, ServerError> {
|
|
||||||
fastnbt::from_bytes(bytes)
|
|
||||||
.map_err(|_| ServerError::DeTextComponent)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_json(self) -> Result<String, ServerError> {
|
pub fn as_json(self) -> Result<String, ServerError> {
|
||||||
serde_json::to_string(&self)
|
serde_json::to_string(&self)
|
||||||
.map_err(|_| ServerError::SerTextComponent)
|
.map_err(|_| ServerError::SerTextComponent)
|
||||||
@ -115,6 +106,34 @@ impl TextComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for TextComponent {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(String::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ReadWriteNBT<T>: DataReader {
|
||||||
|
fn read_nbt(&mut self) -> Result<T, ServerError>;
|
||||||
|
fn write_nbt(&mut self, val: &T) -> Result<(), ServerError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_nbt(&mut self, val: &TextComponent) -> Result<(), ServerError> {
|
||||||
|
craftflow_nbt::to_writer(self.get_mut(), val)
|
||||||
|
.map_err(|_| ServerError::SerTextComponent)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TextComponentBuilder {
|
pub struct TextComponentBuilder {
|
||||||
text: String,
|
text: String,
|
||||||
color: Option<String>,
|
color: Option<String>,
|
||||||
|
101
src/event.rs
Normal file
101
src/event.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use rust_mc_proto::Packet;
|
||||||
|
|
||||||
|
use crate::{context::ClientContext, data::ServerError};
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! generate_handlers {
|
||||||
|
($name:ident $(, $arg_ty:ty)* $(,)?) => {
|
||||||
|
paste::paste! {
|
||||||
|
fn [<on_ $name _priority>](&self) -> i8 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn [<on_ $name>](&self, _: std::sync::Arc<ClientContext> $(, _: $arg_ty)*) -> Result<(), ServerError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! trigger_packet {
|
||||||
|
($packet:expr, $client:ident, $state:ident, $bound:ident) => {
|
||||||
|
{
|
||||||
|
paste::paste! {
|
||||||
|
let mut packet = $packet;
|
||||||
|
for handler in $client.server.packet_handlers(
|
||||||
|
|o| o.[<on_ $bound _packet_priority>]()
|
||||||
|
).iter() {
|
||||||
|
handler.[<on_ $bound _packet>]($client.clone(), &mut packet, crate::event::ConnectionState::$state)?;
|
||||||
|
}
|
||||||
|
packet.get_mut().set_position(0);
|
||||||
|
packet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! trigger_event {
|
||||||
|
($client:ident, $event:ident, $($arg:expr),* $(,)?) => {{
|
||||||
|
paste::paste! {
|
||||||
|
trigger_event!(@declare_mut_vars 0, $($arg),*);
|
||||||
|
|
||||||
|
for handler in $client.server.listeners(
|
||||||
|
|o| o.[<on_ $event _priority>]()
|
||||||
|
).iter() {
|
||||||
|
handler.[<on_ $event>](
|
||||||
|
$client.clone(),
|
||||||
|
$(trigger_event!(@expand_arg 0, $arg)),*
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
(@declare_mut_vars $i:tt, &mut $head:expr, $($tail:tt)*) => {
|
||||||
|
paste::paste! {
|
||||||
|
let mut [<__arg $i>] = $head;
|
||||||
|
}
|
||||||
|
trigger_event!(@declare_mut_vars trigger_event!(@inc $i), $($tail)*);
|
||||||
|
};
|
||||||
|
(@declare_mut_vars $i:tt, $head:expr, $($tail:tt)*) => {
|
||||||
|
trigger_event!(@declare_mut_vars trigger_event!(@inc $i), $($tail)*);
|
||||||
|
};
|
||||||
|
(@declare_mut_vars $_i:tt,) => {};
|
||||||
|
|
||||||
|
(@expand_arg $i:tt, &mut $head:expr) => {
|
||||||
|
paste::paste! { &mut [<__arg $i>] }
|
||||||
|
};
|
||||||
|
(@expand_arg $_i:tt, $head:expr) => {
|
||||||
|
$head
|
||||||
|
};
|
||||||
|
|
||||||
|
(@inc 0) => { 1 };
|
||||||
|
(@inc 1) => { 2 };
|
||||||
|
(@inc 2) => { 3 };
|
||||||
|
(@inc 3) => { 4 };
|
||||||
|
(@inc 4) => { 5 };
|
||||||
|
(@inc 5) => { 6 };
|
||||||
|
(@inc 6) => { 7 };
|
||||||
|
(@inc 7) => { 8 };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Listener: Sync + Send {
|
||||||
|
generate_handlers!(status, &mut String);
|
||||||
|
generate_handlers!(plugin_message, &mut String);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PacketHandler: Sync + Send {
|
||||||
|
generate_handlers!(incoming_packet, &mut Packet, ConnectionState);
|
||||||
|
generate_handlers!(outcoming_packet, &mut Packet, ConnectionState);
|
||||||
|
generate_handlers!(state, ConnectionState);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ConnectionState {
|
||||||
|
Handshake,
|
||||||
|
Status,
|
||||||
|
Login,
|
||||||
|
Configuration,
|
||||||
|
Play
|
||||||
|
}
|
38
src/main.rs
38
src/main.rs
@ -1,7 +1,8 @@
|
|||||||
use std::{env::args, io::Read, net::TcpListener, path::PathBuf, sync::Arc, thread, time::Duration};
|
use std::{env::args, io::Read, net::TcpListener, path::PathBuf, sync::Arc, thread, time::Duration};
|
||||||
|
|
||||||
use config::Config;
|
use config::Config;
|
||||||
use context::{ClientContext, ConnectionState, Listener, PacketHandler, ServerContext};
|
use event::{ConnectionState, Listener, PacketHandler};
|
||||||
|
use context::{ClientContext, ServerContext};
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use player::{ClientInfo, Handshake, PlayerInfo};
|
use player::{ClientInfo, Handshake, PlayerInfo};
|
||||||
use rust_mc_proto::{DataReader, DataWriter, MinecraftConnection, Packet};
|
use rust_mc_proto::{DataReader, DataWriter, MinecraftConnection, Packet};
|
||||||
@ -11,6 +12,7 @@ use pohuy::Pohuy;
|
|||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
|
pub mod event;
|
||||||
pub mod context;
|
pub mod context;
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod pohuy;
|
pub mod pohuy;
|
||||||
@ -194,7 +196,7 @@ fn handle_connection(
|
|||||||
// Получение пакетов производится через client.conn(),
|
// Получение пакетов производится через client.conn(),
|
||||||
// ВАЖНО: не помещать сам client.conn() в переменные,
|
// ВАЖНО: не помещать сам client.conn() в переменные,
|
||||||
// он должен сразу убиваться иначе соединение гдето задедлочится
|
// он должен сразу убиваться иначе соединение гдето задедлочится
|
||||||
let mut packet = call_handlers!(client.conn().read_packet()?, client, Handshake, incoming);
|
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Handshake, incoming);
|
||||||
|
|
||||||
if packet.id() != 0x00 {
|
if packet.id() != 0x00 {
|
||||||
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет рукопожатия")));
|
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет рукопожатия")));
|
||||||
@ -214,9 +216,11 @@ fn handle_connection(
|
|||||||
|
|
||||||
match next_state {
|
match next_state {
|
||||||
1 => { // Тип подключения - статус
|
1 => { // Тип подключения - статус
|
||||||
|
client.set_state(ConnectionState::Status)?; // Мы находимся в режиме Status
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Чтение запроса
|
// Чтение запроса
|
||||||
let packet = call_handlers!(client.conn().read_packet()?, client, Status, incoming);
|
let packet = trigger_packet!(client.conn().read_packet()?, client, Status, incoming);
|
||||||
|
|
||||||
match packet.id() {
|
match packet.id() {
|
||||||
0x00 => { // Запрос статуса
|
0x00 => { // Запрос статуса
|
||||||
@ -241,10 +245,10 @@ fn handle_connection(
|
|||||||
// Отправка статуса
|
// Отправка статуса
|
||||||
packet.write_string(&status)?;
|
packet.write_string(&status)?;
|
||||||
|
|
||||||
client.conn().write_packet(&call_handlers!(packet, client, Status, outcoming))?;
|
client.conn().write_packet(&trigger_packet!(packet, client, Status, outcoming))?;
|
||||||
},
|
},
|
||||||
0x01 => { // Пинг
|
0x01 => { // Пинг
|
||||||
client.conn().write_packet(&call_handlers!(packet, client, Status, outcoming))?;
|
client.conn().write_packet(&trigger_packet!(packet, client, Status, outcoming))?;
|
||||||
// Просто отправляем этот же пакет обратно
|
// Просто отправляем этот же пакет обратно
|
||||||
// ID такой-же, содержание тоже, так почему бы и нет?
|
// ID такой-же, содержание тоже, так почему бы и нет?
|
||||||
},
|
},
|
||||||
@ -255,10 +259,10 @@ fn handle_connection(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
2 => { // Тип подключения - игра
|
2 => { // Тип подключения - игра
|
||||||
// Мы находимся в режиме Login
|
client.set_state(ConnectionState::Login)?; // Мы находимся в режиме Login
|
||||||
|
|
||||||
// Читаем пакет Login Start
|
// Читаем пакет Login Start
|
||||||
let mut packet = call_handlers!(client.conn().read_packet()?, client, Login, incoming);
|
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Login, incoming);
|
||||||
|
|
||||||
let name = packet.read_string()?;
|
let name = packet.read_string()?;
|
||||||
let uuid = packet.read_uuid()?;
|
let uuid = packet.read_uuid()?;
|
||||||
@ -274,29 +278,29 @@ 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(&call_handlers!(Packet::build(0x03, |p| p.write_usize_varint(threshold))?, client, Login, outcoming))?;
|
client.conn().write_packet(&trigger_packet!(Packet::build(0x03, |p| p.write_usize_varint(threshold))?, client, Login, outcoming))?;
|
||||||
client.conn().set_compression(Some(threshold)); // Устанавливаем сжатие на соединении
|
client.conn().set_compression(Some(threshold)); // Устанавливаем сжатие на соединении
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отправка пакета Login Success
|
// Отправка пакета Login Success
|
||||||
client.conn().write_packet(&call_handlers!(Packet::build(0x02, |p| {
|
client.conn().write_packet(&trigger_packet!(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))?;
|
})?, client, Login, outcoming))?;
|
||||||
|
|
||||||
let packet = call_handlers!(client.conn().read_packet()?, client, Login, incoming);
|
let packet = trigger_packet!(client.conn().read_packet()?, client, Login, incoming);
|
||||||
|
|
||||||
if packet.id() != 0x03 {
|
if packet.id() != 0x03 {
|
||||||
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Login Acknowledged")));
|
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Login Acknowledged")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Мы перешли в режим 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 = call_handlers!(client.conn().read_packet()?, client, Configuration, incoming);
|
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Configuration, incoming);
|
||||||
|
|
||||||
if packet.id() == 0x02 { // Пакет Serverbound Plugin Message
|
if packet.id() == 0x02 { // Пакет Serverbound Plugin Message
|
||||||
let identifier = packet.read_string()?;
|
let identifier = packet.read_string()?;
|
||||||
@ -316,7 +320,7 @@ fn handle_connection(
|
|||||||
|
|
||||||
// debug!("brand: {brand}");
|
// debug!("brand: {brand}");
|
||||||
|
|
||||||
let mut packet = call_handlers!(client.conn().read_packet()?, client, Configuration, incoming);
|
let mut packet = trigger_packet!(client.conn().read_packet()?, client, Configuration, incoming);
|
||||||
|
|
||||||
// Пакет Client Information
|
// Пакет Client Information
|
||||||
if packet.id() != 0x00 {
|
if packet.id() != 0x00 {
|
||||||
@ -358,19 +362,19 @@ fn handle_connection(
|
|||||||
|
|
||||||
// TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото
|
// TODO: Заюзать Listener'ы чтобы они подмешивали сюда чото
|
||||||
|
|
||||||
client.conn().write_packet(&call_handlers!(Packet::empty(0x03), client, Configuration, outcoming))?;
|
client.conn().write_packet(&trigger_packet!(Packet::empty(0x03), client, Configuration, outcoming))?;
|
||||||
|
|
||||||
let packet = call_handlers!(client.conn().read_packet()?, client, Configuration, incoming);
|
let packet = trigger_packet!(client.conn().read_packet()?, client, Configuration, incoming);
|
||||||
|
|
||||||
if packet.id() != 0x03 {
|
if packet.id() != 0x03 {
|
||||||
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Acknowledge Finish Configuration")));
|
return Err(ServerError::UnknownPacket(format!("Неизвестный пакет при ожидании Acknowledge Finish Configuration")));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Мы перешли в режим Play
|
client.set_state(ConnectionState::Play)?; // Мы перешли в режим Play
|
||||||
|
|
||||||
// Отключение игрока с сообщением
|
// Отключение игрока с сообщением
|
||||||
// Отправляет в формате 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(&call_handlers!(Packet::build(0x1C, |p| {
|
client.conn().write_packet(&trigger_packet!(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
|
||||||
|
105
src/player.rs
105
src/player.rs
@ -1,5 +1,10 @@
|
|||||||
|
use std::{io::Read, sync::Arc};
|
||||||
|
|
||||||
|
use rust_mc_proto::{DataReader, DataWriter, Packet};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{context::ClientContext, data::{ServerError, TextComponent, ReadWriteNBT}, event::ConnectionState};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Handshake {
|
pub struct Handshake {
|
||||||
pub protocol_version: i32,
|
pub protocol_version: i32,
|
||||||
@ -25,4 +30,104 @@ pub struct ClientInfo {
|
|||||||
pub struct PlayerInfo {
|
pub struct PlayerInfo {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub uuid: Uuid
|
pub uuid: Uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ProtocolHelper {
|
||||||
|
client: Arc<ClientContext>,
|
||||||
|
state: ConnectionState
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtocolHelper {
|
||||||
|
pub fn new(client: Arc<ClientContext>) -> Self {
|
||||||
|
Self {
|
||||||
|
state: client.state(),
|
||||||
|
client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.conn().close();
|
||||||
|
return Ok(())
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.client.conn().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(0x00);
|
||||||
|
packet.write_string(id)?;
|
||||||
|
self.client.conn().write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = self.client.conn().read_packet()?;
|
||||||
|
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(0x04);
|
||||||
|
packet.write_varint(id)?;
|
||||||
|
packet.write_string(channel)?;
|
||||||
|
packet.write_bytes(data)?;
|
||||||
|
self.client.conn().write_packet(&packet)?;
|
||||||
|
|
||||||
|
let mut packet = self.client.conn().read_packet()?;
|
||||||
|
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(0x01),
|
||||||
|
ConnectionState::Play => Packet::empty(0x18),
|
||||||
|
_ => return Err(ServerError::UnexpectedState)
|
||||||
|
};
|
||||||
|
packet.write_string(channel)?;
|
||||||
|
packet.write_bytes(data)?;
|
||||||
|
self.client.conn().write_packet(&packet)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user