|
| 1 | +use pumpkin_data::translation; |
| 2 | +use pumpkin_util::permission::{Permission, PermissionDefault, PermissionRegistry}; |
| 3 | +use pumpkin_util::text::TextComponent; |
| 4 | + |
| 5 | +use crate::command::argument_builder::{ArgumentBuilder, argument, command, literal}; |
| 6 | +use crate::command::argument_types::range::IntRangeArgumentType; |
| 7 | +use crate::command::context::command_context::CommandContext; |
| 8 | +use crate::command::errors::error_types::CommandErrorType; |
| 9 | +use crate::command::node::dispatcher::CommandDispatcher; |
| 10 | +use crate::command::node::{CommandExecutor, CommandExecutorResult}; |
| 11 | + |
| 12 | +const DESCRIPTION: &str = "Draws a random value."; |
| 13 | + |
| 14 | +const PERMISSION: &str = "minecraft:command.random"; |
| 15 | + |
| 16 | +const ARG_RANGE: &str = "range"; |
| 17 | + |
| 18 | +/// The largest allowed range span, matching Vanilla (`i32::MAX - 1`). |
| 19 | +const MAX_RANGE_SPAN: i64 = (i32::MAX - 1) as i64; |
| 20 | + |
| 21 | +const RANGE_TOO_LARGE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new( |
| 22 | + translation::java::COMMANDS_RANDOM_ERROR_RANGE_TOO_LARGE, |
| 23 | + translation::java::COMMANDS_RANDOM_ERROR_RANGE_TOO_LARGE, |
| 24 | +); |
| 25 | + |
| 26 | +const RANGE_TOO_SMALL_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new( |
| 27 | + translation::java::COMMANDS_RANDOM_ERROR_RANGE_TOO_SMALL, |
| 28 | + translation::java::COMMANDS_RANDOM_ERROR_RANGE_TOO_SMALL, |
| 29 | +); |
| 30 | + |
| 31 | +struct RandomExecutor { |
| 32 | + /// Whether the result is announced to every player (`roll`) instead of |
| 33 | + /// only the command source (`value`). |
| 34 | + roll: bool, |
| 35 | +} |
| 36 | + |
| 37 | +impl CommandExecutor for RandomExecutor { |
| 38 | + fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> { |
| 39 | + Box::pin(async move { |
| 40 | + let bounds = IntRangeArgumentType::get(context, ARG_RANGE)?; |
| 41 | + let min = bounds.min().unwrap_or(i32::MIN); |
| 42 | + let max = bounds.max().unwrap_or(i32::MAX); |
| 43 | + |
| 44 | + let span = i64::from(max) - i64::from(min); |
| 45 | + if span == 0 { |
| 46 | + return Err(RANGE_TOO_SMALL_ERROR_TYPE.create_without_context()); |
| 47 | + } |
| 48 | + if span >= MAX_RANGE_SPAN { |
| 49 | + return Err(RANGE_TOO_LARGE_ERROR_TYPE.create_without_context()); |
| 50 | + } |
| 51 | + |
| 52 | + let result = rand::random_range(min..=max); |
| 53 | + |
| 54 | + if self.roll { |
| 55 | + let msg = TextComponent::translate_cross( |
| 56 | + translation::java::COMMANDS_RANDOM_ROLL, |
| 57 | + translation::java::COMMANDS_RANDOM_ROLL, |
| 58 | + [ |
| 59 | + context.source.display_name.clone(), |
| 60 | + TextComponent::text(result.to_string()), |
| 61 | + TextComponent::text(min.to_string()), |
| 62 | + TextComponent::text(max.to_string()), |
| 63 | + ], |
| 64 | + ); |
| 65 | + for player in context.server().get_all_players() { |
| 66 | + player.send_system_message(&msg).await; |
| 67 | + } |
| 68 | + // Also show the roll on the console (or another non-player source). |
| 69 | + if context.source.player_or_none().is_none() { |
| 70 | + context.source.send_message(msg).await; |
| 71 | + } |
| 72 | + } else { |
| 73 | + context |
| 74 | + .source |
| 75 | + .send_feedback( |
| 76 | + TextComponent::translate_cross( |
| 77 | + translation::java::COMMANDS_RANDOM_SAMPLE_SUCCESS, |
| 78 | + translation::java::COMMANDS_RANDOM_SAMPLE_SUCCESS, |
| 79 | + [TextComponent::text(result.to_string())], |
| 80 | + ), |
| 81 | + false, |
| 82 | + ) |
| 83 | + .await; |
| 84 | + } |
| 85 | + |
| 86 | + Ok(result) |
| 87 | + }) |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +pub fn register(dispatcher: &mut CommandDispatcher, registry: &mut PermissionRegistry) { |
| 92 | + registry.register_permission_or_panic(Permission::new( |
| 93 | + PERMISSION, |
| 94 | + DESCRIPTION, |
| 95 | + // Vanilla allows every player to use `/random value` and `/random roll`. |
| 96 | + PermissionDefault::Allow, |
| 97 | + )); |
| 98 | + |
| 99 | + dispatcher.register( |
| 100 | + command("random", DESCRIPTION) |
| 101 | + .requires(PERMISSION) |
| 102 | + .then(literal("value").then( |
| 103 | + argument(ARG_RANGE, IntRangeArgumentType).executes(RandomExecutor { roll: false }), |
| 104 | + )) |
| 105 | + .then(literal("roll").then( |
| 106 | + argument(ARG_RANGE, IntRangeArgumentType).executes(RandomExecutor { roll: true }), |
| 107 | + )), |
| 108 | + ); |
| 109 | +} |
0 commit comments