diff --git a/pumpkin-core/src/text/mod.rs b/pumpkin-core/src/text/mod.rs index 706c034ea..d953e0e4b 100644 --- a/pumpkin-core/src/text/mod.rs +++ b/pumpkin-core/src/text/mod.rs @@ -40,6 +40,13 @@ impl<'a> TextComponent<'a> { } } + pub fn text_string(text: String) -> Self { + Self { + content: TextContent::Text { text: text.into() }, + style: Style::default(), + } + } + pub fn to_pretty_console(self) -> String { let style = self.style; let color = style.color; diff --git a/pumpkin-world/src/level.rs b/pumpkin-world/src/level.rs index 866fd9759..e7e5327cb 100644 --- a/pumpkin-world/src/level.rs +++ b/pumpkin-world/src/level.rs @@ -6,7 +6,9 @@ use rayon::prelude::*; use tokio::sync::mpsc; use crate::{ - chunk::{anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError}, + chunk::{ + anvil::AnvilChunkReader, ChunkData, ChunkParsingError, ChunkReader, ChunkReadingError, + }, world_gen::{get_world_gen, Seed, WorldGenerator}, }; @@ -96,7 +98,10 @@ impl Level { let data = match &self.save_file { Some(save_file) => { match self.chunk_reader.read_chunk(save_file, at) { - Err(ChunkReadingError::ParsingError(ChunkParsingError::ChunkNotGenerated) | ChunkReadingError::ChunkNotExist) => { + Err( + ChunkReadingError::ParsingError(ChunkParsingError::ChunkNotGenerated) + | ChunkReadingError::ChunkNotExist, + ) => { // This chunk was not generated yet. Ok(self.world_gen.generate_chunk(at)) } diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 60b6243c0..a999231a6 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -1,6 +1,7 @@ use crate::entity::player::Player; use crate::server::Server; use itertools::Itertools; +use pumpkin_core::text::TextComponent; use pumpkin_core::GameMode; use pumpkin_inventory::container_click::{ Click, ClickType, KeyClick, MouseClick, MouseDragState, MouseDragType, @@ -10,7 +11,7 @@ use pumpkin_inventory::window_property::{WindowProperty, WindowPropertyTrait}; use pumpkin_inventory::Container; use pumpkin_inventory::{container_click, InventoryError, OptionallyCombinedContainer}; use pumpkin_protocol::client::play::{ - CCloseContainer, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, + CCloseContainer, COpenScreen, CSetContainerContent, CSetContainerProperty, CSetContainerSlot, }; use pumpkin_protocol::server::play::SClickContainer; use pumpkin_protocol::slot::Slot; @@ -26,28 +27,30 @@ impl Player { let total_opened_containers = inventory.total_opened_containers; let container = self.get_open_container(server); let container = container.as_ref().map(|container| container.lock()); - // let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY - // .get("minecraft:menu") - // .unwrap() - // .entries - // .get(minecraft_menu_id) - // .expect("Should be a valid menu id") - // .get("protocol_id") - // .unwrap()) - // .into(); - // let window_title = container - // .as_ref() - // .map(|container| container.window_name()) - // .unwrap_or_else(|| inventory.window_name()); - // let title = TextComponent::text(window_title); + let menu_protocol_id = (*pumpkin_world::global_registry::REGISTRY + .get("minecraft:menu") + .unwrap() + .entries + .get(minecraft_menu_id) + .expect("Should be a valid menu id") + .get("protocol_id") + .unwrap()) + .into(); + let window_title = match container { + Some(container) => container.await.window_name(), + None => inventory.window_name(), + }; + let title = TextComponent::text(window_title); - // self.client.send_packet(&COpenScreen::new( - // total_opened_containers.into(), - // menu_protocol_id, - // title, - // )); + self.client + .send_packet(&COpenScreen::new( + total_opened_containers.into(), + menu_protocol_id, + title, + )) + .await; drop(inventory); - // self.set_container_content(container.as_deref_mut()); + // self.set_container_content(container.as_deref_mut()); } pub async fn set_container_content(&self, container: Option<&mut Box>) { diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index f86bc9771..deeed543a 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -98,17 +98,17 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { let target = parse_arg_player(sender, server, ARG_TARGET, args)?; if target.gamemode.load() == gamemode { - sender.send_message(TextComponent::text(&format!( - "{} is already in {:?} gamemode", - target.gameprofile.name, gamemode - ))); + // sender.send_message(TextComponent::text(&format!( + // "{} is already in {:?} gamemode", + // target.gameprofile.name, gamemode + // ))); } else { // TODO target.set_gamemode(gamemode); - sender.send_message(TextComponent::text(&format!( - "{}'s Game mode was set to {:?}", - target.gameprofile.name, gamemode - ))); + // sender.send_message(TextComponent::text(&format!( + // "{}'s Game mode was set to {:?}", + // target.gameprofile.name, gamemode + // ))); } Ok(()) diff --git a/pumpkin/src/commands/cmd_help.rs b/pumpkin/src/commands/cmd_help.rs index 18dddc6e7..9795f84dc 100644 --- a/pumpkin/src/commands/cmd_help.rs +++ b/pumpkin/src/commands/cmd_help.rs @@ -43,12 +43,12 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { argument(ARG_COMMAND, consume_arg_command).execute(&|sender, server, args| { let tree = parse_arg_command(args, &server.command_dispatcher)?; - sender.send_message(TextComponent::text(&format!( - "{} - {} Usage: {}", - tree.names.join("/"), - tree.description, - tree - ))); + // sender.send_message(TextComponent::text(&format!( + // "{} - {} Usage: {}", + // tree.names.join("/"), + // tree.description, + // tree + // ))); Ok(()) }), @@ -62,12 +62,12 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { continue; }; - sender.send_message(TextComponent::text(&format!( - "{} - {} Usage: {}", - tree.names.join("/"), - tree.description, - tree - ))); + // sender.send_message(TextComponent::text(&format!( + // "{} - {} Usage: {}", + // tree.names.join("/"), + // tree.description, + // tree + // ))); } Ok(()) diff --git a/pumpkin/src/commands/cmd_pumpkin.rs b/pumpkin/src/commands/cmd_pumpkin.rs index b13da29ff..b697e458c 100644 --- a/pumpkin/src/commands/cmd_pumpkin.rs +++ b/pumpkin/src/commands/cmd_pumpkin.rs @@ -1,4 +1,7 @@ -use crate::commands::tree::CommandTree; +use pumpkin_core::text::{color::NamedColor, TextComponent}; +use pumpkin_protocol::CURRENT_MC_PROTOCOL; + +use crate::{commands::tree::CommandTree, server::CURRENT_MC_VERSION}; const NAMES: [&str; 1] = ["pumpkin"]; @@ -11,7 +14,7 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> { // sender.send_message(TextComponent::text( // &format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})") - // ).color_named(NamedColor::Green)); + // ).color_named(NamedColor::Green)).await; Ok(()) }) diff --git a/pumpkin/src/commands/dispatcher.rs b/pumpkin/src/commands/dispatcher.rs index 6a044388f..304ae50e8 100644 --- a/pumpkin/src/commands/dispatcher.rs +++ b/pumpkin/src/commands/dispatcher.rs @@ -1,3 +1,5 @@ +use pumpkin_core::text::TextComponent; + use crate::commands::dispatcher::InvalidTreeError::{ InvalidConsumptionError, InvalidRequirementError, }; @@ -24,11 +26,14 @@ pub struct CommandDispatcher<'a> { /// Stores registered [CommandTree]s and dispatches commands to them. impl<'a> CommandDispatcher<'a> { - pub fn handle_command(&self, sender: &mut CommandSender, server: &Server, cmd: &str) { + pub async fn handle_command(&self, sender: &mut CommandSender<'a>, server: &Server, cmd: &str) { if let Err(err) = self.dispatch(sender, server, cmd) { - // sender.send_message( - // TextComponent::text(&err).color_named(pumpkin_core::text::color::NamedColor::Red), - // ) + sender + .send_message( + TextComponent::text_string(err) + .color_named(pumpkin_core::text::color::NamedColor::Red), + ) + .await; } } diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index 84ddfdd68..53f270ba3 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -27,11 +27,10 @@ pub enum CommandSender<'a> { } impl<'a> CommandSender<'a> { - pub fn send_message(&mut self, text: TextComponent) { + pub async fn send_message(&mut self, text: TextComponent<'a>) { match self { - // TODO: add color and stuff to console CommandSender::Console => log::info!("{}", text.to_pretty_console()), - CommandSender::Player(c) => c.send_system_message(&text), + CommandSender::Player(c) => c.send_system_message(&text).await, CommandSender::Rcon(s) => s.push(text.to_pretty_console()), } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 387e5c267..634890fca 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -276,7 +276,7 @@ impl Player { .await; } - pub async fn send_system_message<'a>(&self, text: TextComponent<'a>) { + pub async fn send_system_message<'a>(&self, text: &TextComponent<'a>) { self.client .send_packet(&CSystemChatMessage::new(text, false)) .await; @@ -284,7 +284,7 @@ impl Player { } impl Player { - pub async fn process_packets(&self, server: &Arc) { + pub async fn process_packets(self: &Arc, server: &Arc) { let mut packets = self.client.client_packets_queue.lock().await; while let Some(mut packet) = packets.pop_back() { match self.handle_play_packet(server, &mut packet).await { @@ -308,7 +308,7 @@ impl Player { } pub async fn handle_play_packet( - &self, + self: &Arc, server: &Arc, packet: &mut RawPacket, ) -> Result<(), Box> { diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index f2034c664..b1308de2a 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -11,7 +11,6 @@ #![expect(clippy::significant_drop_tightening)] #![expect(clippy::future_not_send)] #![expect(clippy::single_call_fn)] -#![expect(clippy::await_holding_lock)] #[cfg(target_os = "wasi")] compile_error!("Compiling for WASI targets is not supported!"); @@ -150,7 +149,7 @@ async fn main() -> io::Result<()> { if rcon.enabled { let server = server.clone(); tokio::spawn(async move { - RCONServer::new(&rcon, &server).await.unwrap(); + RCONServer::new(&rcon, server).await.unwrap(); }); } let mut unique_id = 0; diff --git a/pumpkin/src/rcon/mod.rs b/pumpkin/src/rcon/mod.rs index 429312abb..ce8aa4d3f 100644 --- a/pumpkin/src/rcon/mod.rs +++ b/pumpkin/src/rcon/mod.rs @@ -1,11 +1,11 @@ use std::{ - collections::HashMap, io::{self}, net::SocketAddr, }; use packet::{ClientboundPacket, Packet, PacketError, ServerboundPacket}; use pumpkin_config::{RCONConfig, ADVANCED_CONFIG}; +use std::sync::Arc; use thiserror::Error; use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -26,40 +26,29 @@ pub enum RCONError { pub struct RCONServer; impl RCONServer { - pub async fn new(config: &RCONConfig, server: &Server) -> Result { + pub async fn new(config: &RCONConfig, server: Arc) -> Result { assert!(config.enabled, "RCON is not enabled"); let listener = tokio::net::TcpListener::bind(config.address).await.unwrap(); - let mut connections: HashMap = HashMap::new(); + let password = Arc::new(config.password.clone()); - let password = config.password.clone(); - - let mut unique_id = 0; + let mut connections = 0; loop { // Asynchronously wait for an inbound socket. let (connection, address) = listener.accept().await?; - if config.max_connections != 0 && connections.len() >= config.max_connections as usize { + if config.max_connections != 0 && connections >= config.max_connections { continue; } - unique_id += 1; - let token = unique_id; - connections.insert(token, RCONClient::new(connection, address)); + connections += 1; + let mut client = RCONClient::new(connection, address); - let done = if let Some(client) = connections.get_mut(&token) { - client.handle(server, &password).await - } else { - false - }; - if done { - if let Some(client) = connections.remove(&token) { - let config = &ADVANCED_CONFIG.rcon; - if config.logging.log_quit { - log::info!("RCON ({}): Client closed connection", client.address); - } - } - } + let password = password.clone(); + let server = server.clone(); + tokio::spawn(async move { while !client.handle(&server, &password).await {} }); + dbg!("closed"); + connections -= 1; } } } @@ -83,20 +72,19 @@ impl RCONClient { } } + /// Returns if client is closed or not pub async fn handle(&mut self, server: &Server, password: &str) -> bool { if !self.closed { - loop { - match self.read_bytes().await { - // Stream closed, so we can't reply, so we just close everything. - Ok(true) => return true, - Ok(false) => {} - Err(e) if e.kind() == io::ErrorKind::WouldBlock => break, - Err(e) => { - log::error!("could not read packet: {e}"); - return true; - } + match self.read_bytes().await { + // Stream closed, so we can't reply, so we just close everything. + Ok(true) => return true, + Ok(false) => {} + Err(e) => { + log::error!("could not read packet: {e}"); + return true; } } + dbg!("a"); // If we get a close here, we might have a reply, which we still want to write. let _ = self.poll(server, password).await.map_err(|e| { log::error!("RCON error: {e}"); @@ -107,52 +95,51 @@ impl RCONClient { } async fn poll(&mut self, server: &Server, password: &str) -> Result<(), PacketError> { - loop { - let packet = match self.receive_packet().await? { - Some(p) => p, - None => return Ok(()), - }; - - let config = &ADVANCED_CONFIG.rcon; - match packet.get_type() { - ServerboundPacket::Auth => { - let body = packet.get_body(); - if !body.is_empty() && packet.get_body() == password { - self.send(ClientboundPacket::AuthResponse, packet.get_id(), "".into()) - .await?; - if config.logging.log_logged_successfully { - log::info!("RCON ({}): Client logged in successfully", self.address); - } - self.logged_in = true; - } else { - if config.logging.log_wrong_password { - log::info!("RCON ({}): Client has tried wrong password", self.address); - } - self.send(ClientboundPacket::AuthResponse, -1, "".into()) - .await?; - self.closed = true; + let packet = match self.receive_packet().await? { + Some(p) => p, + None => return Ok(()), + }; + let config = &ADVANCED_CONFIG.rcon; + match packet.get_type() { + ServerboundPacket::Auth => { + if packet.get_body() == password { + self.send(ClientboundPacket::AuthResponse, packet.get_id(), "".into()) + .await?; + if config.logging.log_logged_successfully { + log::info!("RCON ({}): Client logged in successfully", self.address); } + self.logged_in = true; + } else { + if config.logging.log_wrong_password { + log::info!("RCON ({}): Client has tried wrong password", self.address); + } + self.send(ClientboundPacket::AuthResponse, -1, "".into()) + .await?; + self.closed = true; } - ServerboundPacket::ExecCommand => { - if self.logged_in { - let mut output = Vec::new(); - let dispatcher = server.command_dispatcher.clone(); - dispatcher.handle_command( + } + ServerboundPacket::ExecCommand => { + if self.logged_in { + let mut output = Vec::new(); + let dispatcher = server.command_dispatcher.clone(); + dispatcher + .handle_command( &mut crate::commands::CommandSender::Rcon(&mut output), server, packet.get_body(), - ); - for line in output { - if config.logging.log_commands { - log::info!("RCON ({}): {}", self.address, line); - } - self.send(ClientboundPacket::Output, packet.get_id(), line) - .await?; + ) + .await; + for line in output { + if config.logging.log_commands { + log::info!("RCON ({}): {}", self.address, line); } + self.send(ClientboundPacket::Output, packet.get_id(), line) + .await?; } } } } + Ok(()) } async fn read_bytes(&mut self) -> io::Result { diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 87d90aaeb..a720e4c15 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -131,7 +131,7 @@ impl Server { /// Searches every world for a player by name pub async fn get_player_by_name(&self, name: &str) -> Option> { for world in self.worlds.iter() { - if let Some(player) = world.get_player_by_name(name).await { + if let Some(player) = world.get_player_by_name(name) { return Some(player); } } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index c31343803..a4a2b76cf 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -298,8 +298,9 @@ impl World { } /// Gets a Player by name - pub async fn get_player_by_name(&self, name: &str) -> Option> { - for player in self.current_players.lock().await.values() { + pub fn get_player_by_name(&self, name: &str) -> Option> { + // not sure of blocking lock + for player in self.current_players.blocking_lock().values() { if player.gameprofile.name == name { return Some(player.clone()); } diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index 9f443f7aa..c3d698f67 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -78,11 +78,10 @@ pub async fn update_position(entity: &Entity, player: &Player) { Vector2::new(current_watched.x, current_watched.z), view_distance, ); - let new_cylindrical = - Cylindrical::new(chunk_pos, view_distance); + let new_cylindrical = Cylindrical::new(chunk_pos, view_distance); player.watched_section.store(new_watched); - + let mut loading_chunks = Vec::new(); Cylindrical::for_each_changed_chunk( old_cylindrical, diff --git a/pumpkin/src/world/scoreboard.rs b/pumpkin/src/world/scoreboard.rs index fa758d630..f82759548 100644 --- a/pumpkin/src/world/scoreboard.rs +++ b/pumpkin/src/world/scoreboard.rs @@ -26,7 +26,7 @@ impl Scoreboard { } } - pub fn add_objective(&mut self, world: &World, objective: ScoreboardObjective) { + pub async fn add_objective<'a>(&mut self, world: &World, objective: ScoreboardObjective<'a>) { if self.objectives.contains_key(objective.name) { // Maybe make this an error ? log::warn!( @@ -35,20 +35,24 @@ impl Scoreboard { ); return; } - world.broadcast_packet_all(&CUpdateObjectives::new( - objective.name, - pumpkin_protocol::client::play::Mode::Add, - objective.display_name, - objective.render_type, - objective.number_format, - )); - world.broadcast_packet_all(&CDisplayObjective::new( - pumpkin_protocol::client::play::DisplaySlot::Sidebar, - "test", - )); + world + .broadcast_packet_all(&CUpdateObjectives::new( + objective.name, + pumpkin_protocol::client::play::Mode::Add, + objective.display_name, + objective.render_type, + objective.number_format, + )) + .await; + world + .broadcast_packet_all(&CDisplayObjective::new( + pumpkin_protocol::client::play::DisplaySlot::Sidebar, + "test", + )) + .await; } - pub fn update_score(&self, world: &World, score: ScoreboardScore) { + pub async fn update_score<'a>(&self, world: &World, score: ScoreboardScore<'a>) { if self.objectives.contains_key(score.objective_name) { log::warn!( "Tried to place a score into a Objective which does not exist, {}", @@ -56,13 +60,15 @@ impl Scoreboard { ); return; } - world.broadcast_packet_all(&CUpdateScore::new( - score.entity_name, - score.objective_name, - score.value, - score.display_name, - score.number_format, - )); + world + .broadcast_packet_all(&CUpdateScore::new( + score.entity_name, + score.objective_name, + score.value, + score.display_name, + score.number_format, + )) + .await; } // pub fn add_team(&mut self, name: String) {