steel_core/command/builtins/
stopsound.rs1use std::sync::Arc;
2
3use steel_protocol::packets::game::{CStopSound, SoundSource};
4use steel_utils::{Identifier, translations};
5use text_components::TextComponent;
6
7use super::super::{
8 brigadier::{CommandNodeBuilder, CommandSyntaxError},
9 execution::{
10 CommandSource, SteelArgumentType, SteelCommandContext, SteelCommandRuntime, argument,
11 literal,
12 },
13 registration::CommandRegistration,
14};
15
16use crate::player::Player;
17
18pub(super) fn registration() -> CommandRegistration<CommandSource> {
19 CommandRegistration::new(Identifier::vanilla_static("stopsound"), |_| command())
20}
21
22fn command() -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
23 let mut targets =
24 argument("targets", SteelArgumentType::players()).executes(stop_all_for_targets);
25
26 for source in SoundSource::VALUES {
27 targets = targets.then(source_command(source));
28 }
29
30 targets = targets.then(
31 literal("*")
32 .then(argument("sound", SteelArgumentType::sound()).executes(stop_sound_any_source)),
33 );
34
35 literal("stopsound").then(targets)
36}
37
38fn source_command(source: SoundSource) -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
39 literal(source.name())
40 .executes(move |context| stop_all_for_targets_with_source(context, source))
41 .then(
42 argument("sound", SteelArgumentType::sound())
43 .executes(move |context| stop_sound(context, source)),
44 )
45}
46
47fn stop_all_for_targets(
48 context: &SteelCommandContext<CommandSource>,
49) -> Result<i32, CommandSyntaxError> {
50 let targets = context.players("targets")?;
51
52 execute(None, None, &targets);
53
54 context.source().send_success(
55 &TextComponent::from(&translations::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_ANY),
56 true,
57 );
58
59 target_count(&targets)
60}
61
62fn stop_all_for_targets_with_source(
63 context: &SteelCommandContext<CommandSource>,
64 source: SoundSource,
65) -> Result<i32, CommandSyntaxError> {
66 let targets = context.players("targets")?;
67
68 execute(Some(source), None, &targets);
69
70 let message = translations::COMMANDS_STOPSOUND_SUCCESS_SOURCE_ANY
71 .message([TextComponent::plain(source.name())])
72 .component();
73
74 context.source().send_success(&message, true);
75
76 target_count(&targets)
77}
78
79fn stop_sound_any_source(
80 context: &SteelCommandContext<CommandSource>,
81) -> Result<i32, CommandSyntaxError> {
82 let targets = context.players("targets")?;
83 let sound = context.identifier("sound")?.clone();
84
85 execute(None, Some(&sound), &targets);
86
87 let message = translations::COMMANDS_STOPSOUND_SUCCESS_SOURCELESS_SOUND
88 .message([TextComponent::plain(sound.to_string())])
89 .component();
90
91 context.source().send_success(&message, true);
92
93 target_count(&targets)
94}
95
96fn stop_sound(
97 context: &SteelCommandContext<CommandSource>,
98 source: SoundSource,
99) -> Result<i32, CommandSyntaxError> {
100 let targets = context.players("targets")?;
101 let sound = context.identifier("sound")?.clone();
102
103 execute(Some(source), Some(&sound), &targets);
104
105 let message = translations::COMMANDS_STOPSOUND_SUCCESS_SOURCE_SOUND
106 .message([
107 TextComponent::plain(sound.to_string()),
108 TextComponent::plain(source.name()),
109 ])
110 .component();
111
112 context.source().send_success(&message, true);
113
114 target_count(&targets)
115}
116
117fn execute(source: Option<SoundSource>, sound: Option<&Identifier>, targets: &[Arc<Player>]) {
118 for target in targets {
119 target.send_packet(CStopSound::new(source, sound.cloned()));
120 }
121}
122
123fn target_count(targets: &[Arc<Player>]) -> Result<i32, CommandSyntaxError> {
124 i32::try_from(targets.len()).map_err(|_| {
125 CommandSyntaxError::dynamic("Target player count exceeds the command result range")
126 })
127}