Extract all packet writing logic to single function
This commit is contained in:
parent
7df3829e00
commit
47fe7d0387
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -685,7 +685,7 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
[[package]]
|
||||
name = "minecraft-protocol"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=d26a525#d26a525c7b29b61d2db64805181fb5471ea4317a"
|
||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=4e6a1f9#4e6a1f93807f35671943630c9bdc0d0c5da67eb8"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"minecraft-protocol-derive",
|
||||
@ -698,7 +698,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "minecraft-protocol-derive"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=d26a525#d26a525c7b29b61d2db64805181fb5471ea4317a"
|
||||
source = "git+https://github.com/timvisee/rust-minecraft-protocol?rev=4e6a1f9#4e6a1f93807f35671943630c9bdc0d0c5da67eb8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -40,7 +40,7 @@ dotenv = "0.15"
|
||||
flate2 = { version = "1.0", default-features = false, features = ["default"] }
|
||||
futures = { version = "0.3", default-features = false }
|
||||
log = "0.4"
|
||||
minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "d26a525" }
|
||||
minecraft-protocol = { git = "https://github.com/timvisee/rust-minecraft-protocol", rev = "4e6a1f9" }
|
||||
pretty_env_logger = "0.4"
|
||||
rand = "0.8"
|
||||
serde = "1.0"
|
||||
|
394
src/lobby.rs
394
src/lobby.rs
@ -8,15 +8,13 @@ use bytes::BytesMut;
|
||||
use futures::FutureExt;
|
||||
use minecraft_protocol::data::chat::{Message, Payload};
|
||||
use minecraft_protocol::decoder::Decoder;
|
||||
use minecraft_protocol::encoder::Encoder;
|
||||
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
|
||||
use minecraft_protocol::version::v1_14_4::login::{LoginStart, LoginSuccess, SetCompression};
|
||||
use minecraft_protocol::version::v1_17_1::game::{
|
||||
ClientBoundKeepAlive, JoinGame, NamedSoundEffect, PlayerPositionAndLook, PluginMessage,
|
||||
Respawn, SetTitleSubtitle, SetTitleText, SetTitleTimes, TimeUpdate,
|
||||
ClientBoundKeepAlive, ClientBoundPluginMessage, JoinGame, NamedSoundEffect,
|
||||
PlayerPositionAndLook, Respawn, SetTitleSubtitle, SetTitleText, SetTitleTimes, TimeUpdate,
|
||||
};
|
||||
use nbt::CompoundTag;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::select;
|
||||
@ -28,8 +26,7 @@ use crate::mc;
|
||||
use crate::net;
|
||||
use crate::proto;
|
||||
use crate::proto::client::{Client, ClientInfo, ClientState};
|
||||
use crate::proto::packet::{self, RawPacket};
|
||||
use crate::proto::packets;
|
||||
use crate::proto::{packet, packets};
|
||||
use crate::proxy;
|
||||
use crate::server::{Server, State};
|
||||
|
||||
@ -185,15 +182,7 @@ async fn respond_set_compression(
|
||||
writer: &mut WriteHalf<'_>,
|
||||
threshold: i32,
|
||||
) -> Result<(), ()> {
|
||||
let packet = SetCompression { threshold };
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::login::CLIENT_SET_COMPRESSION, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(SetCompression { threshold }, client, writer).await
|
||||
}
|
||||
|
||||
/// Respond to client with login success packet
|
||||
@ -203,21 +192,18 @@ async fn respond_login_success(
|
||||
writer: &mut WriteHalf<'_>,
|
||||
login_start: &LoginStart,
|
||||
) -> Result<(), ()> {
|
||||
let packet = LoginSuccess {
|
||||
uuid: Uuid::new_v3(
|
||||
&Uuid::new_v3(&Uuid::nil(), b"OfflinePlayer"),
|
||||
login_start.name.as_bytes(),
|
||||
),
|
||||
username: login_start.name.clone(),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::login::CLIENT_LOGIN_SUCCESS, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
LoginSuccess {
|
||||
uuid: Uuid::new_v3(
|
||||
&Uuid::new_v3(&Uuid::nil(), b"OfflinePlayer"),
|
||||
login_start.name.as_bytes(),
|
||||
),
|
||||
username: login_start.name.clone(),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Play lobby ready sound effect if configured.
|
||||
@ -271,84 +257,75 @@ async fn send_lobby_join_game(
|
||||
server: &Server,
|
||||
) -> Result<(), ()> {
|
||||
// Send Minecrafts default states, slightly customised for lobby world
|
||||
let packet = {
|
||||
let status = server.status().await;
|
||||
packet::write_packet(
|
||||
{
|
||||
let status = server.status().await;
|
||||
|
||||
JoinGame {
|
||||
// Player ID must be unique, if it collides with another server entity ID the player gets
|
||||
// in a weird state and cannot move
|
||||
entity_id: 0,
|
||||
// TODO: use real server value
|
||||
hardcore: false,
|
||||
game_mode: 3,
|
||||
previous_game_mode: -1i8 as u8,
|
||||
world_names: vec![
|
||||
"minecraft:overworld".into(),
|
||||
"minecraft:the_nether".into(),
|
||||
"minecraft:the_end".into(),
|
||||
],
|
||||
dimension_codec: snbt_to_compound_tag(include_str!("../res/dimension_codec.snbt")),
|
||||
dimension: snbt_to_compound_tag(include_str!("../res/dimension.snbt")),
|
||||
world_name: "lazymc:lobby".into(),
|
||||
hashed_seed: 0,
|
||||
max_players: status.as_ref().map(|s| s.players.max as i32).unwrap_or(20),
|
||||
// TODO: use real server value
|
||||
view_distance: 10,
|
||||
// TODO: use real server value
|
||||
reduced_debug_info: false,
|
||||
// TODO: use real server value
|
||||
enable_respawn_screen: true,
|
||||
is_debug: true,
|
||||
is_flat: false,
|
||||
}
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_JOIN_GAME, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
JoinGame {
|
||||
// Player ID must be unique, if it collides with another server entity ID the player gets
|
||||
// in a weird state and cannot move
|
||||
entity_id: 0,
|
||||
// TODO: use real server value
|
||||
hardcore: false,
|
||||
game_mode: 3,
|
||||
previous_game_mode: -1i8 as u8,
|
||||
world_names: vec![
|
||||
"minecraft:overworld".into(),
|
||||
"minecraft:the_nether".into(),
|
||||
"minecraft:the_end".into(),
|
||||
],
|
||||
dimension_codec: snbt_to_compound_tag(include_str!("../res/dimension_codec.snbt")),
|
||||
dimension: snbt_to_compound_tag(include_str!("../res/dimension.snbt")),
|
||||
world_name: "lazymc:lobby".into(),
|
||||
hashed_seed: 0,
|
||||
max_players: status.as_ref().map(|s| s.players.max as i32).unwrap_or(20),
|
||||
// TODO: use real server value
|
||||
view_distance: 10,
|
||||
// TODO: use real server value
|
||||
reduced_debug_info: false,
|
||||
// TODO: use real server value
|
||||
enable_respawn_screen: true,
|
||||
is_debug: true,
|
||||
is_flat: false,
|
||||
}
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send lobby brand to client.
|
||||
async fn send_lobby_brand(client: &Client, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||
let packet = PluginMessage {
|
||||
channel: "minecraft:brand".into(),
|
||||
data: SERVER_BRAND.into(),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_PLUGIN_MESSAGE, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
ClientBoundPluginMessage {
|
||||
channel: "minecraft:brand".into(),
|
||||
data: SERVER_BRAND.into(),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send lobby player position to client.
|
||||
async fn send_lobby_player_pos(client: &Client, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||
// Send player location, disables download terrain screen
|
||||
let packet = PlayerPositionAndLook {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
yaw: 0.0,
|
||||
pitch: 90.0,
|
||||
flags: 0b00000000,
|
||||
teleport_id: 0,
|
||||
dismount_vehicle: true,
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_PLAYER_POS_LOOK, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
PlayerPositionAndLook {
|
||||
x: 0.0,
|
||||
y: 0.0,
|
||||
z: 0.0,
|
||||
yaw: 0.0,
|
||||
pitch: 90.0,
|
||||
flags: 0b00000000,
|
||||
teleport_id: 0,
|
||||
dismount_vehicle: true,
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send lobby time update to client.
|
||||
@ -356,38 +333,32 @@ async fn send_lobby_time_update(client: &Client, writer: &mut WriteHalf<'_>) ->
|
||||
const MC_TIME_NOON: i64 = 6000;
|
||||
|
||||
// Send time update, required once for keep-alive packets
|
||||
let packet = TimeUpdate {
|
||||
world_age: MC_TIME_NOON,
|
||||
time_of_day: MC_TIME_NOON,
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_TIME_UPDATE, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
TimeUpdate {
|
||||
world_age: MC_TIME_NOON,
|
||||
time_of_day: MC_TIME_NOON,
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send keep alive packet to client.
|
||||
///
|
||||
/// Required periodically in play mode to prevent client timeout.
|
||||
async fn send_keep_alive(client: &Client, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||
let packet = ClientBoundKeepAlive {
|
||||
// Keep sending new IDs
|
||||
id: KEEP_ALIVE_ID.fetch_add(1, Ordering::Relaxed),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_KEEP_ALIVE, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
packet::write_packet(
|
||||
ClientBoundKeepAlive {
|
||||
// Keep sending new IDs
|
||||
id: KEEP_ALIVE_ID.fetch_add(1, Ordering::Relaxed),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
|
||||
// TODO: verify we receive keep alive response with same ID from client
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send lobby title packets to client.
|
||||
@ -405,50 +376,45 @@ async fn send_lobby_title(
|
||||
let subtitle = text.lines().skip(1).collect::<Vec<_>>().join("\n");
|
||||
|
||||
// Set title
|
||||
let packet = SetTitleText {
|
||||
text: Message::new(Payload::text(title)),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_SET_TITLE_TEXT, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
packet::write_packet(
|
||||
SetTitleText {
|
||||
text: Message::new(Payload::text(title)),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Set subtitle
|
||||
let packet = SetTitleSubtitle {
|
||||
text: Message::new(Payload::text(&subtitle)),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_SET_TITLE_SUBTITLE, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
packet::write_packet(
|
||||
SetTitleSubtitle {
|
||||
text: Message::new(Payload::text(&subtitle)),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Set title times
|
||||
let packet = if title.is_empty() && subtitle.is_empty() {
|
||||
// Defaults: https://minecraft.fandom.com/wiki/Commands/title#Detail
|
||||
SetTitleTimes {
|
||||
fade_in: 10,
|
||||
stay: 70,
|
||||
fade_out: 20,
|
||||
}
|
||||
} else {
|
||||
SetTitleTimes {
|
||||
fade_in: 0,
|
||||
stay: KEEP_ALIVE_INTERVAL.as_secs() as i32 * mc::TICKS_PER_SECOND as i32 * 2,
|
||||
fade_out: 0,
|
||||
}
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_SET_TITLE_TIMES, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
if title.is_empty() && subtitle.is_empty() {
|
||||
// Defaults: https://minecraft.fandom.com/wiki/Commands/title#Detail
|
||||
SetTitleTimes {
|
||||
fade_in: 10,
|
||||
stay: 70,
|
||||
fade_out: 20,
|
||||
}
|
||||
} else {
|
||||
SetTitleTimes {
|
||||
fade_in: 0,
|
||||
stay: KEEP_ALIVE_INTERVAL.as_secs() as i32 * mc::TICKS_PER_SECOND as i32 * 2,
|
||||
fade_out: 0,
|
||||
}
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send lobby ready sound effect to client.
|
||||
@ -457,23 +423,20 @@ async fn send_lobby_sound_effect(
|
||||
writer: &mut WriteHalf<'_>,
|
||||
sound_name: &str,
|
||||
) -> Result<(), ()> {
|
||||
let packet = NamedSoundEffect {
|
||||
sound_name: sound_name.into(),
|
||||
sound_category: 0,
|
||||
effect_pos_x: 0,
|
||||
effect_pos_y: 0,
|
||||
effect_pos_z: 0,
|
||||
volume: 1.0,
|
||||
pitch: 1.0,
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_NAMED_SOUND_EFFECT, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
NamedSoundEffect {
|
||||
sound_name: sound_name.into(),
|
||||
sound_category: 0,
|
||||
effect_pos_x: 0,
|
||||
effect_pos_y: 0,
|
||||
effect_pos_z: 0,
|
||||
volume: 1.0,
|
||||
pitch: 1.0,
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send respawn packet to client to jump from lobby into now loaded server.
|
||||
@ -484,24 +447,21 @@ async fn send_respawn_from_join(
|
||||
writer: &mut WriteHalf<'_>,
|
||||
join_game: JoinGame,
|
||||
) -> Result<(), ()> {
|
||||
let packet = Respawn {
|
||||
dimension: join_game.dimension,
|
||||
world_name: join_game.world_name,
|
||||
hashed_seed: join_game.hashed_seed,
|
||||
game_mode: join_game.game_mode,
|
||||
previous_game_mode: join_game.previous_game_mode,
|
||||
is_debug: join_game.is_debug,
|
||||
is_flat: join_game.is_flat,
|
||||
copy_metadata: false,
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_RESPAWN, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
Respawn {
|
||||
dimension: join_game.dimension,
|
||||
world_name: join_game.world_name,
|
||||
hashed_seed: join_game.hashed_seed,
|
||||
game_mode: join_game.game_mode,
|
||||
previous_game_mode: join_game.previous_game_mode,
|
||||
is_debug: join_game.is_debug,
|
||||
is_flat: join_game.is_flat,
|
||||
copy_metadata: false,
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// An infinite keep-alive loop.
|
||||
@ -634,29 +594,27 @@ async fn connect_to_server_no_timeout(
|
||||
tmp_client.set_state(ClientState::Login);
|
||||
|
||||
// Handshake packet
|
||||
let packet = Handshake {
|
||||
protocol_version: client_info.protocol_version.unwrap(),
|
||||
server_addr: config.server.address.ip().to_string(),
|
||||
server_port: config.server.address.port(),
|
||||
next_state: ClientState::Login.to_id(),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let request = RawPacket::new(packets::handshake::SERVER_HANDSHAKE, data).encode(&tmp_client)?;
|
||||
writer.write_all(&request).await.map_err(|_| ())?;
|
||||
packet::write_packet(
|
||||
Handshake {
|
||||
protocol_version: client_info.protocol_version.unwrap(),
|
||||
server_addr: config.server.address.ip().to_string(),
|
||||
server_port: config.server.address.port(),
|
||||
next_state: ClientState::Login.to_id(),
|
||||
},
|
||||
&tmp_client,
|
||||
&mut writer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Request login start
|
||||
let packet = LoginStart {
|
||||
name: client_info.username.ok_or(())?,
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let request = RawPacket::new(packets::login::SERVER_LOGIN_START, data).encode(&tmp_client)?;
|
||||
writer.write_all(&request).await.map_err(|_| ())?;
|
||||
packet::write_packet(
|
||||
LoginStart {
|
||||
name: client_info.username.ok_or(())?,
|
||||
},
|
||||
&tmp_client,
|
||||
&mut writer,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Incoming buffer
|
||||
let mut buf = BytesMut::new();
|
||||
|
@ -7,18 +7,17 @@ use std::time::Duration;
|
||||
use bytes::BytesMut;
|
||||
use minecraft_protocol::data::server_status::ServerStatus;
|
||||
use minecraft_protocol::decoder::Decoder;
|
||||
use minecraft_protocol::encoder::Encoder;
|
||||
use minecraft_protocol::version::v1_14_4::handshake::Handshake;
|
||||
use minecraft_protocol::version::v1_14_4::status::{PingRequest, PingResponse, StatusResponse};
|
||||
use minecraft_protocol::version::v1_14_4::status::{
|
||||
PingRequest, PingResponse, StatusRequest, StatusResponse,
|
||||
};
|
||||
use rand::Rng;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::proto::client::{Client, ClientState};
|
||||
use crate::proto::packet::{self, RawPacket};
|
||||
use crate::proto::packets;
|
||||
use crate::proto::{packet, packets};
|
||||
use crate::server::{Server, State};
|
||||
|
||||
/// Monitor ping inverval in seconds.
|
||||
@ -125,45 +124,28 @@ async fn send_handshake(
|
||||
config: &Config,
|
||||
addr: SocketAddr,
|
||||
) -> Result<(), ()> {
|
||||
let handshake = Handshake {
|
||||
protocol_version: config.public.protocol as i32,
|
||||
server_addr: addr.ip().to_string(),
|
||||
server_port: addr.port(),
|
||||
next_state: ClientState::Status.to_id(),
|
||||
};
|
||||
|
||||
let mut packet = Vec::new();
|
||||
handshake.encode(&mut packet).map_err(|_| ())?;
|
||||
|
||||
let raw = RawPacket::new(packets::handshake::SERVER_HANDSHAKE, packet)
|
||||
.encode(client)
|
||||
.map_err(|_| ())?;
|
||||
stream.write_all(&raw).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
packet::write_packet(
|
||||
Handshake {
|
||||
protocol_version: config.public.protocol as i32,
|
||||
server_addr: addr.ip().to_string(),
|
||||
server_port: addr.port(),
|
||||
next_state: ClientState::Status.to_id(),
|
||||
},
|
||||
client,
|
||||
&mut stream.split().1,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Send status request.
|
||||
async fn request_status(client: &Client, stream: &mut TcpStream) -> Result<(), ()> {
|
||||
let raw = RawPacket::new(packets::status::SERVER_STATUS, vec![])
|
||||
.encode(client)
|
||||
.map_err(|_| ())?;
|
||||
stream.write_all(&raw).await.map_err(|_| ())?;
|
||||
Ok(())
|
||||
packet::write_packet(StatusRequest {}, client, &mut stream.split().1).await
|
||||
}
|
||||
|
||||
/// Send status request.
|
||||
async fn send_ping(client: &Client, stream: &mut TcpStream) -> Result<u64, ()> {
|
||||
let token = rand::thread_rng().gen();
|
||||
let ping = PingRequest { time: token };
|
||||
|
||||
let mut packet = Vec::new();
|
||||
ping.encode(&mut packet).map_err(|_| ())?;
|
||||
|
||||
let raw = RawPacket::new(packets::status::SERVER_PING, packet)
|
||||
.encode(client)
|
||||
.map_err(|_| ())?;
|
||||
stream.write_all(&raw).await.map_err(|_| ())?;
|
||||
packet::write_packet(PingRequest { time: token }, client, &mut stream.split().1).await?;
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
|
@ -1,51 +1,36 @@
|
||||
use minecraft_protocol::data::chat::{Message, Payload};
|
||||
use minecraft_protocol::encoder::Encoder;
|
||||
use minecraft_protocol::version::v1_14_4::game::GameDisconnect;
|
||||
use minecraft_protocol::version::v1_14_4::login::LoginDisconnect;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::tcp::WriteHalf;
|
||||
|
||||
use crate::proto::client::{Client, ClientState};
|
||||
use crate::proto::packet::RawPacket;
|
||||
use crate::proto::packets;
|
||||
use crate::proto::packet;
|
||||
|
||||
/// Kick client with a message.
|
||||
///
|
||||
/// Should close connection afterwards.
|
||||
pub async fn kick(client: &Client, msg: &str, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||
match client.state() {
|
||||
ClientState::Login => login_kick(client, msg, writer).await,
|
||||
ClientState::Play => play_kick(client, msg, writer).await,
|
||||
ClientState::Login => {
|
||||
packet::write_packet(
|
||||
LoginDisconnect {
|
||||
reason: Message::new(Payload::text(msg)),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
ClientState::Play => {
|
||||
packet::write_packet(
|
||||
GameDisconnect {
|
||||
reason: Message::new(Payload::text(msg)),
|
||||
},
|
||||
client,
|
||||
writer,
|
||||
)
|
||||
.await
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Kick client with a message in login state.
|
||||
///
|
||||
/// Should close connection afterwards.
|
||||
async fn login_kick(client: &Client, msg: &str, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||
let packet = LoginDisconnect {
|
||||
reason: Message::new(Payload::text(msg)),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::login::CLIENT_DISCONNECT, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())
|
||||
}
|
||||
|
||||
/// Kick client with a message in play state.
|
||||
///
|
||||
/// Should close connection afterwards.
|
||||
async fn play_kick(client: &Client, msg: &str, writer: &mut WriteHalf<'_>) -> Result<(), ()> {
|
||||
let packet = GameDisconnect {
|
||||
reason: Message::new(Payload::text(msg)),
|
||||
};
|
||||
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packets::play::CLIENT_DISCONNECT, data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())
|
||||
}
|
||||
|
@ -4,9 +4,11 @@ use bytes::BytesMut;
|
||||
use flate2::read::ZlibDecoder;
|
||||
use flate2::write::ZlibEncoder;
|
||||
use flate2::Compression;
|
||||
use minecraft_protocol::encoder::Encoder;
|
||||
use minecraft_protocol::version::PacketId;
|
||||
use tokio::io;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::tcp::ReadHalf;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::tcp::{ReadHalf, WriteHalf};
|
||||
|
||||
use crate::proto::client::Client;
|
||||
use crate::proto::BUF_SIZE;
|
||||
@ -197,3 +199,18 @@ pub async fn read_packet(
|
||||
|
||||
Ok(Some((packet, raw.to_vec())))
|
||||
}
|
||||
|
||||
/// Write packet to stream writer.
|
||||
pub async fn write_packet(
|
||||
packet: impl PacketId + Encoder,
|
||||
client: &Client,
|
||||
writer: &mut WriteHalf<'_>,
|
||||
) -> Result<(), ()> {
|
||||
let mut data = Vec::new();
|
||||
packet.encode(&mut data).map_err(|_| ())?;
|
||||
|
||||
let response = RawPacket::new(packet.packet_id(), data).encode(client)?;
|
||||
writer.write_all(&response).await.map_err(|_| ())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user