steel_core/command/
sender.rs1use std::{fmt, sync::Arc};
3use text_components::TextComponent;
4use uuid::Uuid;
5
6use crate::{
7 player::{DomainResidenceToken, Player},
8 server::Server,
9};
10
11#[derive(Clone)]
13pub enum CommandSender {
14 Player(Arc<Player>),
16 Console,
18 Rcon,
20}
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24pub(crate) enum CommandSenderKey {
25 Player(Uuid),
26 Console,
27 Rcon,
28}
29
30#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub(crate) enum CommandSuggestionKey {
33 Player {
34 uuid: Uuid,
35 session_address: usize,
36 residence: Option<DomainResidenceToken>,
37 },
38 Console,
39 Rcon,
40}
41
42#[derive(Clone)]
47pub(crate) struct CommandExecutionOwner {
48 sender: CommandSender,
49 player_residence: Option<DomainResidenceToken>,
50}
51
52impl CommandExecutionOwner {
53 pub(crate) fn capture(sender: CommandSender, server: &Server) -> Self {
54 let player_residence = sender.get_player().and_then(|player| {
55 let residence = player.domain_residence_token();
58 server.command_world_for_player(player).map(|_| residence)
59 });
60 Self {
61 sender,
62 player_residence,
63 }
64 }
65
66 #[cfg(test)]
67 pub(crate) fn non_player_for_test(sender: CommandSender) -> Self {
68 assert!(
69 sender.get_player().is_none(),
70 "test helper only constructs non-player command owners"
71 );
72 Self {
73 sender,
74 player_residence: None,
75 }
76 }
77
78 pub(crate) fn key(&self) -> CommandSenderKey {
79 self.sender.key()
80 }
81
82 pub(crate) fn suggestion_key(&self) -> CommandSuggestionKey {
83 match &self.sender {
84 CommandSender::Player(player) => CommandSuggestionKey::Player {
85 uuid: player.gameprofile.id,
86 session_address: Arc::as_ptr(player) as usize,
89 residence: self.player_residence,
90 },
91 CommandSender::Console => CommandSuggestionKey::Console,
92 CommandSender::Rcon => CommandSuggestionKey::Rcon,
93 }
94 }
95
96 pub(crate) const fn sender(&self) -> &CommandSender {
97 &self.sender
98 }
99
100 pub(crate) fn is_current(&self, server: &Server) -> bool {
101 let CommandSender::Player(player) = &self.sender else {
102 return true;
103 };
104 let Some(residence) = self.player_residence else {
105 return false;
106 };
107 server.command_world_for_player(player).is_some()
108 && player.is_domain_residence_current(residence)
109 }
110}
111
112impl CommandSender {
113 pub(crate) fn key(&self) -> CommandSenderKey {
114 match self {
115 Self::Player(player) => CommandSenderKey::Player(player.gameprofile.id),
116 Self::Console => CommandSenderKey::Console,
117 Self::Rcon => CommandSenderKey::Rcon,
118 }
119 }
120
121 #[must_use]
123 pub const fn get_player(&self) -> Option<&Arc<Player>> {
124 match self {
125 Self::Player(player) => Some(player),
126 _ => None,
127 }
128 }
129
130 pub fn send_message(&self, text: &TextComponent) {
132 match self {
133 Self::Player(player) => player.send_message(text),
134 Self::Console => log::info!("{text}"),
135 Self::Rcon => log::warn!("Dropping Rcon command message until Rcon output is wired"),
137 }
138 }
139}
140
141impl fmt::Display for CommandSender {
142 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143 write!(
144 f,
145 "{}",
146 match self {
147 Self::Player(p) => &p.gameprofile.name,
148 Self::Console => "Server",
149 Self::Rcon => "Rcon",
150 }
151 )
152 }
153}