refactor: enchant command code refactor (#2768)

* refactor: enchant command

Removes the only_one var and nested logic

* chore: removed TODO since it is now patched

In commit 938a627 (fix(item): prevent stacking items with different components (#2624)) this is patched since if a item is enchanted it will always have a extra data component which will now be detected and synced
This commit is contained in:
TNXWasTaken
2026-08-05 07:37:15 +01:00
committed by GitHub
parent 7c3177b404
commit 060c10dfa2
2 changed files with 92 additions and 86 deletions

View File

@@ -547,7 +547,6 @@ impl ItemStack {
}
pub fn enchant(&mut self, enchantment: &'static Enchantment, level: i32) {
// TODO itemstack may not send update packet to client
if level <= 0 {
return;
}

View File

@@ -1,5 +1,6 @@
use pumpkin_data::translation;
use pumpkin_data::{Enchantment, translation};
use pumpkin_util::text::TextComponent;
use std::sync::Arc;
use crate::command::args::bounded_num::{BoundedNumArgumentConsumer, NotInBounds};
use crate::command::args::entities::EntitiesArgumentConsumer;
@@ -8,6 +9,7 @@ use crate::command::args::{ConsumedArgs, FindArgDefaultName};
use crate::command::tree::CommandTree;
use crate::command::tree::builder::argument_default_name;
use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender};
use crate::entity::EntityBase;
use pumpkin_data::data_component_impl::EnchantmentsImpl;
const NAMES: [&str; 1] = ["enchant"];
@@ -16,7 +18,6 @@ const DESCRIPTION: &str = "Adds an enchantment to a player's selected item, subj
struct Executor;
impl CommandExecutor for Executor {
#[expect(clippy::too_many_lines)]
fn execute<'a>(
&'a self,
sender: &'a CommandSender,
@@ -65,94 +66,46 @@ impl CommandExecutor for Executor {
return Err(CommandError::CommandFailed(msg));
}
let only_one = targets.len() == 1;
let mut success = 0;
let mut successful_targets = 0;
if targets.len() == 1 {
return match enchant_target(&targets[0], enchantment, level).await {
Ok(()) => {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_SUCCESS_SINGLE,
translation::bedrock::COMMANDS_ENCHANT_SUCCESS,
[
enchantment.get_fullname(level),
targets[0].get_display_name().await,
],
);
sender.send_message(msg).await;
Ok(1)
}
Err(e) => Err(e),
};
}
for target in targets {
// let Some(target) = target.get_living_entity() else {
// if only_one {
// let msg = TextComponent::translate_cross(// "commands.enchant.failed.entity".clone(), // "commands.enchant.failed.entity", // [targets[0].get_display_name().await],
// );
// sender.send_message(msg).await;
// return Ok(());
// }
// continue;
// };
// let lock = target.entity_equipment.lock().await.get(&EquipmentSlot::MAIN_HAND); TODO this dont work
let Some(player) = target.get_player() else {
continue;
};
let lock = player.inventory.held_item();
let mut item = lock.lock().await;
if item.is_empty() {
if only_one {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_ITEMLESS,
translation::bedrock::COMMANDS_ENCHANT_NOITEM,
[targets[0].get_display_name().await],
);
return Err(CommandError::CommandFailed(msg));
}
continue;
}
if !enchantment.can_enchant(item.item) {
if only_one {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_INCOMPATIBLE,
translation::bedrock::COMMANDS_ENCHANT_CANTENCHANT,
[item.item.translated_name()],
);
return Err(CommandError::CommandFailed(msg));
}
continue;
}
if let Some(data) = item.get_data_component::<EnchantmentsImpl>() {
if enchantment.is_enchantment_compatible(data) {
item.enchant(enchantment, level);
success += 1;
} else if only_one {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_INCOMPATIBLE,
translation::bedrock::COMMANDS_ENCHANT_CANTENCHANT,
[item.item.translated_name()],
);
return Err(CommandError::CommandFailed(msg));
}
} else {
item.enchant(enchantment, level);
success += 1;
if enchant_target(target, enchantment, level).await.is_ok() {
successful_targets += 1;
}
}
if success == 0 {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED,
translation::bedrock::COMMANDS_ENCHANT_CANTENCHANT,
[TextComponent::text("")],
);
return Err(CommandError::CommandFailed(msg));
if successful_targets == 0 {
return Err(commands_enchant_failed());
}
if only_one {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_SUCCESS_SINGLE,
translation::bedrock::COMMANDS_ENCHANT_SUCCESS,
[
enchantment.get_fullname(level),
targets[0].get_display_name().await,
],
);
sender.send_message(msg).await;
} else {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_SUCCESS_MULTIPLE,
translation::bedrock::COMMANDS_ENCHANT_SUCCESS,
[
enchantment.get_fullname(level),
TextComponent::text(targets.len().to_string()),
],
);
sender.send_message(msg).await;
}
Ok(success)
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_SUCCESS_MULTIPLE,
translation::bedrock::COMMANDS_ENCHANT_SUCCESS,
[
enchantment.get_fullname(level),
TextComponent::text(targets.len().to_string()),
],
);
sender.send_message(msg).await;
Ok(successful_targets)
})
}
}
@@ -164,6 +117,60 @@ const fn enchantment_level_consumer() -> BoundedNumArgumentConsumer<i32> {
.max(i32::MAX)
}
fn commands_enchant_failed() -> CommandError {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED,
translation::bedrock::COMMANDS_ENCHANT_CANTENCHANT,
[TextComponent::text("")],
);
CommandError::CommandFailed(msg)
}
async fn enchant_target(
target: &Arc<dyn EntityBase>,
enchantment: &'static Enchantment,
level: i32,
) -> Result<(), CommandError> {
let Some(player) = target.get_player() else {
return Err(commands_enchant_failed());
};
let lock = player.inventory.held_item();
let mut item = lock.lock().await;
if item.is_empty() {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_ITEMLESS,
translation::bedrock::COMMANDS_ENCHANT_NOITEM,
[target.get_display_name().await],
);
return Err(CommandError::CommandFailed(msg));
}
if !enchantment.can_enchant(item.item) {
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_INCOMPATIBLE,
translation::bedrock::COMMANDS_ENCHANT_CANTENCHANT,
[item.item.translated_name()],
);
return Err(CommandError::CommandFailed(msg));
}
if let Some(data) = item.get_data_component::<EnchantmentsImpl>()
&& !enchantment.is_enchantment_compatible(data)
{
let msg = TextComponent::translate_cross(
translation::java::COMMANDS_ENCHANT_FAILED_INCOMPATIBLE,
translation::bedrock::COMMANDS_ENCHANT_CANTENCHANT,
[item.item.translated_name()],
);
return Err(CommandError::CommandFailed(msg));
}
item.enchant(enchantment, level);
Ok(())
}
pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then(
argument_default_name(EntitiesArgumentConsumer).then(