1use std::sync::Arc;
7
8use glam::DVec3;
9use rand::seq::SliceRandom;
10use simdnbt::owned::NbtCompound;
11use steel_math::wrap_degrees;
12use steel_registry::{
13 REGISTRY, RegistryExt as _, TaggedRegistryExt as _, entity_type::EntityTypeRef,
14 vanilla_entities,
15};
16use steel_utils::{
17 Identifier,
18 geometry::WorldAabb,
19 java,
20 nbt::{compare_nbt_compounds, parse_snbt_compound_argument},
21 translations,
22 types::GameType,
23};
24use text_components::TextComponent;
25use uuid::Uuid;
26
27use crate::{
28 command::brigadier::{
29 CommandSyntaxError, CommandSyntaxErrorKind, ReaderCursor, StringReader, SuggestionsBuilder,
30 },
31 entity::{Entity, SharedEntity},
32 player::Player,
33 scoreboard::{ScoreHolder, Scoreboard},
34 world::World,
35};
36
37use super::{CommandArgumentSource, CommandSource};
38
39const SORT_NEAREST: &str = "nearest";
40const SORT_FURTHEST: &str = "furthest";
41const SORT_RANDOM: &str = "random";
42const SORT_ARBITRARY: &str = "arbitrary";
43const SELECTOR_OPTION_KEYS: &[&str] = &[
44 "name",
45 "distance",
46 "level",
47 "x",
48 "y",
49 "z",
50 "dx",
51 "dy",
52 "dz",
53 "x_rotation",
54 "y_rotation",
55 "limit",
56 "sort",
57 "gamemode",
58 "type",
59 "tag",
60 "team",
61 "nbt",
62 "scores",
63 "advancements",
64 "predicate",
65];
66const UNSUPPORTED_SELECTOR_OPTION_KEYS: &[&str] = &[
67 "advancements",
69 "predicate",
71];
72const SET_ONCE_SELECTOR_OPTIONS: &[&str] = &[
73 "distance",
74 "level",
75 "x",
76 "y",
77 "z",
78 "dx",
79 "dy",
80 "dz",
81 "x_rotation",
82 "y_rotation",
83 "limit",
84 "sort",
85 "scores",
86 "advancements",
87];
88const GAME_MODE_SUGGESTIONS: &[&str] = &["survival", "creative", "adventure", "spectator"];
89
90#[derive(Clone, Debug, PartialEq)]
91pub(crate) struct EntitySelector {
92 kind: SelectorKind,
93 max_results: usize,
94 includes_entities: bool,
95 current_entity: bool,
96 world_limited: bool,
97 order: SelectorOrder,
98 position: SelectorPosition,
99 delta: SelectorDelta,
100 distance: Option<DoubleRange>,
101 level: Option<IntRange>,
102 x_rotation: Option<FloatRange>,
103 y_rotation: Option<FloatRange>,
104 filters: Vec<SelectorFilter>,
105 uses_advanced_options: bool,
106}
107
108#[derive(Clone, Debug, PartialEq)]
109enum SelectorKind {
110 Selector(SelectorType),
111 PlayerName(String),
112 EntityUuid(Uuid),
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116enum SelectorType {
117 AllPlayers,
118 AllEntities,
119 NearestEntity,
120 NearestPlayer,
121 RandomPlayer,
122 SelfEntity,
123}
124
125#[derive(Clone, Copy, Debug, PartialEq, Eq)]
126enum SelectorOrder {
127 Nearest,
128 Furthest,
129 Random,
130 Arbitrary,
131}
132
133#[derive(Clone, Debug, Default, PartialEq)]
134struct SelectorPosition {
135 x: Option<f64>,
136 y: Option<f64>,
137 z: Option<f64>,
138}
139
140impl SelectorPosition {
141 fn apply(&self, base: DVec3) -> DVec3 {
142 DVec3::new(
143 self.x.unwrap_or(base.x),
144 self.y.unwrap_or(base.y),
145 self.z.unwrap_or(base.z),
146 )
147 }
148}
149
150#[derive(Clone, Copy, Debug, Default, PartialEq)]
151struct SelectorDelta {
152 x: Option<f64>,
153 y: Option<f64>,
154 z: Option<f64>,
155}
156
157impl SelectorDelta {
158 const fn has_any(self) -> bool {
159 self.x.is_some() || self.y.is_some() || self.z.is_some()
160 }
161
162 fn aabb(self) -> WorldAabb {
163 create_delta_aabb(
164 self.x.unwrap_or(0.0),
165 self.y.unwrap_or(0.0),
166 self.z.unwrap_or(0.0),
167 )
168 }
169}
170
171#[derive(Clone, Debug, PartialEq)]
172enum SelectorFilter {
173 Alive,
174 Name {
175 value: String,
176 inverted: bool,
177 },
178 GameMode {
179 value: GameType,
180 inverted: bool,
181 },
182 EntityType {
183 value: EntityTypeRef,
184 inverted: bool,
185 },
186 EntityTypeTag {
187 value: Identifier,
188 inverted: bool,
189 },
190 Tag {
191 value: String,
192 inverted: bool,
193 },
194 Team {
195 value: String,
196 inverted: bool,
197 },
198 Nbt {
199 value: NbtCompound,
200 inverted: bool,
201 },
202 Scores(Vec<(String, IntRange)>),
203}
204
205#[derive(Clone, Copy, Debug, PartialEq)]
206struct DoubleRange {
207 min: Option<f64>,
208 max: Option<f64>,
209}
210
211impl DoubleRange {
212 fn matches_squared(self, value: f64) -> bool {
213 if let Some(min) = self.min
214 && value < min * min
215 {
216 return false;
217 }
218 if let Some(max) = self.max
219 && value > max * max
220 {
221 return false;
222 }
223 true
224 }
225}
226
227#[derive(Clone, Copy, Debug, PartialEq)]
228struct FloatRange {
229 min: Option<f32>,
230 max: Option<f32>,
231}
232
233impl FloatRange {
234 fn matches_rotation(self, value: f32) -> bool {
235 let min = wrap_degrees(self.min.unwrap_or(0.0));
236 let max = wrap_degrees(self.max.unwrap_or(359.0));
237 let value = wrap_degrees(value);
238 if min > max {
239 value >= min || value <= max
240 } else {
241 value >= min && value <= max
242 }
243 }
244}
245
246#[derive(Clone, Copy, Debug, PartialEq)]
247struct IntRange {
248 min: Option<i32>,
249 max: Option<i32>,
250}
251
252impl IntRange {
253 #[cfg(test)]
254 const fn exactly(value: i32) -> Self {
255 Self {
256 min: Some(value),
257 max: Some(value),
258 }
259 }
260
261 const fn matches(self, value: i32) -> bool {
262 if let Some(min) = self.min
263 && value < min
264 {
265 return false;
266 }
267 if let Some(max) = self.max
268 && value > max
269 {
270 return false;
271 }
272 true
273 }
274}
275
276#[derive(Clone, Debug, Default)]
277struct InvertableOptionState {
278 positive_seen: bool,
279 negative_seen: bool,
280}
281
282impl InvertableOptionState {
283 fn parse_element(&mut self, inverted: bool, option: &str) -> Result<(), SelectorParseError> {
284 if inverted {
285 if self.positive_seen {
286 return Err(SelectorParseError::invalid(format!(
287 "option '{option}' cannot be repeated after a positive value"
288 )));
289 }
290 self.negative_seen = true;
291 } else {
292 if self.positive_seen || self.negative_seen {
293 return Err(SelectorParseError::invalid(format!(
294 "option '{option}' cannot add a positive value after another value"
295 )));
296 }
297 self.positive_seen = true;
298 }
299 Ok(())
300 }
301
302 const fn suggestion_mode(&self) -> InvertableSuggestionMode {
303 if self.positive_seen {
304 InvertableSuggestionMode::None
305 } else if self.negative_seen {
306 InvertableSuggestionMode::NegativeOnly
307 } else {
308 InvertableSuggestionMode::Any
309 }
310 }
311}
312
313#[derive(Clone, Copy, Debug, PartialEq, Eq)]
314enum InvertableSuggestionMode {
315 Any,
316 NegativeOnly,
317 None,
318}
319
320impl InvertableSuggestionMode {
321 const fn allows_positive(self) -> bool {
322 matches!(self, Self::Any)
323 }
324
325 const fn allows_negative(self) -> bool {
326 matches!(self, Self::Any | Self::NegativeOnly)
327 }
328
329 const fn allows_any(self) -> bool {
330 !matches!(self, Self::None)
331 }
332}
333
334#[derive(Clone, Debug, Default)]
335struct EntityTypeOptionState {
336 invertible: InvertableOptionState,
337 tags_seen: Vec<Identifier>,
338}
339
340impl EntityTypeOptionState {
341 fn parse_element(&mut self, inverted: bool, option: &str) -> Result<(), SelectorParseError> {
342 self.invertible.parse_element(inverted, option)
343 }
344
345 fn parse_tag(&mut self, tag: &Identifier, option: &str) -> Result<(), SelectorParseError> {
346 if self.tags_seen.iter().any(|existing| existing == tag) {
347 return Err(SelectorParseError::invalid(format!(
348 "option '{option}' cannot repeat tag '#{tag}'"
349 )));
350 }
351 self.invertible.parse_element(true, option)?;
352 self.tags_seen.push(tag.clone());
353 Ok(())
354 }
355}
356
357#[derive(Clone, Debug, Default)]
358struct SelectorOptionState {
359 name: InvertableOptionState,
360 team: InvertableOptionState,
361 gamemode: InvertableOptionState,
362 entity_type: EntityTypeOptionState,
363 distance: bool,
364 level: bool,
365 x: bool,
366 y: bool,
367 z: bool,
368 dx: bool,
369 dy: bool,
370 dz: bool,
371 x_rotation: bool,
372 y_rotation: bool,
373 limit: bool,
374 sort: bool,
375 scores: bool,
376}
377
378#[derive(Clone, Debug)]
379struct SelectorParseError {
380 kind: SelectorParseErrorKind,
381 cursor: usize,
382}
383
384#[derive(Clone, Debug)]
385enum SelectorParseErrorKind {
386 NotAllowed,
387 AdvancedNotAllowed,
388 Invalid(Box<TextComponent>),
389 Unsupported(String),
390}
391
392impl SelectorParseError {
393 const fn not_allowed(cursor: usize) -> Self {
394 Self {
395 kind: SelectorParseErrorKind::NotAllowed,
396 cursor,
397 }
398 }
399
400 const fn advanced_not_allowed(cursor: usize) -> Self {
401 Self {
402 kind: SelectorParseErrorKind::AdvancedNotAllowed,
403 cursor,
404 }
405 }
406
407 fn invalid(message: impl Into<TextComponent>) -> Self {
408 Self {
409 kind: SelectorParseErrorKind::Invalid(Box::new(message.into())),
410 cursor: 0,
411 }
412 }
413
414 fn invalid_at(message: impl Into<TextComponent>, cursor: usize) -> Self {
415 Self {
416 kind: SelectorParseErrorKind::Invalid(Box::new(message.into())),
417 cursor,
418 }
419 }
420
421 fn unsupported(option: impl Into<String>, cursor: usize) -> Self {
422 Self {
423 kind: SelectorParseErrorKind::Unsupported(option.into()),
424 cursor,
425 }
426 }
427
428 fn message(self) -> TextComponent {
429 match self.kind {
430 SelectorParseErrorKind::NotAllowed => {
431 TextComponent::from(&translations::ARGUMENT_ENTITY_SELECTOR_NOT_ALLOWED)
432 }
433 SelectorParseErrorKind::AdvancedNotAllowed => {
434 TextComponent::from("Advanced entity selectors are not allowed")
435 }
436 SelectorParseErrorKind::Invalid(message) => *message,
437 SelectorParseErrorKind::Unsupported(option) => {
438 TextComponent::from(format!("Unsupported entity selector option: {option}"))
439 }
440 }
441 }
442}
443
444mod model;
445mod parser;
446mod suggestions;
447
448use model::create_delta_aabb;
449pub(crate) use parser::*;
450pub(crate) use suggestions::*;
451
452#[cfg(test)]
453mod tests;