Clippy: warn clippy::pedantic

This commit is contained in:
Snowiiii 2024-10-21 15:07:05 +02:00
parent 11a588b622
commit 12ab2c7298
26 changed files with 181 additions and 194 deletions

View File

@ -96,12 +96,12 @@ pub fn validate_textures(property: &Property, config: &TextureConfig) -> Result<
for texture in textures.textures {
let url =
Url::parse(&texture.1.url).map_err(|e| TextureError::InvalidURL(e.to_string()))?;
is_texture_url_valid(url, config)?
is_texture_url_valid(&url, config)?;
}
Ok(())
}
pub fn is_texture_url_valid(url: Url, config: &TextureConfig) -> Result<(), TextureError> {
pub fn is_texture_url_valid(url: &Url, config: &TextureConfig) -> Result<(), TextureError> {
let scheme = url.scheme();
if !config
.allowed_url_schemes

View File

@ -192,15 +192,11 @@ impl Client {
let profile = authentication::authenticate(username, &hash, &ip, auth_client).await?;
// Check if player should join
if let Some(actions) = &profile.profile_actions {
if !ADVANCED_CONFIG
if ADVANCED_CONFIG
.authentication
.player_profile
.allow_banned_players
{
if !actions.is_empty() {
return Err(AuthError::Banned);
}
} else {
for allowed in &ADVANCED_CONFIG
.authentication
.player_profile
@ -210,6 +206,11 @@ impl Client {
return Err(AuthError::DisallowedAction);
}
}
if !actions.is_empty() {
return Err(AuthError::Banned);
}
} else if !actions.is_empty() {
return Err(AuthError::Banned);
}
}
// validate textures
@ -234,7 +235,7 @@ impl Client {
Ok((profile, new_address)) => {
self.finish_login(&profile).await;
*self.gameprofile.lock().await = Some(profile);
*address = new_address
*address = new_address;
}
Err(error) => self.kick(&error.to_string()).await,
}
@ -259,10 +260,10 @@ impl Client {
&resource_config.resource_pack_url,
&resource_config.resource_pack_sha1,
resource_config.force,
if !resource_config.prompt_message.is_empty() {
Some(TextComponent::text(&resource_config.prompt_message))
} else {
if resource_config.prompt_message.is_empty() {
None
} else {
Some(TextComponent::text(&resource_config.prompt_message))
},
);
@ -298,7 +299,7 @@ impl Client {
server_listing: client_information.server_listing,
});
} else {
self.kick("Invalid hand or chat type").await
self.kick("Invalid hand or chat type").await;
}
}
@ -328,7 +329,7 @@ impl Client {
self.send_packet(&CFinishConfig::new()).await;
}
pub async fn handle_config_acknowledged(&self, _config_acknowledged: SAcknowledgeFinishConfig) {
pub fn handle_config_acknowledged(&self, _config_acknowledged: &SAcknowledgeFinishConfig) {
dbg!("config acknowledged");
self.connection_state.store(ConnectionState::Play);
self.make_player

View File

@ -71,7 +71,7 @@ impl Player {
.carried_item
.load()
.as_ref()
.map_or_else(Slot::empty, |item| item.into());
.map_or_else(Slot::empty, std::convert::Into::into);
// Gets the previous value
let i = inventory
@ -92,7 +92,7 @@ impl Player {
inventory.total_opened_containers += 1;
self.client
.send_packet(&CCloseContainer::new(inventory.total_opened_containers))
.await
.await;
}
pub async fn set_container_property<T: WindowPropertyTrait>(
@ -270,7 +270,7 @@ impl Player {
slots.skip(36).rev().find_map(find_condition)
};
if let Some(slot) = slots {
let mut item_slot = container.all_slots()[slot].map(|i| i.to_owned());
let mut item_slot = container.all_slots()[slot].map(|i| i);
container.handle_item_change(&mut item_slot, slot, MouseClick::Left)?;
*container.all_slots()[slot] = item_slot;
}
@ -363,13 +363,14 @@ impl Player {
let player_id = self.entity_id();
let container_id = opened_container
.as_ref()
.map(|container| container.internal_pumpkin_id())
.unwrap_or(player_id as u64);
.map_or(player_id as u64, |container| {
container.internal_pumpkin_id()
});
match mouse_drag_state {
MouseDragState::Start(drag_type) => {
if drag_type == MouseDragType::Middle && self.gamemode.load() != GameMode::Creative
{
Err(InventoryError::PermissionError)?
Err(InventoryError::PermissionError)?;
}
drag_handler
.new_drag(container_id, player_id, drag_type)
@ -417,15 +418,15 @@ impl Player {
.await
.iter()
.filter_map(|(token, player)| {
if *token != player_token {
if *token == player_token {
None
} else {
let entity_id = player.entity_id();
if player_ids.contains(&entity_id) {
Some(player.clone())
} else {
None
}
} else {
None
}
})
.collect_vec();

View File

@ -125,7 +125,7 @@ impl Client {
gameprofile: Mutex::new(None),
config: Mutex::new(None),
brand: Mutex::new(None),
server_address: Mutex::new("".to_string()),
server_address: Mutex::new(String::new()),
id,
address: Mutex::new(address),
connection_state: AtomicCell::new(ConnectionState::HandShake),
@ -211,9 +211,9 @@ impl Client {
while let Some(mut packet) = self.client_packets_queue.lock().await.pop_front() {
if let Err(error) = self.handle_packet(server, &mut packet).await {
dbg!("{:?}", packet.id);
let text = format!("Error while reading incoming packet {}", error);
let text = format!("Error while reading incoming packet {error}");
log::error!("{}", text);
self.kick(&text).await
self.kick(&text).await;
};
}
}
@ -239,7 +239,7 @@ impl Client {
pumpkin_protocol::ConnectionState::Config => {
self.handle_config_packet(server, packet).await
}
_ => {
pumpkin_protocol::ConnectionState::Play => {
log::error!("Invalid Connection state {:?}", self.connection_state);
Ok(())
}
@ -251,20 +251,16 @@ impl Client {
packet: &mut RawPacket,
) -> Result<(), DeserializerError> {
let bytebuf = &mut packet.bytebuf;
match packet.id.0 {
SHandShake::PACKET_ID => {
if packet.id.0 == SHandShake::PACKET_ID {
self.handle_handshake(SHandShake::read(bytebuf)?).await;
Ok(())
}
_ => {
} else {
log::error!(
"Failed to handle packet id {} while in Handshake state",
packet.id.0
);
}
Ok(())
}
}
}
async fn handle_status_packet(
&self,
@ -276,22 +272,20 @@ impl Client {
SStatusRequest::PACKET_ID => {
self.handle_status_request(server, SStatusRequest::read(bytebuf)?)
.await;
Ok(())
}
SStatusPingRequest::PACKET_ID => {
self.handle_ping_request(SStatusPingRequest::read(bytebuf)?)
.await;
Ok(())
}
_ => {
log::error!(
"Failed to handle packet id {} while in Status state",
packet.id.0
);
}
}
Ok(())
}
}
}
async fn handle_login_packet(
&self,
@ -303,32 +297,28 @@ impl Client {
SLoginStart::PACKET_ID => {
self.handle_login_start(server, SLoginStart::read(bytebuf)?)
.await;
Ok(())
}
SEncryptionResponse::PACKET_ID => {
self.handle_encryption_response(server, SEncryptionResponse::read(bytebuf)?)
.await;
Ok(())
}
SLoginPluginResponse::PACKET_ID => {
self.handle_plugin_response(SLoginPluginResponse::read(bytebuf)?)
.await;
Ok(())
}
SLoginAcknowledged::PACKET_ID => {
self.handle_login_acknowledged(server, SLoginAcknowledged::read(bytebuf)?)
.await;
Ok(())
}
_ => {
log::error!(
"Failed to handle packet id {} while in Login state",
packet.id.0
);
}
}
Ok(())
}
}
}
async fn handle_config_packet(
&self,
@ -340,32 +330,27 @@ impl Client {
SClientInformationConfig::PACKET_ID => {
self.handle_client_information_config(SClientInformationConfig::read(bytebuf)?)
.await;
Ok(())
}
SPluginMessage::PACKET_ID => {
self.handle_plugin_message(SPluginMessage::read(bytebuf)?)
.await;
Ok(())
}
SAcknowledgeFinishConfig::PACKET_ID => {
self.handle_config_acknowledged(SAcknowledgeFinishConfig::read(bytebuf)?)
.await;
Ok(())
self.handle_config_acknowledged(&SAcknowledgeFinishConfig::read(bytebuf)?);
}
SKnownPacks::PACKET_ID => {
self.handle_known_packs(server, SKnownPacks::read(bytebuf)?)
.await;
Ok(())
}
_ => {
log::error!(
"Failed to handle packet id {} while in Config state",
packet.id.0
);
}
}
Ok(())
}
}
}
/// Reads the connection until our buffer of len 4096 is full, then decode
/// Close connection when an error occurs or when the Client closed the connection
@ -406,7 +391,7 @@ impl Client {
match self.connection_state.load() {
ConnectionState::Login => {
self.try_send_packet(&CLoginDisconnect::new(
&serde_json::to_string_pretty(&reason).unwrap_or_else(|_| "".into()),
&serde_json::to_string_pretty(&reason).unwrap_or_else(|_| String::new()),
))
.await
.unwrap_or_else(|_| self.close());
@ -423,10 +408,10 @@ impl Client {
.unwrap_or_else(|_| self.close());
}
_ => {
log::warn!("Can't kick in {:?} State", self.connection_state)
log::warn!("Can't kick in {:?} State", self.connection_state);
}
}
self.close()
self.close();
}
/// You should prefer to use `kick` when you can

View File

@ -52,13 +52,13 @@ impl Player {
*awaiting_teleport = None;
} else {
self.kick(TextComponent::text("Wrong teleport id")).await
self.kick(TextComponent::text("Wrong teleport id")).await;
}
} else {
self.kick(TextComponent::text(
"Send Teleport confirm, but we did not teleport",
))
.await
.await;
}
}
@ -255,7 +255,7 @@ impl Player {
}
}
pub fn handle_player_ground(&self, ground: SSetPlayerGround) {
pub fn handle_player_ground(&self, ground: &SSetPlayerGround) {
self.living_entity
.entity
.on_ground
@ -272,23 +272,23 @@ impl Player {
match action {
pumpkin_protocol::server::play::Action::StartSneaking => {
if !entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) {
entity.set_sneaking(true).await
entity.set_sneaking(true).await;
}
}
pumpkin_protocol::server::play::Action::StopSneaking => {
if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) {
entity.set_sneaking(false).await
entity.set_sneaking(false).await;
}
}
pumpkin_protocol::server::play::Action::LeaveBed => todo!(),
pumpkin_protocol::server::play::Action::StartSprinting => {
if !entity.sprinting.load(std::sync::atomic::Ordering::Relaxed) {
entity.set_sprinting(true).await
entity.set_sprinting(true).await;
}
}
pumpkin_protocol::server::play::Action::StopSprinting => {
if entity.sprinting.load(std::sync::atomic::Ordering::Relaxed) {
entity.set_sprinting(false).await
entity.set_sprinting(false).await;
}
}
pumpkin_protocol::server::play::Action::StartHorseJump => todo!(),
@ -307,7 +307,7 @@ impl Player {
}
} else {
self.kick(TextComponent::text("Invalid player command"))
.await
.await;
}
}
@ -325,7 +325,7 @@ impl Player {
&[self.client.id],
&CEntityAnimation::new(id.into(), animation as u8),
)
.await
.await;
}
None => {
self.kick(TextComponent::text("Invalid hand")).await;
@ -362,7 +362,7 @@ impl Player {
TextComponent::text(&gameprofile.name),
None,
))
.await
.await;
/* server.broadcast_packet(
self,
@ -392,7 +392,7 @@ impl Player {
};
} else {
self.kick(TextComponent::text("Invalid hand or chat type"))
.await
.await;
}
}
@ -425,8 +425,8 @@ impl Player {
let saved_velo = victem_entity.velocity.load();
victem_entity.knockback(
strength * 0.5,
(yaw * (PI / 180.0)).sin() as f64,
-(yaw * (PI / 180.0)).cos() as f64,
f64::from((yaw * (PI / 180.0)).sin()),
f64::from(-(yaw * (PI / 180.0)).cos()),
);
let victem_velocity = victem_entity.velocity.load();
let packet = &CEntityVelocity::new(
@ -447,12 +447,12 @@ impl Player {
&entity_id,
entity.yaw.load(),
))
.await
.await;
}
if config.swing {}
} else {
self.kick(TextComponent::text("Interacted with invalid entity id"))
.await
.await;
}
}
}
@ -552,7 +552,7 @@ impl Player {
self.wait_for_keep_alive
.store(false, std::sync::atomic::Ordering::Relaxed);
} else {
self.kick(TextComponent::text("Timeout")).await
self.kick(TextComponent::text("Timeout")).await;
}
}
@ -605,11 +605,11 @@ impl Player {
.send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence))
.await;
} else {
self.kick(TextComponent::text("Invalid block face")).await
self.kick(TextComponent::text("Invalid block face")).await;
}
}
pub fn handle_use_item(&self, _use_item: SUseItem) {
pub fn handle_use_item(&self, _use_item: &SUseItem) {
// TODO: handle packet correctly
log::error!("An item was used(SUseItem), but the packet is not implemented yet");
}
@ -651,7 +651,7 @@ impl Player {
if let Some(id) = open_container {
let mut open_containers = server.open_containers.write().await;
if let Some(container) = open_containers.get_mut(&id) {
container.remove_player(self.entity_id())
container.remove_player(self.entity_id());
}
self.open_container.store(None);
}

View File

@ -17,9 +17,8 @@ pub fn consume_arg_player(
"@s" => {
if src.is_player() {
return Ok(arg.into());
} else {
return Err(Some("You are not a Player".into()));
}
return Err(Some("You are not a Player".into()));
}
"@p" if src.is_player() => return Ok(arg.into()),
"@r" => todo!(), // todo: implement random player target selector
@ -31,7 +30,7 @@ pub fn consume_arg_player(
return Ok(name.into());
}
}
return Err(Some(format!("Player not found: {}", arg)));
return Err(Some(format!("Player not found: {arg}")));
}
}
}
@ -52,9 +51,9 @@ pub fn parse_arg_player(
match s {
"@s" if src.is_player() => Ok(src.as_mut_player().unwrap()),
"@p" if src.is_player() => Ok(src.as_mut_player().unwrap()),
"@r" => Err(InvalidConsumptionError(Some(s.into()))), // todo: implement random player target selector
"@a" | "@e" => Err(InvalidConsumptionError(Some(s.into()))), // todo: implement all players target selector
"@p" => todo!(),
"@r" => todo!(), // todo: implement random player target selector
"@a" | "@e" => todo!(), // todo: implement all players target selector
name => {
for world in &server.worlds {
if let Some(player) = world.get_player_by_name(name) {

View File

@ -37,7 +37,7 @@ pub fn consume_arg_gamemode(
match GameMode::from_str(arg) {
Err(_) | Ok(GameMode::Undefined) => {
return Err(Some(format!("Gamemode not found: {}", arg)))
return Err(Some(format!("Gamemode not found: {arg}")))
}
Ok(_) => return Ok(arg.into()),
}

View File

@ -56,7 +56,7 @@ pub fn init_command_tree<'a>() -> CommandTree<'a> {
)
.execute(&|sender, server, _args| {
let mut keys: Vec<&str> = server.command_dispatcher.commands.keys().copied().collect();
keys.sort();
keys.sort_unstable();
for key in keys {
let Command::Tree(tree) = &server.command_dispatcher.commands[key] else {

View File

@ -15,7 +15,7 @@ pub(crate) enum InvalidTreeError {
/// never fail.
InvalidConsumptionError(Option<String>),
/// Return this if a condition that a [Node::Require] should ensure is met is not met.
/// Return this if a condition that a [`Node::Require`] should ensure is met is not met.
InvalidRequirementError,
}
@ -24,7 +24,7 @@ pub struct CommandDispatcher<'a> {
pub(crate) commands: HashMap<&'a str, Command<'a>>,
}
/// Stores registered [CommandTree]s and dispatches commands to them.
/// Stores registered [`CommandTree`]s and dispatches commands to them.
impl<'a> CommandDispatcher<'a> {
pub async fn handle_command(&self, sender: &mut CommandSender<'a>, server: &Server, cmd: &str) {
if let Err(err) = self.dispatch(sender, server, cmd) {
@ -37,7 +37,7 @@ impl<'a> CommandDispatcher<'a> {
}
}
/// Execute a command using its corresponding [CommandTree].
/// Execute a command using its corresponding [`CommandTree`].
pub(crate) fn dispatch(
&'a self,
src: &mut CommandSender,
@ -52,7 +52,7 @@ impl<'a> CommandDispatcher<'a> {
// try paths until fitting path is found
for path in tree.iter_paths() {
match Self::try_is_fitting_path(src, server, path, tree, raw_args.clone()) {
match Self::try_is_fitting_path(src, server, &path, tree, raw_args.clone()) {
Err(InvalidConsumptionError(s)) => {
println!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed");
return Err("Internal Error (See logs for details)".into());
@ -62,7 +62,7 @@ impl<'a> CommandDispatcher<'a> {
return Err("Internal Error (See logs for details)".into());
}
Ok(is_fitting_path) => match is_fitting_path {
Ok(_) => return Ok(()),
Ok(()) => return Ok(()),
Err(error) => {
// Custom error message or not ?
if let Some(error) = error {
@ -72,7 +72,7 @@ impl<'a> CommandDispatcher<'a> {
},
}
}
Err(format!("Invalid Syntax. Usage: {}", tree))
Err(format!("Invalid Syntax. Usage: {tree}"))
}
pub(crate) fn get_tree(&'a self, key: &str) -> Result<&'a CommandTree<'a>, String> {
@ -93,7 +93,7 @@ impl<'a> CommandDispatcher<'a> {
fn try_is_fitting_path(
src: &mut CommandSender,
server: &Server,
path: Vec<usize>,
path: &[usize],
tree: &CommandTree,
mut raw_args: RawArgs,
) -> Result<Result<(), Option<String>>, InvalidTreeError> {

View File

@ -35,39 +35,30 @@ impl<'a> CommandSender<'a> {
}
}
#[must_use]
pub const fn is_player(&self) -> bool {
match self {
CommandSender::Console => false,
CommandSender::Player(_) => true,
CommandSender::Rcon(_) => false,
}
matches!(self, CommandSender::Player(_))
}
#[must_use]
pub const fn is_console(&self) -> bool {
match self {
CommandSender::Console => true,
CommandSender::Player(_) => false,
CommandSender::Rcon(_) => true,
}
matches!(self, CommandSender::Console)
}
pub fn as_mut_player(&mut self) -> Option<Arc<Player>> {
match self {
CommandSender::Player(player) => Some(player.clone()),
CommandSender::Console => None,
CommandSender::Rcon(_) => None,
_ => None,
}
}
/// todo: implement
#[must_use]
pub const fn permission_lvl(&self) -> i32 {
match self {
CommandSender::Rcon(_) => 4,
CommandSender::Console => 4,
CommandSender::Player(_) => 4,
}
4
}
}
#[must_use]
pub fn default_dispatcher<'a>() -> CommandDispatcher<'a> {
let mut dispatcher = CommandDispatcher::default();

View File

@ -2,13 +2,13 @@ use super::RunFunctionType;
use crate::{commands::CommandSender, server::Server};
use std::collections::{HashMap, VecDeque};
/// see [crate::commands::tree_builder::argument]
/// see [`crate::commands::tree_builder::argument`]
pub type RawArgs<'a> = Vec<&'a str>;
/// see [crate::commands::tree_builder::argument] and [CommandTree::execute]/[crate::commands::tree_builder::NonLeafNodeBuilder::execute]
/// see [`crate::commands::tree_builder::argument`] and [`CommandTree::execute`]/[`crate::commands::tree_builder::NonLeafNodeBuilder::execute`]
pub type ConsumedArgs<'a> = HashMap<&'a str, String>;
/// see [crate::commands::tree_builder::argument]
/// see [`crate::commands::tree_builder::argument`]
/// Provide value or an Optional error message, If no Error message provided the default will be used
pub type ArgumentConsumer<'a> =
fn(&CommandSender, &Server, &mut RawArgs) -> Result<String, Option<String>>;
@ -48,7 +48,7 @@ pub struct CommandTree<'a> {
}
impl<'a> CommandTree<'a> {
/// iterate over all possible paths that end in a [NodeType::ExecuteLeaf]
/// iterate over all possible paths that end in a [`NodeType::ExecuteLeaf`]
pub(crate) fn iter_paths(&'a self) -> impl Iterator<Item = Vec<usize>> + 'a {
let mut todo = VecDeque::<(usize, usize)>::new();

View File

@ -4,7 +4,7 @@ use crate::commands::CommandSender;
use super::RunFunctionType;
impl<'a> CommandTree<'a> {
/// Add a child [Node] to the root of this [CommandTree].
/// Add a child [Node] to the root of this [`CommandTree`].
pub fn with_child(mut self, child: impl NodeBuilder<'a>) -> Self {
let node = child.build(&mut self);
self.children.push(self.nodes.len());
@ -35,11 +35,11 @@ impl<'a> CommandTree<'a> {
/// Executes if a command terminates at this [Node], i.e. without any arguments.
///
/// [ConsumedArgs] maps the names of all
/// [`ConsumedArgs`] maps the names of all
/// arguments to the result of their consumption, i.e. a string that can be parsed to the
/// desired type.
///
/// Also see [NonLeafNodeBuilder::execute].
/// Also see [`NonLeafNodeBuilder::execute`].
pub fn execute(mut self, run: &'a RunFunctionType) -> Self {
let node = Node {
node_type: NodeType::ExecuteLeaf { run },
@ -108,11 +108,11 @@ impl<'a> NonLeafNodeBuilder<'a> {
/// Executes if a command terminates at this [Node].
///
/// [ConsumedArgs] maps the names of all
/// [`ConsumedArgs`] maps the names of all
/// arguments to the result of their consumption, i.e. a string that can be parsed to the
/// desired type.
///
/// Also see [CommandTree::execute].
/// Also see [`CommandTree::execute`].
pub fn execute(mut self, run: &'a RunFunctionType) -> Self {
self.leaf_nodes.push(LeafNodeBuilder {
node_type: NodeType::ExecuteLeaf { run },
@ -132,14 +132,14 @@ pub const fn literal(string: &str) -> NonLeafNodeBuilder {
}
}
/// ```name``` identifies this argument in [ConsumedArgs].
/// ```name``` identifies this argument in [`ConsumedArgs`].
///
/// ```consumer: ArgumentConsumer``` has the purpose of validating arguments. Conversion may start
/// here, as long as the result remains a [String] (e.g. convert offset to absolute position actual
/// coordinates), because the result of this function will be passed to following
/// [NonLeafNodeBuilder::execute] nodes in a [ConsumedArgs] instance. It must remove consumed arg(s)
/// from [RawArgs] and return them. It must return None if [RawArgs] are invalid. [RawArgs] is
/// reversed, so [Vec::pop] can be used to obtain args in ltr order.
/// [`NonLeafNodeBuilder::execute`] nodes in a [`ConsumedArgs`] instance. It must remove consumed arg(s)
/// from [`RawArgs`] and return them. It must return None if [`RawArgs`] are invalid. [`RawArgs`] is
/// reversed, so [`Vec::pop`] can be used to obtain args in ltr order.
pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBuilder<'a> {
NonLeafNodeBuilder {
node_type: NodeType::Argument { name, consumer },

View File

@ -9,12 +9,10 @@ trait IsVisible {
impl<'a> IsVisible for Node<'a> {
fn is_visible(&self) -> bool {
match self.node_type {
NodeType::ExecuteLeaf { .. } => false,
NodeType::Literal { .. } => true,
NodeType::Argument { .. } => true,
NodeType::Require { .. } => false,
}
matches!(
self.node_type,
NodeType::Literal { .. } | NodeType::Argument { .. }
)
}
}
@ -43,7 +41,7 @@ fn flatten_require_nodes(nodes: &[Node], children: &[usize]) -> Vec<usize> {
let node = &nodes[i];
match &node.node_type {
NodeType::Require { .. } => {
new_children.extend(flatten_require_nodes(nodes, node.children.as_slice()))
new_children.extend(flatten_require_nodes(nodes, node.children.as_slice()));
}
_ => new_children.push(i),
}

View File

@ -201,7 +201,7 @@ impl Entity {
self.entity_id.into(),
Metadata::new(6, 20.into(), (pose).into()),
);
self.world.broadcast_packet_all(&packet).await
self.world.broadcast_packet_all(&packet).await;
}
}

View File

@ -76,7 +76,7 @@ pub struct Player {
/// This field represents the various abilities that the player possesses, such as flight, invulnerability, and other special effects.
///
/// **Note:** When the `abilities` field is updated, the server should send a `send_abilities_update` packet to the client to notify them of the changes.
pub abilities: Mutex<PlayerAbilities>,
pub abilities: Mutex<Abilities>,
/// The player's last known position.
///
/// This field is used to calculate the player's movement delta for network synchronization and other purposes.
@ -110,7 +110,7 @@ impl Player {
log::error!("No gameprofile?. Impossible");
GameProfile {
id: uuid::Uuid::new_v4(),
name: "".to_string(),
name: String::new(),
properties: vec![],
profile_actions: None,
}
@ -137,7 +137,7 @@ impl Player {
open_container: AtomicCell::new(None),
carried_item: AtomicCell::new(None),
teleport_id_count: AtomicI32::new(0),
abilities: Mutex::new(PlayerAbilities::default()),
abilities: Mutex::new(Abilities::default()),
gamemode: AtomicCell::new(gamemode),
watched_section: AtomicCell::new(Vector3::new(0, 0, 0)),
last_position: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)),
@ -152,7 +152,7 @@ impl Player {
self.living_entity.entity.world.remove_player(self).await;
let watched = chunk_section_from_pos(&self.living_entity.entity.block_pos.load());
let view_distance = get_view_distance(self).await as i32;
let view_distance = i32::from(get_view_distance(self).await);
let cylindrical = Cylindrical::new(Vector2::new(watched.x, watched.z), view_distance);
self.living_entity
.entity
@ -256,7 +256,7 @@ impl Player {
let standing_eye_height = self.living_entity.entity.standing_eye_height;
box_pos.squared_magnitude(Vector3 {
x: entity_pos.x,
y: entity_pos.y + standing_eye_height as f64,
y: entity_pos.y + f64::from(standing_eye_height),
z: entity_pos.z,
}) < d * d
}
@ -277,7 +277,7 @@ impl Player {
self.gameprofile.name,
reason.to_pretty_console()
);
self.client.close()
self.client.close();
}
pub async fn set_health(&self, health: f32, food: i32, food_saturation: f32) {
@ -330,15 +330,14 @@ impl Player {
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 {
Ok(_) => {}
Ok(()) => {}
Err(e) => {
if e.is_kick() {
if let Some(kick_reason) = e.client_kick_reason() {
self.kick(TextComponent::text(&kick_reason)).await
self.kick(TextComponent::text(&kick_reason)).await;
} else {
self.kick(TextComponent::text(&format!(
"Error while reading incoming packet {}",
e
"Error while reading incoming packet {e}"
)))
.await;
}
@ -380,7 +379,7 @@ impl Player {
Ok(())
}
SSetPlayerGround::PACKET_ID => {
self.handle_player_ground(SSetPlayerGround::read(bytebuf)?);
self.handle_player_ground(&SSetPlayerGround::read(bytebuf)?);
Ok(())
}
SPlayerCommand::PACKET_ID => {
@ -421,7 +420,7 @@ impl Player {
Ok(())
}
SUseItem::PACKET_ID => {
self.handle_use_item(SUseItem::read(bytebuf)?);
self.handle_use_item(&SUseItem::read(bytebuf)?);
Ok(())
}
SSetHeldItem::PACKET_ID => {
@ -464,7 +463,7 @@ impl Player {
/// Represents a player's abilities and special powers.
///
/// This struct contains information about the player's current abilities, such as flight, invulnerability, and creative mode.
pub struct PlayerAbilities {
pub struct Abilities {
/// Indicates whether the player is invulnerable to damage.
pub invulnerable: bool,
/// Indicates whether the player is currently flying.
@ -479,7 +478,7 @@ pub struct PlayerAbilities {
pub walk_speed_fov: f32,
}
impl Default for PlayerAbilities {
impl Default for Abilities {
fn default() -> Self {
Self {
invulnerable: false,

View File

@ -22,14 +22,20 @@ impl<ErrorType: PumpkinError + 'static> From<ErrorType> for Box<dyn PumpkinError
}
impl PumpkinError for InventoryError {
fn is_kick(&self) -> bool {
use InventoryError::*;
use InventoryError::{
ClosedContainerInteract, InvalidPacket, InvalidSlot, LockError,
MultiplePlayersDragging, OutOfOrderDragging, PermissionError,
};
match self {
InvalidSlot | ClosedContainerInteract(..) | InvalidPacket | PermissionError => true,
LockError | OutOfOrderDragging | MultiplePlayersDragging => false,
}
}
fn severity(&self) -> log::Level {
use InventoryError::*;
use InventoryError::{
ClosedContainerInteract, InvalidPacket, InvalidSlot, LockError,
MultiplePlayersDragging, OutOfOrderDragging, PermissionError,
};
match self {
LockError
| InvalidSlot

View File

@ -1,9 +1,9 @@
#![deny(clippy::all)]
// #![warn(clippy::pedantic)]
#![warn(clippy::pedantic)]
// #![warn(clippy::restriction)]
#![warn(clippy::nursery)]
#![warn(clippy::cargo)]
// expect
// REMOVE SOME WHEN RELEASE
#![expect(clippy::cargo_common_metadata)]
#![expect(clippy::multiple_crate_versions)]
#![expect(clippy::while_float)]
@ -11,6 +11,16 @@
#![expect(clippy::significant_drop_tightening)]
#![expect(clippy::future_not_send)]
#![expect(clippy::single_call_fn)]
#![expect(clippy::cast_sign_loss)]
#![expect(clippy::cast_possible_truncation)]
#![expect(clippy::cast_possible_wrap)]
#![expect(clippy::too_many_lines)]
#![expect(clippy::missing_panics_doc)]
#![expect(clippy::missing_errors_doc)]
#![expect(clippy::module_name_repetitions)]
#![expect(clippy::struct_excessive_bools)]
#![expect(clippy::many_single_char_names)]
#![expect(clippy::float_cmp)]
#[cfg(target_os = "wasi")]
compile_error!("Compiling for WASI targets is not supported!");
@ -60,7 +70,7 @@ fn init_logger() {
logger = logger.with_colors(ADVANCED_CONFIG.logging.color);
logger = logger.with_threads(ADVANCED_CONFIG.logging.threads);
logger.init().unwrap()
logger.init().unwrap();
}
}
@ -82,6 +92,7 @@ async fn main() -> io::Result<()> {
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
use pumpkin_core::text::{color::NamedColor, TextComponent};
use rcon::RCONServer;
use std::time::Instant;
init_logger();
@ -109,8 +120,6 @@ async fn main() -> io::Result<()> {
std::process::exit(1);
}));
use std::time::Instant;
let time = Instant::now();
// Setup the TCP server socket.
@ -173,7 +182,7 @@ async fn main() -> io::Result<()> {
log::info!(
"Accepted connection from: {} (id: {})",
scrub_address(&format!("{}", address)),
scrub_address(&format!("{address}")),
id
);
@ -188,7 +197,7 @@ async fn main() -> io::Result<()> {
{
let open = client.poll().await;
if open {
client.process_packets(&server).await
client.process_packets(&server).await;
};
}
if client
@ -206,7 +215,7 @@ async fn main() -> io::Result<()> {
{
let open = player.client.poll().await;
if open {
player.process_packets(&server).await
player.process_packets(&server).await;
};
}
player.remove().await;

View File

@ -13,9 +13,9 @@ use thiserror::Error;
use crate::client::{authentication::GameProfile, Client};
/// Proxy implementation for Velocity <https://papermc.io/software/velocity> by PaperMC
/// Sadly PaperMC does not care about 3th Parties providing support for Velocity, There is no documentation.
/// I had to understand the Code logic by looking at PaperMC's Velocity implementation: <https://github.com/PaperMC/Paper/blob/master/patches/server/0731-Add-Velocity-IP-Forwarding-Support.patch>
/// Proxy implementation for Velocity <https://papermc.io/software/velocity> by `PaperMC`
/// Sadly `PaperMC` does not care about 3th Parties providing support for Velocity, There is no documentation.
/// I had to understand the Code logic by looking at `PaperMC`'s Velocity implementation: <https://github.com/PaperMC/Paper/blob/master/patches/server/0731-Add-Velocity-IP-Forwarding-Support.patch>
type HmacSha256 = Hmac<Sha256>;
@ -59,6 +59,7 @@ pub async fn velocity_login(client: &Client) {
.await;
}
#[must_use]
pub fn check_integrity(data: (&[u8], &[u8]), secret: &str) -> bool {
let (signature, data_without_signature) = data;
// Our fault, We can panic/expect ?
@ -80,7 +81,7 @@ fn read_game_profile(buf: &mut ByteBuffer) -> Result<GameProfile, VelocityError>
.get_list(|data| {
let name = data.get_string()?;
let value = data.get_string()?;
let signature = data.get_option(|data| data.get_string())?;
let signature = data.get_option(pumpkin_protocol::bytebuf::ByteBuffer::get_string)?;
Ok(Property {
name,

View File

@ -95,15 +95,14 @@ impl RCONClient {
}
async fn poll(&mut self, server: &Server, password: &str) -> Result<(), PacketError> {
let packet = match self.receive_packet().await? {
Some(p) => p,
None => return Ok(()),
let Some(packet) = self.receive_packet().await? else {
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())
self.send(ClientboundPacket::AuthResponse, packet.get_id(), "")
.await?;
if config.logging.log_logged_successfully {
log::info!("RCON ({}): Client logged in successfully", self.address);
@ -113,8 +112,7 @@ impl RCONClient {
if config.logging.log_wrong_password {
log::info!("RCON ({}): Client has tried wrong password", self.address);
}
self.send(ClientboundPacket::AuthResponse, -1, "".into())
.await?;
self.send(ClientboundPacket::AuthResponse, -1, "").await?;
self.closed = true;
}
}
@ -133,7 +131,7 @@ impl RCONClient {
if config.logging.log_commands {
log::info!("RCON ({}): {}", self.address, line);
}
self.send(ClientboundPacket::Output, packet.get_id(), line)
self.send(ClientboundPacket::Output, packet.get_id(), &line)
.await?;
}
}
@ -156,7 +154,7 @@ impl RCONClient {
&mut self,
packet: ClientboundPacket,
id: i32,
body: String,
body: &str,
) -> Result<(), PacketError> {
let buf = packet.write_buf(id, body);
self.connection

View File

@ -13,7 +13,7 @@ use tokio::io::AsyncReadExt;
pub enum ServerboundPacket {
/// Typically, the first packet sent by the client, which is used to authenticate the connection with the server.
Auth = 2,
/// This packet type represents a command issued to the server by a client. This can be a ConCommand such as kill <player> or weather clear.
/// This packet type represents a command issued to the server by a client. This can be a `ConCommand` such as kill <player> or weather clear.
/// The response will vary depending on the command issued.
ExecCommand = 3,
}
@ -21,7 +21,7 @@ pub enum ServerboundPacket {
impl ServerboundPacket {
pub const fn from_i32(n: i32) -> Self {
match n {
3 => Self::Auth,
// 3 => Self::Auth,
2 => Self::ExecCommand,
_ => Self::Auth,
}
@ -32,21 +32,21 @@ impl ServerboundPacket {
#[repr(i32)]
/// Server -> Client
pub enum ClientboundPacket {
/// This packet is a notification of the connection's current auth status. When the server receives an auth request, it will respond with an empty SERVERDATA_RESPONSE_VALUE,
/// followed immediately by a SERVERDATA_AUTH_RESPONSE indicating whether authentication succeeded or failed. Note that the status code is returned in the packet id field, so when pairing the response with the original auth request, you may need to look at the packet id of the preceeding SERVERDATA_RESPONSE_VALUE.
/// This packet is a notification of the connection's current auth status. When the server receives an auth request, it will respond with an empty `SERVERDATA_RESPONSE_VALUE`,
/// followed immediately by a `SERVERDATA_AUTH_RESPONSE` indicating whether authentication succeeded or failed. Note that the status code is returned in the packet id field, so when pairing the response with the original auth request, you may need to look at the packet id of the preceeding `SERVERDATA_RESPONSE_VALUE`.
AuthResponse = 2,
/// A SERVERDATA_RESPONSE packet is the response to a SERVERDATA_EXECCOMMAND request.
/// A `SERVERDATA_RESPONSE` packet is the response to a `SERVERDATA_EXECCOMMAND` request.
Output = 0,
}
impl ClientboundPacket {
pub fn write_buf(&self, id: i32, body: String) -> BytesMut {
pub fn write_buf(self, id: i32, body: &str) -> BytesMut {
// let len = outgoing.len() as u64;
let mut buf = BytesMut::new();
// 10 is for 4 bytes ty, 4 bytes id, and 2 terminating nul bytes.
buf.put_i32_le(10 + body.len() as i32);
buf.put_i32_le(id);
buf.put_i32_le(*self as i32);
buf.put_i32_le(self as i32);
let bytes = body.as_bytes();
buf.put_slice(bytes);
buf.put_u8(0);

View File

@ -113,8 +113,8 @@ impl CachedStatus {
max: config.max_players,
online: 0,
sample: vec![Sample {
name: "".into(),
id: "".into(),
name: String::new(),
id: String::new(),
}],
}),
description: config.motd.clone(),

View File

@ -56,6 +56,7 @@ pub struct Server {
impl Server {
#[allow(clippy::new_without_default)]
#[must_use]
pub fn new() -> Self {
// TODO: only create when needed
@ -126,13 +127,13 @@ impl Server {
P: ClientPacket,
{
for world in &self.worlds {
world.broadcast_packet_all(packet).await
world.broadcast_packet_all(packet).await;
}
}
/// Searches every world for a player by name
pub async fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
for world in self.worlds.iter() {
pub fn get_player_by_name(&self, name: &str) -> Option<Arc<Player>> {
for world in &self.worlds {
if let Some(player) = world.get_player_by_name(name) {
return Some(player);
}

View File

@ -8,6 +8,7 @@ pub struct Ticker {
}
impl Ticker {
#[must_use]
pub fn new(tps: f32) -> Self {
Self {
tick_interval: Duration::from_millis((1000.0 / tps) as u64),

View File

@ -43,6 +43,7 @@ pub struct World {
}
impl World {
#[must_use]
pub fn load(level: Level) -> Self {
Self {
level: Arc::new(Mutex::new(level)),
@ -174,7 +175,7 @@ impl World {
},
PlayerAction::UpdateListed(true),
],
})
});
}
player
.client
@ -234,7 +235,7 @@ impl World {
0.0,
0.0,
))
.await
.await;
}
// entity meta data
// set skin parts
@ -243,7 +244,7 @@ impl World {
entity_id.into(),
Metadata::new(17, VarInt(0), config.skin_parts),
);
self.broadcast_packet_all(&packet).await
self.broadcast_packet_all(&packet).await;
}
// Start waiting for level chunks, Sets the "Loading Terrain" screen
@ -266,12 +267,7 @@ impl World {
level.mark_chunk_as_newly_watched(chunks);
}
async fn spawn_world_chunks(
&self,
client: Arc<Client>,
chunks: Vec<Vector2<i32>>,
distance: i32,
) {
fn spawn_world_chunks(&self, client: Arc<Client>, chunks: Vec<Vector2<i32>>, distance: i32) {
if client.closed.load(std::sync::atomic::Ordering::Relaxed) {
log::info!(
"The connection with {} has closed before world chunks were spawned",
@ -286,7 +282,7 @@ impl World {
let chunks = Arc::new(chunks);
tokio::spawn(async move {
let level = level.lock().await;
level.fetch_chunks(&chunks, sender)
level.fetch_chunks(&chunks, sender);
});
tokio::spawn(async move {
@ -358,6 +354,6 @@ impl World {
pub async fn remove_entity(&self, entity: &Entity) {
self.broadcast_packet_all(&CRemoveEntities::new(&[entity.entity_id.into()]))
.await
.await;
}
}

View File

@ -32,7 +32,7 @@ pub async fn player_join(world: &World, player: Arc<Player>) {
chunk_z: chunk_pos.z.into(),
})
.await;
let view_distance = get_view_distance(&player).await as i32;
let view_distance = i32::from(get_view_distance(&player).await);
dbg!(view_distance);
let old_cylindrical = Cylindrical::new(
Vector2::new(watched_section.x, watched_section.z),
@ -57,9 +57,7 @@ pub async fn player_join(world: &World, player: Arc<Player>) {
);
if !loading_chunks.is_empty() {
world.mark_chunks_as_watched(&loading_chunks).await;
world
.spawn_world_chunks(player.client.clone(), loading_chunks, view_distance)
.await;
world.spawn_world_chunks(player.client.clone(), loading_chunks, view_distance);
}
if !unloading_chunks.is_empty() {
@ -93,7 +91,7 @@ pub async fn update_position(player: &Player) {
})
.await;
let view_distance = get_view_distance(player).await as i32;
let view_distance = i32::from(get_view_distance(player).await);
let old_cylindrical = Cylindrical::new(
Vector2::new(current_watched.x, current_watched.z),
view_distance,
@ -119,8 +117,7 @@ pub async fn update_position(player: &Player) {
entity.world.mark_chunks_as_watched(&loading_chunks).await;
entity
.world
.spawn_world_chunks(player.client.clone(), loading_chunks, view_distance)
.await;
.spawn_world_chunks(player.client.clone(), loading_chunks, view_distance);
}
if !unloading_chunks.is_empty() {
@ -145,6 +142,7 @@ pub async fn update_position(player: &Player) {
}
}
#[must_use]
pub const fn chunk_section_from_pos(block_pos: &WorldPosition) -> Vector3<i32> {
let block_pos = block_pos.0;
Vector3::new(

View File

@ -20,6 +20,7 @@ impl Default for Scoreboard {
}
impl Scoreboard {
#[must_use]
pub fn new() -> Self {
Self {
objectives: HashMap::new(),
@ -87,6 +88,7 @@ pub struct ScoreboardObjective<'a> {
}
impl<'a> ScoreboardObjective<'a> {
#[must_use]
pub const fn new(
name: &'a str,
display_name: TextComponent<'a>,
@ -111,6 +113,7 @@ pub struct ScoreboardScore<'a> {
}
impl<'a> ScoreboardScore<'a> {
#[must_use]
pub const fn new(
entity_name: &'a str,
objective_name: &'a str,