composition/server/net/
mod.rs

1/// Definitions for all the packets in the Minecraft protocol.
2pub mod packets;
3
4use super::messages::*;
5use crate::{mctypes::*, CONFIG, FAVICON};
6use log::*;
7use packets::*;
8use serde_json::json;
9use std::sync::mpsc::Sender;
10use std::time::{Duration, Instant};
11use tokio::net::TcpStream;
12
13/// The network client can only be in a few states,
14/// this enum keeps track of that.
15#[derive(PartialEq, Debug)]
16pub enum NetworkClientState {
17    Handshake,
18    Status,
19    Login,
20    Play,
21    Disconnected,
22}
23
24/// A wrapper to contain everything related
25/// to networking for the client.
26#[derive(Debug)]
27pub struct NetworkClient {
28    pub id: u128,
29    pub connected: bool,
30    pub stream: TcpStream,
31    pub state: NetworkClientState,
32    pub uuid: Option<String>,
33    pub username: Option<String>,
34    pub last_keep_alive: Instant,
35    pub message_sender: Sender<ServerboundMessage>,
36}
37impl NetworkClient {
38    /// Create a new `NetworkClient`
39    pub fn new(
40        stream: TcpStream,
41        id: u128,
42        message_sender: Sender<ServerboundMessage>,
43    ) -> NetworkClient {
44        NetworkClient {
45            id,
46            connected: true,
47            stream,
48            state: NetworkClientState::Handshake,
49            uuid: None,
50            username: None,
51            last_keep_alive: Instant::now(),
52            message_sender,
53        }
54    }
55
56    /// Update the client.
57    ///
58    /// Updating could mean connecting new clients, reading packets,
59    /// writing packets, or disconnecting clients.
60    pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> {
61        // println!("{:?}", self);
62        match self.state {
63            NetworkClientState::Handshake => {
64                let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
65                let handshake = self.get_packet::<Handshake>().await?;
66                // Minecraft versions 1.8 - 1.8.9 use protocol version 47.
67                let compatible_versions = handshake.protocol_version == 47;
68                let next_state = match handshake.next_state.into() {
69                    1 => NetworkClientState::Status,
70                    2 => NetworkClientState::Login,
71                    _ => NetworkClientState::Disconnected,
72                };
73                self.state = next_state;
74                // If incompatible versions or wrong next state
75                if !compatible_versions {
76                    let mut logindisconnect = LoginDisconnect::new();
77                    logindisconnect.reason = MCChat {
78                        text: MCString::from("Incompatible client! Server is on 1.8.9"),
79                    };
80                    self.send_packet(logindisconnect).await?;
81                    self.state = NetworkClientState::Disconnected;
82                }
83            }
84            NetworkClientState::Status => {
85                let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
86                let _statusrequest = self.get_packet::<StatusRequest>().await?;
87                let mut statusresponse = StatusResponse::new();
88                statusresponse.json_response = json!({
89                    "version": {
90                        "name": "1.8.9",
91                        "protocol": 47,
92                    },
93                    "players": {
94                        "max": CONFIG.max_players,
95                        "online": num_players,
96                        "sample": [
97                            {
98                                "name": "shvr",
99                                "id": "e3f58380-60bb-4714-91f2-151d525e64aa"
100                            }
101                        ]
102                    },
103                    "description": {
104                        "text": CONFIG.motd
105                    },
106                    "favicon": format!("data:image/png;base64,{}", if FAVICON.is_ok() { radix64::STD.encode(FAVICON.as_ref().unwrap().as_slice()) } else { "".to_owned() })
107                })
108                .to_string()
109                .into();
110                self.send_packet(statusresponse).await?;
111                let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
112                let statusping = self.get_packet::<StatusPing>().await?;
113                let mut statuspong = StatusPong::new();
114                statuspong.payload = statusping.payload;
115                self.send_packet(statuspong).await?;
116                self.state = NetworkClientState::Disconnected;
117            }
118            NetworkClientState::Login => {
119                let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
120                let loginstart = self.get_packet::<LoginStart>().await?;
121                // Offline mode skips encryption and compression.
122                // TODO: Encryption and compression
123                let mut loginsuccess = LoginSuccess::new();
124                // We're in offline mode, so this is a temporary uuid.
125                // TODO: Get uuid and username from Mojang servers.
126                loginsuccess.uuid = "00000000-0000-3000-0000-000000000000".into();
127                loginsuccess.username = loginstart.player_name;
128                self.uuid = Some(loginsuccess.uuid.clone().into());
129                self.username = Some(loginsuccess.username.clone().into());
130                self.send_packet(loginsuccess).await?;
131                self.state = NetworkClientState::Play;
132                let joingame = JoinGame::new();
133                // TODO: Fill out `joingame` with actual information.
134                self.send_packet(joingame).await?;
135                let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
136                let _clientsettings = self.get_packet::<ClientSettings>().await?;
137                // TODO: Actually use client settings.
138                let helditemchange = HeldItemChange::new();
139                // TODO: Retrieve selected slot from storage.
140                self.send_packet(helditemchange).await?;
141                // TODO: S->C Declare Recipes (1.16?)
142                // TODO: S->C Tags (1.16?)
143                // TODO: S->C Entity Status (optional?)
144                // TODO: S->C Declare Commands (1.16?)
145                // TODO: S->C Unlock Recipes (1.16?)
146                // TODO: S->C Player Position and Look
147                let playerpositionandlook = ClientboundPlayerPositionAndLook::new();
148                // TODO: Retrieve player position from storage.
149                self.send_packet(playerpositionandlook).await?;
150                // TODO: S->C Player Info (Add Player action) (1.16?)
151                // TODO: S->C Player Info (Update latency action) (1.16?)
152                // TODO: S->C Update View Position (1.16?)
153                // TODO: S->C Update Light (1.16?)
154                // TODO: S->C Chunk Data
155                // TODO: S->C World Border
156                // TODO: S->C Spawn Position
157                let spawnposition = SpawnPosition::new();
158                self.send_packet(spawnposition).await?;
159                // Send initial keep alive.
160                self.keep_alive().await?;
161                // TODO: S->C Player Position and Look
162                // TODO: C->S Teleport Confirm
163                // TODO: C->S Player Position and Look
164                // TODO: C->S Client Status
165                // TODO: S->C inventories, entities, etc.
166                self.message_sender
167                    .send(ServerboundMessage::PlayerJoin(
168                        self.uuid
169                            .as_ref()
170                            .unwrap_or(&"00000000-0000-3000-0000-000000000000".to_owned())
171                            .to_string(),
172                        self.username
173                            .as_ref()
174                            .unwrap_or(&"unknown".to_owned())
175                            .to_string(),
176                    ))
177                    .expect("Message receiver disconnected");
178            }
179            NetworkClientState::Play => {
180                if self.last_keep_alive.elapsed() > Duration::from_millis(1000) {
181                    self.keep_alive().await?;
182                }
183                let (packet_length, packet_id) = read_packet_header(&mut self.stream).await?;
184                // debug!("{}", packet_id);
185                if packet_id == Player::id() {
186                    let _player = self.get_packet::<Player>().await?;
187                } else if packet_id == PlayerPosition::id() {
188                    let _playerposition = self.get_packet::<PlayerPosition>().await?;
189                } else if packet_id == PlayerLook::id() {
190                    let _playerlook = self.get_packet::<PlayerLook>().await?;
191                } else if packet_id == ServerboundPlayerPositionAndLook::id() {
192                    let _playerpositionandlook = self
193                        .get_packet::<ServerboundPlayerPositionAndLook>()
194                        .await?;
195                } else if packet_id == ServerboundChatMessage::id() {
196                    let serverboundchatmessage =
197                        self.get_packet::<ServerboundChatMessage>().await?;
198                    let reply = format!("<{}> {}", self.get_name(), serverboundchatmessage.text);
199                    info!("{}", reply);
200                    self.message_sender
201                        .send(ServerboundMessage::Chat(reply))
202                        .expect("Message receiver disconnected");
203                } else {
204                    let _ = read_bytes(&mut self.stream, Into::<i32>::into(packet_length) as usize)
205                        .await?;
206                }
207            }
208            NetworkClientState::Disconnected => {
209                if self.connected {
210                    self.disconnect("Disconnected").await?;
211                }
212            }
213        }
214        Ok(())
215    }
216
217    /// Send a generic packet to the client.
218    pub async fn send_packet<P: PacketCommon>(&mut self, packet: P) -> tokio::io::Result<()> {
219        debug!("Sent {:?} {:#04X?} {:?}", self.state, P::id(), packet);
220        packet.write(&mut self.stream).await
221    }
222
223    /// Read a generic packet from the network.
224    pub async fn get_packet<T: PacketCommon>(&mut self) -> tokio::io::Result<T> {
225        let packet = T::read(&mut self.stream).await?;
226        debug!("Got {:?} {:#04X?} {:?}", self.state, T::id(), packet);
227        Ok(packet)
228    }
229
230    /// Send the client a message in chat.
231    pub async fn send_chat_message<C: Into<MCChat>>(
232        &mut self,
233        message: C,
234    ) -> tokio::io::Result<()> {
235        let mut chatmessage = ClientboundChatMessage::new();
236        chatmessage.text = message.into();
237        self.send_packet(chatmessage).await?;
238        Ok(())
239    }
240
241    /// Disconnect the client.
242    ///
243    /// Sends `0x40 Disconnect` then waits 10 seconds before forcing the connection closed.
244    pub async fn disconnect<S: Into<MCString>>(&mut self, reason: S) -> tokio::io::Result<()> {
245        let mut disconnect = Disconnect::new();
246        disconnect.reason.text = reason.into();
247        self.send_packet(disconnect).await?;
248        self.force_disconnect();
249        Ok(())
250    }
251
252    /// Force disconnect the client by marking it for cleanup as disconnected.
253    pub fn force_disconnect(&mut self) {
254        self.connected = false;
255        self.state = NetworkClientState::Disconnected;
256    }
257
258    /// Send a keep alive packet to the client.
259    pub async fn keep_alive(&mut self) -> tokio::io::Result<()> {
260        if cfg!(debug_assertions) {
261            self.send_chat_message("keep alive").await?;
262        }
263        // Keep alive ping to client.
264        let clientboundkeepalive = KeepAlivePing::new();
265        self.send_packet(clientboundkeepalive).await?;
266        // Keep alive pong to server.
267        let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?;
268        let _serverboundkeepalive = self.get_packet::<KeepAlivePong>().await?;
269        self.last_keep_alive = Instant::now();
270        Ok(())
271    }
272
273    /// Helper function to get the name of the player.
274    pub fn get_name(&self) -> String {
275        self.username
276            .as_ref()
277            .unwrap_or(&"unknown".to_owned())
278            .to_string()
279    }
280
281    /// Receives broadcast messages from the server.
282    pub async fn handle_broadcast_message(
283        &mut self,
284        message: BroadcastMessage,
285    ) -> tokio::io::Result<()> {
286        use BroadcastMessage::*;
287        match message {
288            Chat(s) => self.send_chat_message(s).await?,
289            Disconnect(reason) => self.disconnect(reason).await?,
290        }
291        Ok(())
292    }
293}