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