diff --git a/Cargo.lock b/Cargo.lock index ff52592..a1e4c39 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,6 +104,7 @@ dependencies = [ "serde_json", "tokio", "toml", + "uuid", ] [[package]] @@ -586,6 +587,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index 8f7fcd3..5baa10f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ async-trait = "0.1.48" lazy_static = "1.4.0" ctrlc = "3.1.8" futures = "0.3.13" +uuid = "0.8.2" # colorful = "0.2.1" # ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2) # toml = "0.5.6" diff --git a/src/entity/player.rs b/src/entity/player.rs index cf69bf6..4bdd975 100644 --- a/src/entity/player.rs +++ b/src/entity/player.rs @@ -1,8 +1,38 @@ -use crate::server::net::NetworkClient; use crate::world::location::Location; +use uuid::Uuid; +#[derive(Debug, Clone, PartialEq)] pub struct Player { - position: Location, - display_name: String, - connection: NetworkClient, + pub position: Location, + pub uuid: Uuid, + names: (String, Option), +} +impl Player { + pub fn new() -> Player { + Player { + position: Location::zero(), + uuid: Uuid::nil(), + names: ("Player".into(), None), + } + } + pub fn username(&self) -> &String { + &self.names.0 + } + pub fn username_mut(&mut self) -> &mut String { + &mut self.names.0 + } + pub fn display_name(&self) -> &String { + match &self.names.1 { + Some(name) => name, + None => &self.names.0, + } + } + pub fn display_name_mut(&mut self) -> &mut Option { + &mut self.names.1 + } +} +impl Default for Player { + fn default() -> Self { + Player::new() + } } diff --git a/src/mctypes/numbers.rs b/src/mctypes/numbers.rs index d4d6ca1..89160a7 100644 --- a/src/mctypes/numbers.rs +++ b/src/mctypes/numbers.rs @@ -636,26 +636,28 @@ pub struct MCVarInt { impl MCVarInt { pub async fn read(t: &mut TcpStream) -> tokio::io::Result { let mut num_read = 0; - let mut result = 0i32; + let mut result = 0i128; let mut read = 0u8; let mut run_once = false; while (read & 0b10000000) != 0 || !run_once { run_once = true; read = read_byte(t).await?; - let value = (read & 0b01111111) as i32; + let value = (read & 0b01111111) as i128; result |= value << (7 * num_read); num_read += 1; if num_read > 5 { return Err(io_error("MCVarInt is too big")); } } - Ok(MCVarInt { value: result }) + Ok(MCVarInt { + value: result as i32, + }) } pub fn from_bytes(_v: Vec) -> MCVarInt { unimplemented!() } pub fn to_bytes(&self) -> Vec { - unimplemented!() + Into::>::into(*self) } } impl From for MCVarInt { diff --git a/src/server/messages.rs b/src/server/messages.rs index a91d51b..3a11353 100644 --- a/src/server/messages.rs +++ b/src/server/messages.rs @@ -5,10 +5,7 @@ pub enum ServerboundMessage { } #[derive(Debug, PartialEq, Clone)] -pub enum ClientboundMessage {} - -#[derive(Debug, PartialEq, Clone)] -pub enum BroadcastMessage { +pub enum ClientboundMessage { Chat(String), // The chat message. Disconnect(String), // The reason for disconnecting. } diff --git a/src/server/mod.rs b/src/server/mod.rs index d134231..9153225 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,25 +4,28 @@ pub mod messages; pub mod net; use crate::entity::player::Player; +use crate::{mctypes::*, CONFIG, FAVICON}; use log::*; use messages::*; -use net::*; +use net::{ + packets::{self, Packet, PacketCommon}, + *, +}; +use serde_json::json; use std::sync::mpsc::{self, Receiver, TryRecvError}; +use std::time::Duration; use tokio::net::{TcpListener, ToSocketAddrs}; /// The struct containing all the data and running all the updates. pub struct Server { network_clients: Vec, network_receiver: Receiver, - message_receiver: Receiver, - // message_sender: Bus, pub players: Vec, } impl Server { pub fn new(addr: A) -> Server { let (network_client_tx, network_client_rx) = mpsc::channel(); - let (serverbound_message_tx, serverbound_message_rx) = mpsc::channel(); - // let mut broadcast_message_tx = Bus::new(1_000); // Hold up to 1,000 messages in the queue. + let (serverbound_message_tx, _serverbound_message_rx) = mpsc::channel(); tokio::task::spawn(async move { let listener = TcpListener::bind(addr) .await @@ -38,7 +41,6 @@ impl Server { stream, id as u128, serverbound_message_tx.clone(), - // broadcast_message_tx.add_rx(), )) .expect("Network receiver disconnected"); id += 1; @@ -48,7 +50,7 @@ impl Server { Server { network_receiver: network_client_rx, network_clients: vec![], - message_receiver: serverbound_message_rx, + // message_receiver: serverbound_message_rx, players: vec![], } } @@ -61,82 +63,210 @@ impl Server { "Server shutting down. Uptime: {:?}", crate::START_TIME.elapsed() ); - self.broadcast_message(BroadcastMessage::Disconnect( + self.broadcast_message(ClientboundMessage::Disconnect( "The server is shutting down".into(), )) .await; } - /// Update the network server. - /// - /// Update each client in `self.network_clients`. - async fn update_network(&mut self) -> tokio::io::Result<()> { - // Read new clients from the network. + pub fn num_players(&self) -> usize { + let mut num = 0; + for client in &self.network_clients { + if client.state == NetworkClientState::Play { + num += 1; + } + } + num + } + + /// Send a `ClientboundMessage` to all connected network clients. + pub async fn broadcast_message(&mut self, message: ClientboundMessage) { + let mut v = Vec::new(); + for client in self.network_clients.iter_mut() { + v.push(client.handle_message(message.clone())); + } + futures::future::join_all(v).await; + } + + /// Get a client from their id. + pub fn client_from_id(&mut self, client_id: u128) -> Option<&mut NetworkClient> { + // Find the client based on id. + let mut client_index = -1isize; + for (i, c) in self.network_clients.iter().enumerate() { + if c.id == client_id { + client_index = i as isize; + break; + } + } + if client_index == -1 { + return None; + } + Some(&mut self.network_clients[client_index as usize]) + } + + /// Update the game server. + pub async fn update(&mut self) -> tokio::io::Result<()> { + // Get new clients from the network listener thread. loop { match self.network_receiver.try_recv() { Ok(client) => { info!( "Got client at {}", - client.stream.peer_addr().expect("Could not get peer addr") + client.stream.peer_addr().expect("could not get peer addr") ); self.network_clients.push(client) } Err(TryRecvError::Empty) => break, - Err(TryRecvError::Disconnected) => panic!("Network sender disconnected"), + Err(TryRecvError::Disconnected) => panic!("network sender disconnected"), } } - // Count the number of players in the Play state. - let num_players = self.network_clients.iter().fold(0, |acc, nc| { - if nc.state == NetworkClientState::Play { - acc + 1 - } else { - acc - } - }); - // Update each client, disconnecting those with errors. + // Read new packets from each client. for client in self.network_clients.iter_mut() { - if client.update(num_players).await.is_err() { + // Update and ignore errors. + if client.update().await.is_err() { + client.force_disconnect(); + } + } + // Update the client and server according to each packet. + let mut packets = vec![]; + for client in self.network_clients.iter_mut() { + while let Some(packet) = client.read_packet() { + packets.push((client.id, packet)); + } + } + for (client_id, packet) in packets { + if self.handle_packet(client_id, packet).await.is_err() { + self.client_from_id(client_id).unwrap().force_disconnect(); + } + } + // Disconnect clients when necessary. + for client in self.network_clients.iter_mut() { + if client.state == NetworkClientState::Disconnected { + client.force_disconnect(); + } else if client.last_keep_alive.elapsed() > Duration::from_secs(20) { + debug!("Disconnecting client for timing out"); + client.state = NetworkClientState::Disconnected; client.force_disconnect(); } } // Remove disconnected clients. self.network_clients .retain(|nc| nc.state != NetworkClientState::Disconnected); - // Read new messages from the clients. - loop { - match self.message_receiver.try_recv() { - Ok(message) => match message { - ServerboundMessage::Chat(msg) => { - self.broadcast_message(BroadcastMessage::Chat(msg)).await; - } - ServerboundMessage::PlayerJoin(_uuid, username) => { - self.broadcast_message(BroadcastMessage::Chat(format!( - "Welcome {} to the server!", - username - ))) - .await; - } - }, - Err(TryRecvError::Empty) => break, - Err(TryRecvError::Disconnected) => panic!("Message sender disconnected"), - } - } Ok(()) } - pub async fn broadcast_message(&mut self, message: BroadcastMessage) { - let mut v = Vec::new(); - for client in self.network_clients.iter_mut() { - v.push(client.handle_broadcast_message(message.clone())); + /// Handle a packet. + pub async fn handle_packet( + &mut self, + client_id: u128, + packet: P, + ) -> tokio::io::Result<()> { + let num_players = self.num_players(); + let mut client = self.client_from_id(client_id).unwrap(); + match packet.as_packet() { + // Handshaking. + Packet::Handshake(handshake) => { + if handshake.next_state == 1 { + client.state = NetworkClientState::Status; + } else if handshake.next_state == 2 { + client.state = NetworkClientState::Login; + } else { + client.state = NetworkClientState::Disconnected; + } + if handshake.protocol_version != 47 { + let mut logindisconnect = packets::LoginDisconnect::new(); + logindisconnect.reason = MCChat { + text: MCString::from("Incompatible client! Server is on 1.8.9"), + }; + client.send_packet(logindisconnect).await?; + client.state = NetworkClientState::Disconnected; + } + } + // Status. + Packet::StatusRequest(_statusrequest) => { + let mut statusresponse = packets::StatusResponse::new(); + statusresponse.json_response = json!({ + "version": { + "name": "1.8.9", + "protocol": 47, + }, + "players": { + "max": CONFIG.max_players, + "online": num_players, + "sample": [ + { + "name": "shvr", + "id": "e3f58380-60bb-4714-91f2-151d525e64aa" + } + ] + }, + "description": { + "text": CONFIG.motd + }, + "favicon": format!("data:image/png;base64,{}", if FAVICON.is_ok() { radix64::STD.encode(FAVICON.as_ref().unwrap().as_slice()) } else { "".to_owned() }) + }) + .to_string() + .into(); + client.send_packet(statusresponse).await?; + } + Packet::StatusPing(statusping) => { + let mut statuspong = packets::StatusPong::new(); + statuspong.payload = statusping.payload; + client.send_packet(statuspong).await?; + } + // Login. + Packet::LoginStart(loginstart) => { + client.player = Some(crate::entity::player::Player::new()); + let player = client.player.as_mut().unwrap(); + *player.username_mut() = loginstart.player_name.into(); + // Offline mode skips encryption and compression. + // TODO: Encryption and compression + let mut loginsuccess = packets::LoginSuccess::new(); + // We're in offline mode, so this is a temporary uuid. + // TODO: Get uuid and username from Mojang servers. + loginsuccess.uuid = player.uuid.clone().to_hyphenated().to_string().into(); + loginsuccess.username = player.username().clone().into(); + client.send_packet(loginsuccess).await?; + client.state = NetworkClientState::Play; + client.send_packet(packets::JoinGame::new()).await?; + } + Packet::ClientSettings(_clientsettings) => { + // TODO: Handle the packet. + client.send_packet(packets::HeldItemChange::new()).await?; + client + .send_packet(packets::ClientboundPlayerPositionAndLook::new()) + .await?; + client.send_packet(packets::SpawnPosition::new()).await?; + } + // Play. + Packet::KeepAlivePong(_keepalivepong) => { + // TODO: Handle the packet. + } + Packet::ServerboundChatMessage(chatmessage) => { + let player_name = client.player.as_ref().unwrap().username().clone(); + info!("<{}> {}", player_name, chatmessage.text); + self.broadcast_message(ClientboundMessage::Chat(format!( + "<{}> {}", + player_name, chatmessage.text + ))) + .await; + // TODO: Handle the packet. + } + Packet::Player(_player) => { + // TODO: Handle the packet. + } + Packet::PlayerPosition(_playerposition) => { + // TODO: Handle the packet. + } + Packet::PlayerLook(_playerlook) => { + // TODO: Handle the packet. + } + Packet::ServerboundPlayerPositionAndLook(_playerpositionandlook) => { + // TODO: Handle the packet. + } + // Other. + _ => error!("handling unknown packet type: {:?}", packet), } - futures::future::join_all(v).await; - } - - /// Update the game server. - /// - /// Start by updating the network. - pub async fn update(&mut self) -> tokio::io::Result<()> { - self.update_network().await?; Ok(()) } } diff --git a/src/server/net/mod.rs b/src/server/net/mod.rs index 5fd55d6..398a730 100644 --- a/src/server/net/mod.rs +++ b/src/server/net/mod.rs @@ -2,12 +2,15 @@ pub mod packets; use super::messages::*; -use crate::{mctypes::*, CONFIG, FAVICON}; +use crate::mctypes::*; use log::*; use packets::*; -use serde_json::json; -use std::sync::mpsc::Sender; -use std::time::{Duration, Instant}; +// use serde_json::json; +use std::{ + collections::VecDeque, + sync::mpsc::Sender, + time::{Duration, Instant}, +}; use tokio::net::TcpStream; /// The network client can only be in a few states, @@ -29,10 +32,10 @@ pub struct NetworkClient { pub connected: bool, pub stream: TcpStream, pub state: NetworkClientState, - pub uuid: Option, - pub username: Option, pub last_keep_alive: Instant, pub message_sender: Sender, + packets: VecDeque, + pub player: Option, } impl NetworkClient { /// Create a new `NetworkClient` @@ -46,174 +49,101 @@ impl NetworkClient { connected: true, stream, state: NetworkClientState::Handshake, - uuid: None, - username: None, last_keep_alive: Instant::now(), message_sender, + packets: VecDeque::new(), + player: None, } } - /// Update the client. - /// - /// Updating could mean connecting new clients, reading packets, - /// writing packets, or disconnecting clients. - pub async fn update(&mut self, num_players: usize) -> tokio::io::Result<()> { - // println!("{:?}", self); - match self.state { - NetworkClientState::Handshake => { - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let handshake = self.get_packet::().await?; - // Minecraft versions 1.8 - 1.8.9 use protocol version 47. - let compatible_versions = handshake.protocol_version == 47; - let next_state = match handshake.next_state.into() { - 1 => NetworkClientState::Status, - 2 => NetworkClientState::Login, - _ => NetworkClientState::Disconnected, - }; - self.state = next_state; - // If incompatible versions or wrong next state - if !compatible_versions { - let mut logindisconnect = LoginDisconnect::new(); - logindisconnect.reason = MCChat { - text: MCString::from("Incompatible client! Server is on 1.8.9"), - }; - self.send_packet(logindisconnect).await?; - self.state = NetworkClientState::Disconnected; - } - } - NetworkClientState::Status => { - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let _statusrequest = self.get_packet::().await?; - let mut statusresponse = StatusResponse::new(); - statusresponse.json_response = json!({ - "version": { - "name": "1.8.9", - "protocol": 47, - }, - "players": { - "max": CONFIG.max_players, - "online": num_players, - "sample": [ - { - "name": "shvr", - "id": "e3f58380-60bb-4714-91f2-151d525e64aa" - } - ] - }, - "description": { - "text": CONFIG.motd - }, - "favicon": format!("data:image/png;base64,{}", if FAVICON.is_ok() { radix64::STD.encode(FAVICON.as_ref().unwrap().as_slice()) } else { "".to_owned() }) - }) - .to_string() - .into(); - self.send_packet(statusresponse).await?; - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let statusping = self.get_packet::().await?; - let mut statuspong = StatusPong::new(); - statuspong.payload = statusping.payload; - self.send_packet(statuspong).await?; - self.state = NetworkClientState::Disconnected; - } - NetworkClientState::Login => { - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let loginstart = self.get_packet::().await?; - // Offline mode skips encryption and compression. - // TODO: Encryption and compression - let mut loginsuccess = LoginSuccess::new(); - // We're in offline mode, so this is a temporary uuid. - // TODO: Get uuid and username from Mojang servers. - loginsuccess.uuid = "00000000-0000-3000-0000-000000000000".into(); - loginsuccess.username = loginstart.player_name; - self.uuid = Some(loginsuccess.uuid.clone().into()); - self.username = Some(loginsuccess.username.clone().into()); - self.send_packet(loginsuccess).await?; - self.state = NetworkClientState::Play; - let joingame = JoinGame::new(); - // TODO: Fill out `joingame` with actual information. - self.send_packet(joingame).await?; - let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let _clientsettings = self.get_packet::().await?; - // TODO: Actually use client settings. - let helditemchange = HeldItemChange::new(); - // TODO: Retrieve selected slot from storage. - self.send_packet(helditemchange).await?; - // TODO: S->C Declare Recipes (1.16?) - // TODO: S->C Tags (1.16?) - // TODO: S->C Entity Status (optional?) - // TODO: S->C Declare Commands (1.16?) - // TODO: S->C Unlock Recipes (1.16?) - // TODO: S->C Player Position and Look - let playerpositionandlook = ClientboundPlayerPositionAndLook::new(); - // TODO: Retrieve player position from storage. - self.send_packet(playerpositionandlook).await?; - // TODO: S->C Player Info (Add Player action) (1.16?) - // TODO: S->C Player Info (Update latency action) (1.16?) - // TODO: S->C Update View Position (1.16?) - // TODO: S->C Update Light (1.16?) - // TODO: S->C Chunk Data - // TODO: S->C World Border - // TODO: S->C Spawn Position - let spawnposition = SpawnPosition::new(); - self.send_packet(spawnposition).await?; - // Send initial keep alive. - self.keep_alive().await?; - // TODO: S->C Player Position and Look - // TODO: C->S Teleport Confirm - // TODO: C->S Player Position and Look - // TODO: C->S Client Status - // TODO: S->C inventories, entities, etc. - self.message_sender - .send(ServerboundMessage::PlayerJoin( - self.uuid - .as_ref() - .unwrap_or(&"00000000-0000-3000-0000-000000000000".to_owned()) - .to_string(), - self.username - .as_ref() - .unwrap_or(&"unknown".to_owned()) - .to_string(), - )) - .expect("Message receiver disconnected"); - } - NetworkClientState::Play => { - if self.last_keep_alive.elapsed() > Duration::from_millis(1000) { - self.keep_alive().await?; - } - let (packet_length, packet_id) = read_packet_header(&mut self.stream).await?; - // debug!("{}", packet_id); - if packet_id == Player::id() { - let _player = self.get_packet::().await?; - } else if packet_id == PlayerPosition::id() { - let _playerposition = self.get_packet::().await?; - } else if packet_id == PlayerLook::id() { - let _playerlook = self.get_packet::().await?; - } else if packet_id == ServerboundPlayerPositionAndLook::id() { - let _playerpositionandlook = self - .get_packet::() - .await?; - } else if packet_id == ServerboundChatMessage::id() { - let serverboundchatmessage = - self.get_packet::().await?; - let reply = format!("<{}> {}", self.get_name(), serverboundchatmessage.text); - info!("{}", reply); - self.message_sender - .send(ServerboundMessage::Chat(reply)) - .expect("Message receiver disconnected"); - } else { - let _ = read_bytes(&mut self.stream, Into::::into(packet_length) as usize) - .await?; - } - } - NetworkClientState::Disconnected => { - if self.connected { - self.disconnect("Disconnected").await?; + /// Try to read a new packet into the processing queue. + pub async fn update(&mut self) -> tokio::io::Result<()> { + // Don't try to read packets if disconnected. + if self.state == NetworkClientState::Disconnected { + return Ok(()); + } + if self.stream.peek(&mut [0u8; 4096]).await? > 0 { + // Read the packet header. + let (_packet_length, packet_id) = read_packet_header(&mut self.stream).await?; + // Get the packet based on packet_id. + let packet = match packet_id.value { + 0x00 => match self.state { + NetworkClientState::Handshake => { + Some(self.get_wrapped_packet::().await) + } + NetworkClientState::Status => { + Some(self.get_wrapped_packet::().await) + } + NetworkClientState::Login => { + Some(self.get_wrapped_packet::().await) + } + NetworkClientState::Play => { + Some(self.get_wrapped_packet::().await) + } + _ => None, + }, + 0x01 => { + match self.state { + NetworkClientState::Status => { + Some(self.get_wrapped_packet::().await) + } + NetworkClientState::Login => None, // TODO: 0x01 Encryption Response + NetworkClientState::Play => { + Some(self.get_wrapped_packet::().await) + } + _ => None, + } } + // The rest of the packets are all always in the play state. + 0x02 => None, // TODO: 0x02 Use Entity + 0x03 => Some(self.get_wrapped_packet::().await), + 0x04 => Some(self.get_wrapped_packet::().await), + 0x05 => Some(self.get_wrapped_packet::().await), + 0x06 => Some( + self.get_wrapped_packet::() + .await, + ), + 0x07 => None, // TODO: 0x07 Player Digging + 0x08 => None, // TODO: 0x08 Player Block Placement + 0x09 => None, // TODO: 0x09 Held Item Change + 0x0a => None, // TODO: 0x0a Animation + 0x0b => None, // TODO: 0x0b Entity Action + 0x0c => None, // TODO: 0x0c Steer Vehicle + 0x0d => None, // TODO: 0x0d Close Window + 0x0e => None, // TODO: 0x0e Click Window + 0x0f => None, // TODO: 0x0f Confirm Transaction + 0x10 => None, // TODO: 0x10 Creative Inventory Action + 0x11 => None, // TODO: 0x11 Enchant Item + 0x12 => None, // TODO: 0x12 Update Sign + 0x13 => None, // TODO: 0x13 Player Abilities + 0x14 => None, // TODO: 0x14 Tab-Complete + 0x15 => Some(self.get_wrapped_packet::().await), + 0x16 => None, // TODO: 0x16 Client Status + 0x17 => None, // TODO: 0x17 Plugin Message + 0x18 => None, // TODO: 0x18 Spectate + 0x19 => None, // TODO: 0x19 Resource Pack Status + _ => None, + }; + if let Some(Ok(packet)) = packet { + // Add it to the internal queue to be processed. + self.packets.push_back(packet); } } + if self.last_keep_alive.elapsed() > Duration::from_millis(1000) { + debug!( + "Sending keep alive, last one was {:?} ago", + self.last_keep_alive.elapsed() + ); + self.keep_alive().await?; + } Ok(()) } + /// Pop a packet from the queue. + pub fn read_packet(&mut self) -> Option { + self.packets.pop_front() + } + /// Send a generic packet to the client. pub async fn send_packet(&mut self, packet: P) -> tokio::io::Result<()> { debug!("Sent {:?} {:#04X?} {:?}", self.state, P::id(), packet); @@ -221,12 +151,19 @@ impl NetworkClient { } /// Read a generic packet from the network. - pub async fn get_packet(&mut self) -> tokio::io::Result { + async fn get_packet(&mut self) -> tokio::io::Result { let packet = T::read(&mut self.stream).await?; debug!("Got {:?} {:#04X?} {:?}", self.state, T::id(), packet); Ok(packet) } + /// Read a generic packet from the network and wrap it in `Packet`. + async fn get_wrapped_packet(&mut self) -> tokio::io::Result { + let packet = T::read(&mut self.stream).await?; + debug!("Got {:?} {:#04X?} {:?}", self.state, T::id(), packet); + Ok(packet.as_packet()) + } + /// Send the client a message in chat. pub async fn send_chat_message>( &mut self, @@ -258,32 +195,20 @@ impl NetworkClient { /// Send a keep alive packet to the client. pub async fn keep_alive(&mut self) -> tokio::io::Result<()> { if cfg!(debug_assertions) { - self.send_chat_message("keep alive").await?; + // self.send_chat_message("keep alive").await?; } // Keep alive ping to client. - let clientboundkeepalive = KeepAlivePing::new(); - self.send_packet(clientboundkeepalive).await?; + self.send_packet(KeepAlivePing::new()).await?; // Keep alive pong to server. let (_packet_length, _packet_id) = read_packet_header(&mut self.stream).await?; - let _serverboundkeepalive = self.get_packet::().await?; + let _ = self.get_packet::().await?; self.last_keep_alive = Instant::now(); Ok(()) } - /// Helper function to get the name of the player. - pub fn get_name(&self) -> String { - self.username - .as_ref() - .unwrap_or(&"unknown".to_owned()) - .to_string() - } - - /// Receives broadcast messages from the server. - pub async fn handle_broadcast_message( - &mut self, - message: BroadcastMessage, - ) -> tokio::io::Result<()> { - use BroadcastMessage::*; + /// Receives messages from the server. + pub async fn handle_message(&mut self, message: ClientboundMessage) -> tokio::io::Result<()> { + use ClientboundMessage::*; match message { Chat(s) => self.send_chat_message(s).await?, Disconnect(reason) => self.disconnect(reason).await?, diff --git a/src/server/net/packets/mod.rs b/src/server/net/packets/mod.rs index 830cd39..9d158de 100644 --- a/src/server/net/packets/mod.rs +++ b/src/server/net/packets/mod.rs @@ -96,12 +96,30 @@ register_packets!( ); #[async_trait::async_trait] -pub trait PacketCommon: Into + core::fmt::Debug +pub trait PacketCommon: Into + core::fmt::Debug + Clone where Self: Sized, { fn new() -> Self; fn id() -> u8; + fn as_packet(&self) -> Packet { + self.clone().into() + } async fn read(t: &mut TcpStream) -> tokio::io::Result; async fn write(&self, t: &mut TcpStream) -> tokio::io::Result<()>; } +#[async_trait::async_trait] +impl PacketCommon for Packet { + fn new() -> Self { + Packet::new() + } + fn id() -> u8 { + 255 // The generic `Packet` doesn't really have an id, but I can't leave it blank. + } + async fn read(_t: &mut TcpStream) -> tokio::io::Result { + panic!("cannot PacketCommon::read a generic Packet") + } + async fn write(&self, _t: &mut TcpStream) -> tokio::io::Result<()> { + panic!("cannot PacketCommon::write a generic Packet") + } +} diff --git a/src/world/location.rs b/src/world/location.rs index e283673..8a9fb24 100644 --- a/src/world/location.rs +++ b/src/world/location.rs @@ -1,4 +1,5 @@ /// Used to store a point in a world. +#[derive(Debug, Clone, PartialEq)] pub struct Location { pub x: f64, pub y: f64,