steel_core/command/builtins/
tellraw.rs1use steel_protocol::packets::game::CSystemChat;
4use steel_utils::Identifier;
5
6use super::super::{
7 brigadier::{CommandNodeBuilder, CommandSyntaxError},
8 execution::{
9 CommandSource, CommandTextResolver, SteelArgumentType, SteelCommandContext,
10 SteelCommandRuntime, argument, literal,
11 },
12 registration::CommandRegistration,
13};
14
15pub(super) fn registration() -> CommandRegistration<CommandSource> {
16 CommandRegistration::new(Identifier::vanilla_static("tellraw"), |_| command())
17}
18
19fn command() -> CommandNodeBuilder<CommandSource, SteelCommandRuntime> {
20 literal("tellraw").then(
21 argument("targets", SteelArgumentType::players())
22 .then(argument("message", SteelArgumentType::component()).executes(send_message)),
23 )
24}
25
26fn send_message(context: &SteelCommandContext<CommandSource>) -> Result<i32, CommandSyntaxError> {
27 let targets = context.players("targets")?;
28 let message = context.text_component("message")?;
29 let result = i32::try_from(targets.len()).map_err(|_| {
30 CommandSyntaxError::dynamic("Target player count exceeds the command result range")
31 })?;
32
33 for target in targets {
34 let message = message.try_resolve(&CommandTextResolver::with_entity_override(
35 context.source(),
36 target.as_ref(),
37 ))?;
38 target.send_packet(CSystemChat {
39 content: message,
40 overlay: false,
41 });
42 }
43 Ok(result)
44}
45
46#[cfg(test)]
47mod tests {
48 use steel_registry::init_vanilla_registry;
49
50 use super::super::create_dispatcher;
51 use crate::command::execution::SteelArgumentType;
52
53 #[test]
54 fn tellraw_graph_uses_player_targets_and_component_messages() {
55 init_vanilla_registry();
56 let Ok(dispatcher) = create_dispatcher() else {
57 panic!("built-in commands should register");
58 };
59 let Some(tellraw) = dispatcher.children(dispatcher.root()).and_then(|children| {
60 children.iter().copied().find(|child| {
61 dispatcher
62 .node(*child)
63 .is_some_and(|node| node.name() == "tellraw")
64 })
65 }) else {
66 panic!("tellraw root should exist");
67 };
68 let Some(root) = dispatcher.node(tellraw) else {
69 panic!("tellraw root node should exist");
70 };
71 assert!(root.is_restricted());
72 assert!(!root.is_executable());
73
74 let Some(targets) = dispatcher
75 .children(tellraw)
76 .and_then(|children| children.first())
77 .copied()
78 else {
79 panic!("tellraw targets should exist");
80 };
81 assert_eq!(
82 dispatcher
83 .node(targets)
84 .and_then(|node| node.argument_type()),
85 Some(&SteelArgumentType::players())
86 );
87
88 let Some(message) = dispatcher
89 .children(targets)
90 .and_then(|children| children.first())
91 .copied()
92 else {
93 panic!("tellraw message should exist");
94 };
95 let Some(message) = dispatcher.node(message) else {
96 panic!("tellraw message node should exist");
97 };
98 assert_eq!(
99 message.argument_type(),
100 Some(&SteelArgumentType::component())
101 );
102 assert!(message.is_executable());
103 }
104}