Skip to main content

steel_core/command/
mod.rs

1//! Brigadier-compatible command parsing, execution, and sender handling.
2
3mod api;
4pub(crate) mod brigadier;
5mod builtins;
6pub(crate) mod execution;
7mod protocol;
8mod queue;
9mod registration;
10pub mod sender;
11pub(crate) mod storage;
12
13pub use api::{
14    CommandArgument, CommandArgumentParser, CommandContext, CommandError, CommandNode,
15    CommandParserSource, CommandReader, CommandReaderCursor, CommandRegistration,
16    CommandRegistrationError, CommandRegistry, CommandSource, CommandSuggestionContext,
17    CommandSuggestions, SuspendedCommand, SuspendedCommandPoll, argument, literal,
18};
19pub use execution::CommandSuspensionOrder;
20
21pub(crate) use builtins::{
22    create_registered_dispatcher, gamemode::handle_client_request, player_can_change_difficulty,
23};
24pub(crate) use protocol::{command_suggestions_packet, command_tree_packet};
25pub use queue::CommandQueueFull;
26pub(crate) use queue::{
27    COMMAND_REQUESTS_PER_TICK, COMMAND_RESUMPTIONS_PER_TICK, CommandRequest, CommandRequestQueue,
28    PendingCommandExecutionQueue,
29};
30
31use steel_utils::entity_events::EntityStatus;
32
33use self::{
34    brigadier::CommandDispatcher as BrigadierCommandDispatcher,
35    execution::{CommandSource as InternalCommandSource, SteelCommandRuntime},
36};
37use crate::{player::Player, world::World};
38
39pub(crate) type CommandDispatcher =
40    BrigadierCommandDispatcher<InternalCommandSource, SteelCommandRuntime>;
41
42/// One command completion and its replacement range in UTF-16 code units.
43#[derive(Clone, Debug, PartialEq, Eq)]
44pub struct CommandCompletion {
45    replacement_start: usize,
46    replacement_length: usize,
47    text: String,
48}
49
50impl CommandCompletion {
51    pub(crate) const fn new(
52        replacement_start: usize,
53        replacement_length: usize,
54        text: String,
55    ) -> Self {
56        Self {
57            replacement_start,
58            replacement_length,
59            text,
60        }
61    }
62
63    /// Returns the inclusive replacement start in UTF-16 code units.
64    #[must_use]
65    pub const fn replacement_start(&self) -> usize {
66        self.replacement_start
67    }
68
69    /// Returns the replacement length in UTF-16 code units.
70    #[must_use]
71    pub const fn replacement_length(&self) -> usize {
72        self.replacement_length
73    }
74
75    /// Returns the replacement text.
76    #[must_use]
77    pub fn text(&self) -> &str {
78        &self.text
79    }
80}
81
82/// Projects Steel capabilities onto vanilla's shared gamemaster client affordance.
83/// Packet handlers still authorize game-mode and difficulty requests independently.
84pub(crate) fn client_permission_event(player: &Player, world: &World) -> EntityStatus {
85    client_permission_event_for_capabilities(
86        builtins::gamemode::player_can_use_client_switcher(player, world),
87        builtins::player_can_change_difficulty(player, world),
88    )
89}
90
91const fn client_permission_event_for_capabilities(
92    can_change_game_mode: bool,
93    can_change_difficulty: bool,
94) -> EntityStatus {
95    if can_change_game_mode || can_change_difficulty {
96        EntityStatus::PermissionLevelGamemasters
97    } else {
98        EntityStatus::PermissionLevelAll
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::client_permission_event_for_capabilities;
105    use steel_utils::entity_events::EntityStatus;
106
107    #[test]
108    fn gamemaster_projection_enables_either_supported_client_capability() {
109        assert_eq!(
110            client_permission_event_for_capabilities(true, false),
111            EntityStatus::PermissionLevelGamemasters
112        );
113        assert_eq!(
114            client_permission_event_for_capabilities(false, true),
115            EntityStatus::PermissionLevelGamemasters
116        );
117        assert_eq!(
118            client_permission_event_for_capabilities(false, false),
119            EntityStatus::PermissionLevelAll
120        );
121    }
122}