aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2026-05-06 18:38:23 -0545
committermat <git@matdoes.dev>2026-05-07 08:05:58 -1200
commitcabc8b60a729ba17f5b75f7a7956c6d1ddcc8919 (patch)
tree237fd12a9768fe7431ce42dfbdde60f4c7850e06
parent9ffd0e80bbb3feace231553d6539124585b03e3c (diff)
downloadazalea-drasl-cabc8b60a729ba17f5b75f7a7956c6d1ddcc8919.tar.xz
azalea-brigadier now allows commands to return a Result
-rw-r--r--CHANGELOG.md5
-rw-r--r--azalea-brigadier/src/arguments/bool_argument_type.rs2
-rw-r--r--azalea-brigadier/src/arguments/double_argument_type.rs2
-rw-r--r--azalea-brigadier/src/arguments/float_argument_type.rs2
-rw-r--r--azalea-brigadier/src/arguments/integer_argument_type.rs2
-rw-r--r--azalea-brigadier/src/arguments/long_argument_type.rs2
-rw-r--r--azalea-brigadier/src/arguments/string_argument_type.rs2
-rw-r--r--azalea-brigadier/src/builder/argument_builder.rs48
-rw-r--r--azalea-brigadier/src/builder/literal_argument_builder.rs4
-rw-r--r--azalea-brigadier/src/builder/required_argument_builder.rs22
-rw-r--r--azalea-brigadier/src/command_dispatcher.rs56
-rw-r--r--azalea-brigadier/src/context/command_context.rs30
-rw-r--r--azalea-brigadier/src/context/command_context_builder.rs34
-rw-r--r--azalea-brigadier/src/context/context_chain.rs81
-rw-r--r--azalea-brigadier/src/context/parsed_command_node.rs6
-rw-r--r--azalea-brigadier/src/context/suggestion_context.rs4
-rw-r--r--azalea-brigadier/src/errors/command_syntax_error.rs4
-rw-r--r--azalea-brigadier/src/errors/mod.rs21
-rw-r--r--azalea-brigadier/src/modifier.rs4
-rw-r--r--azalea-brigadier/src/parse_results.rs8
-rw-r--r--azalea-brigadier/src/result_consumer.rs14
-rw-r--r--azalea-brigadier/src/suggestion/suggestion_provider.rs4
-rw-r--r--azalea-brigadier/src/tree/mod.rs49
-rw-r--r--azalea-brigadier/tests/bevy_app_usage.rs4
-rw-r--r--azalea-brigadier/tests/command_dispatcher_test.rs6
-rw-r--r--azalea/examples/steal.rs6
-rw-r--r--azalea/examples/testbot/commands.rs5
-rw-r--r--azalea/examples/testbot/commands/combat.rs11
-rw-r--r--azalea/examples/testbot/commands/debug.rs110
-rw-r--r--azalea/examples/testbot/commands/movement.rs74
-rw-r--r--azalea/examples/testbot/main.rs17
-rw-r--r--azalea/src/_docs/performance.md5
-rw-r--r--azalea/src/client_impl/mod.rs5
-rw-r--r--azalea/src/container.rs13
-rw-r--r--azalea/src/lib.rs7
35 files changed, 371 insertions, 298 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 37696906..6db37359 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,10 +10,13 @@ is breaking anyways, semantic versioning is not followed.
### Added
+- `azalea-brigadier` now optionally allows commands to return a `Result<i32, _>` instead of just an `i32`.
+- `azalea-chat` is now re-exported in `azalea::chat`.
+
### Changed
-- Re-export `azalea-chat` from `azalea`, and move the other `azalea::chat` module to `azalea::client_chat`.
- Many functions in `Client` and `EntityRef` now return an `AzaleaResult` instead of panicking when getting entity data fails.
+- The previous `azalea::chat` module (from `azalea-client`) was moved to `azalea::client_chat`.
### Fixed
diff --git a/azalea-brigadier/src/arguments/bool_argument_type.rs b/azalea-brigadier/src/arguments/bool_argument_type.rs
index 5005cda4..51d4a216 100644
--- a/azalea-brigadier/src/arguments/bool_argument_type.rs
+++ b/azalea-brigadier/src/arguments/bool_argument_type.rs
@@ -34,7 +34,7 @@ impl ArgumentType for Boolean {
pub fn bool() -> impl ArgumentType {
Boolean
}
-pub fn get_bool<S>(context: &CommandContext<S>, name: &str) -> Option<bool> {
+pub fn get_bool<S, R>(context: &CommandContext<S, R>, name: &str) -> Option<bool> {
context
.argument(name)
.expect("argument with name not found")
diff --git a/azalea-brigadier/src/arguments/double_argument_type.rs b/azalea-brigadier/src/arguments/double_argument_type.rs
index f4d1d05f..daa1bab3 100644
--- a/azalea-brigadier/src/arguments/double_argument_type.rs
+++ b/azalea-brigadier/src/arguments/double_argument_type.rs
@@ -51,7 +51,7 @@ impl ArgumentType for Double {
pub fn double() -> impl ArgumentType {
Double::default()
}
-pub fn get_double<S>(context: &CommandContext<S>, name: &str) -> Option<f64> {
+pub fn get_double<S, R>(context: &CommandContext<S, R>, name: &str) -> Option<f64> {
context
.argument(name)
.unwrap()
diff --git a/azalea-brigadier/src/arguments/float_argument_type.rs b/azalea-brigadier/src/arguments/float_argument_type.rs
index 766a733f..f68700ec 100644
--- a/azalea-brigadier/src/arguments/float_argument_type.rs
+++ b/azalea-brigadier/src/arguments/float_argument_type.rs
@@ -51,7 +51,7 @@ impl ArgumentType for Float {
pub fn float() -> impl ArgumentType {
Float::default()
}
-pub fn get_float<S>(context: &CommandContext<S>, name: &str) -> Option<f32> {
+pub fn get_float<S, R>(context: &CommandContext<S, R>, name: &str) -> Option<f32> {
context
.argument(name)
.unwrap()
diff --git a/azalea-brigadier/src/arguments/integer_argument_type.rs b/azalea-brigadier/src/arguments/integer_argument_type.rs
index ea547f06..24e9c95f 100644
--- a/azalea-brigadier/src/arguments/integer_argument_type.rs
+++ b/azalea-brigadier/src/arguments/integer_argument_type.rs
@@ -51,7 +51,7 @@ impl ArgumentType for Integer {
pub fn integer() -> impl ArgumentType {
Integer::default()
}
-pub fn get_integer<S>(context: &CommandContext<S>, name: &str) -> Option<i32> {
+pub fn get_integer<S, R>(context: &CommandContext<S, R>, name: &str) -> Option<i32> {
context
.argument(name)
.unwrap()
diff --git a/azalea-brigadier/src/arguments/long_argument_type.rs b/azalea-brigadier/src/arguments/long_argument_type.rs
index de9b27cc..d4369a0d 100644
--- a/azalea-brigadier/src/arguments/long_argument_type.rs
+++ b/azalea-brigadier/src/arguments/long_argument_type.rs
@@ -51,7 +51,7 @@ impl ArgumentType for Long {
pub fn long() -> impl ArgumentType {
Long::default()
}
-pub fn get_long<S>(context: &CommandContext<S>, name: &str) -> Option<i64> {
+pub fn get_long<S, R>(context: &CommandContext<S, R>, name: &str) -> Option<i64> {
context
.argument(name)
.unwrap()
diff --git a/azalea-brigadier/src/arguments/string_argument_type.rs b/azalea-brigadier/src/arguments/string_argument_type.rs
index b074132f..1cac531a 100644
--- a/azalea-brigadier/src/arguments/string_argument_type.rs
+++ b/azalea-brigadier/src/arguments/string_argument_type.rs
@@ -52,7 +52,7 @@ pub fn string() -> impl ArgumentType {
pub fn greedy_string() -> impl ArgumentType {
StringArgument::GreedyPhrase
}
-pub fn get_string<S>(context: &CommandContext<S>, name: &str) -> Option<String> {
+pub fn get_string<S, R>(context: &CommandContext<S, R>, name: &str) -> Option<String> {
context
.argument(name)
.unwrap()
diff --git a/azalea-brigadier/src/builder/argument_builder.rs b/azalea-brigadier/src/builder/argument_builder.rs
index 27320eba..7617d08a 100644
--- a/azalea-brigadier/src/builder/argument_builder.rs
+++ b/azalea-brigadier/src/builder/argument_builder.rs
@@ -14,11 +14,11 @@ use crate::{
};
#[derive(Debug)]
-pub enum ArgumentBuilderType<S> {
+pub enum ArgumentBuilderType<S, R> {
Literal(Literal),
- Argument(Argument<S>),
+ Argument(Argument<S, R>),
}
-impl<S> Clone for ArgumentBuilderType<S> {
+impl<S, R> Clone for ArgumentBuilderType<S, R> {
fn clone(&self) -> Self {
match self {
ArgumentBuilderType::Literal(literal) => ArgumentBuilderType::Literal(literal.clone()),
@@ -30,20 +30,20 @@ impl<S> Clone for ArgumentBuilderType<S> {
}
/// A node that hasn't yet been built.
-pub struct ArgumentBuilder<S> {
- arguments: CommandNode<S>,
+pub struct ArgumentBuilder<S, R = i32> {
+ arguments: CommandNode<S, R>,
- command: Command<S>,
+ command: Command<S, R>,
requirement: Arc<dyn Fn(&S) -> bool + Send + Sync>,
- target: Option<Arc<RwLock<CommandNode<S>>>>,
+ target: Option<Arc<RwLock<CommandNode<S, R>>>>,
forks: bool,
- modifier: Option<Arc<RedirectModifier<S>>>,
+ modifier: Option<Arc<RedirectModifier<S, R>>>,
}
/// A node that isn't yet built.
-impl<S> ArgumentBuilder<S> {
- pub fn new(value: ArgumentBuilderType<S>) -> Self {
+impl<S, R> ArgumentBuilder<S, R> {
+ pub fn new(value: ArgumentBuilderType<S, R>) -> Self {
Self {
arguments: CommandNode {
value,
@@ -65,14 +65,14 @@ impl<S> ArgumentBuilder<S> {
/// literal("foo").then(literal("bar").executes(|ctx: &CommandContext<()>| 42))
/// # ;
/// ```
- pub fn then(self, argument: ArgumentBuilder<S>) -> Self {
+ pub fn then(self, argument: ArgumentBuilder<S, R>) -> Self {
self.then_built(argument.build())
}
/// Add an already built child node to this node.
///
/// You should usually use [`Self::then`] instead.
- pub fn then_built(mut self, argument: CommandNode<S>) -> Self {
+ pub fn then_built(mut self, argument: CommandNode<S, R>) -> Self {
self.arguments.add_child(&Arc::new(RwLock::new(argument)));
self
}
@@ -90,9 +90,9 @@ impl<S> ArgumentBuilder<S> {
/// ```
pub fn executes<F>(mut self, f: F) -> Self
where
- F: Fn(&CommandContext<S>) -> i32 + Send + Sync + 'static,
+ F: Fn(&CommandContext<S, R>) -> R + Send + Sync + 'static,
{
- self.command = Some(Arc::new(move |ctx: &CommandContext<S>| Ok(f(ctx))));
+ self.command = Some(Arc::new(move |ctx: &CommandContext<S, R>| Ok(f(ctx))));
self
}
@@ -100,7 +100,7 @@ impl<S> ArgumentBuilder<S> {
/// CommandSyntaxError>`.
pub fn executes_result<F>(mut self, f: F) -> Self
where
- F: Fn(&CommandContext<S>) -> Result<i32, CommandSyntaxError> + Send + Sync + 'static,
+ F: Fn(&CommandContext<S, R>) -> Result<R, CommandSyntaxError> + Send + Sync + 'static,
{
self.command = Some(Arc::new(f));
self
@@ -131,22 +131,22 @@ impl<S> ArgumentBuilder<S> {
self
}
- pub fn redirect(self, target: Arc<RwLock<CommandNode<S>>>) -> Self {
+ pub fn redirect(self, target: Arc<RwLock<CommandNode<S, R>>>) -> Self {
self.forward(target, None, false)
}
pub fn fork(
self,
- target: Arc<RwLock<CommandNode<S>>>,
- modifier: Arc<RedirectModifier<S>>,
+ target: Arc<RwLock<CommandNode<S, R>>>,
+ modifier: Arc<RedirectModifier<S, R>>,
) -> Self {
self.forward(target, Some(modifier), true)
}
pub fn forward(
mut self,
- target: Arc<RwLock<CommandNode<S>>>,
- modifier: Option<Arc<RedirectModifier<S>>>,
+ target: Arc<RwLock<CommandNode<S, R>>>,
+ modifier: Option<Arc<RedirectModifier<S, R>>>,
fork: bool,
) -> Self {
if !self.arguments.children.is_empty() {
@@ -158,13 +158,13 @@ impl<S> ArgumentBuilder<S> {
self
}
- pub fn arguments(&self) -> &CommandNode<S> {
+ pub fn arguments(&self) -> &CommandNode<S, R> {
&self.arguments
}
/// Manually build this node into a [`CommandNode`]. You probably don't need
/// to do this yourself.
- pub fn build(self) -> CommandNode<S> {
+ pub fn build(self) -> CommandNode<S, R> {
let mut result = CommandNode {
value: self.arguments.value,
command: self.command,
@@ -185,7 +185,7 @@ impl<S> ArgumentBuilder<S> {
}
}
-impl<S> Debug for ArgumentBuilder<S> {
+impl<S, R> Debug for ArgumentBuilder<S, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ArgumentBuilder")
.field("arguments", &self.arguments)
@@ -197,7 +197,7 @@ impl<S> Debug for ArgumentBuilder<S> {
.finish()
}
}
-impl<S> Clone for ArgumentBuilder<S> {
+impl<S, R> Clone for ArgumentBuilder<S, R> {
fn clone(&self) -> Self {
Self {
arguments: self.arguments.clone(),
diff --git a/azalea-brigadier/src/builder/literal_argument_builder.rs b/azalea-brigadier/src/builder/literal_argument_builder.rs
index 7045b79f..c55fbeed 100644
--- a/azalea-brigadier/src/builder/literal_argument_builder.rs
+++ b/azalea-brigadier/src/builder/literal_argument_builder.rs
@@ -12,13 +12,13 @@ impl Literal {
}
}
-impl<S> From<Literal> for ArgumentBuilderType<S> {
+impl<S, R> From<Literal> for ArgumentBuilderType<S, R> {
fn from(literal: Literal) -> Self {
Self::Literal(literal)
}
}
/// Shortcut for creating a new literal builder node.
-pub fn literal<S>(value: &str) -> ArgumentBuilder<S> {
+pub fn literal<S, R>(value: &str) -> ArgumentBuilder<S, R> {
ArgumentBuilder::new(ArgumentBuilderType::Literal(Literal::new(value)))
}
diff --git a/azalea-brigadier/src/builder/required_argument_builder.rs b/azalea-brigadier/src/builder/required_argument_builder.rs
index 01bfc0bc..e0822db3 100644
--- a/azalea-brigadier/src/builder/required_argument_builder.rs
+++ b/azalea-brigadier/src/builder/required_argument_builder.rs
@@ -16,16 +16,16 @@ use crate::{
/// An argument node type.
///
/// The `T` type parameter is the type of the argument, which can be anything.
-pub struct Argument<S> {
+pub struct Argument<S, R> {
pub name: String,
parser: Arc<dyn ArgumentType + Send + Sync>,
- custom_suggestions: Option<Arc<dyn SuggestionProvider<S> + Send + Sync>>,
+ custom_suggestions: Option<Arc<dyn SuggestionProvider<S, R> + Send + Sync>>,
}
-impl<S> Argument<S> {
+impl<S, R> Argument<S, R> {
pub fn new(
name: &str,
parser: Arc<dyn ArgumentType + Send + Sync>,
- custom_suggestions: Option<Arc<dyn SuggestionProvider<S> + Send + Sync>>,
+ custom_suggestions: Option<Arc<dyn SuggestionProvider<S, R> + Send + Sync>>,
) -> Self {
Self {
name: name.to_owned(),
@@ -40,7 +40,7 @@ impl<S> Argument<S> {
pub fn list_suggestions(
&self,
- context: CommandContext<S>,
+ context: CommandContext<S, R>,
builder: SuggestionsBuilder,
) -> Suggestions {
if let Some(s) = &self.custom_suggestions {
@@ -55,13 +55,13 @@ impl<S> Argument<S> {
}
}
-impl<S> From<Argument<S>> for ArgumentBuilderType<S> {
- fn from(argument: Argument<S>) -> Self {
+impl<S, R> From<Argument<S, R>> for ArgumentBuilderType<S, R> {
+ fn from(argument: Argument<S, R>) -> Self {
Self::Argument(argument)
}
}
-impl<S> Debug for Argument<S> {
+impl<S, R> Debug for Argument<S, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Argument")
.field("name", &self.name)
@@ -71,14 +71,14 @@ impl<S> Debug for Argument<S> {
}
/// Shortcut for creating a new argument builder node.
-pub fn argument<S>(
+pub fn argument<S, R>(
name: &str,
parser: impl ArgumentType + Send + Sync + 'static,
-) -> ArgumentBuilder<S> {
+) -> ArgumentBuilder<S, R> {
ArgumentBuilder::new(Argument::new(name, Arc::new(parser), None).into())
}
-impl<S> Clone for Argument<S> {
+impl<S, R> Clone for Argument<S, R> {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
diff --git a/azalea-brigadier/src/command_dispatcher.rs b/azalea-brigadier/src/command_dispatcher.rs
index ecf5d157..5b162964 100644
--- a/azalea-brigadier/src/command_dispatcher.rs
+++ b/azalea-brigadier/src/command_dispatcher.rs
@@ -11,7 +11,7 @@ use parking_lot::RwLock;
use crate::{
builder::argument_builder::ArgumentBuilder,
context::{CommandContextBuilder, ContextChain},
- errors::{BuiltInError, CommandSyntaxError},
+ errors::{BuiltInError, CommandResultTrait, CommandSyntaxError},
parse_results::ParseResults,
result_consumer::{DefaultResultConsumer, ResultConsumer},
string_reader::StringReader,
@@ -26,15 +26,15 @@ use crate::{
/// # struct CommandSource;
/// let mut subject = CommandDispatcher::<CommandSource>::new();
/// ```
-pub struct CommandDispatcher<S>
+pub struct CommandDispatcher<S, R = i32>
where
Self: Sync + Send,
{
- pub root: Arc<RwLock<CommandNode<S>>>,
- consumer: Box<dyn ResultConsumer<S> + Send + Sync>,
+ pub root: Arc<RwLock<CommandNode<S, R>>>,
+ consumer: Box<dyn ResultConsumer<S, R> + Send + Sync>,
}
-impl<S> CommandDispatcher<S> {
+impl<S, R: CommandResultTrait> CommandDispatcher<S, R> {
pub fn new() -> Self {
Self {
root: Arc::new(RwLock::new(CommandNode::default())),
@@ -49,13 +49,13 @@ impl<S> CommandDispatcher<S> {
/// # let mut subject = CommandDispatcher::<()>::new();
/// subject.register(literal("foo").executes(|_| 42));
/// ```
- pub fn register(&mut self, node: ArgumentBuilder<S>) -> Arc<RwLock<CommandNode<S>>> {
+ pub fn register(&mut self, node: ArgumentBuilder<S, R>) -> Arc<RwLock<CommandNode<S, R>>> {
let build = Arc::new(RwLock::new(node.build()));
self.root.write().add_child(&build);
build
}
- pub fn parse(&self, command: StringReader, source: S) -> ParseResults<'_, S> {
+ pub fn parse(&self, command: StringReader, source: S) -> ParseResults<'_, S, R> {
let source = Arc::new(source);
let context = CommandContextBuilder::new(self, source, self.root.clone(), command.cursor());
@@ -64,14 +64,14 @@ impl<S> CommandDispatcher<S> {
fn parse_nodes<'a>(
&'a self,
- node: &Arc<RwLock<CommandNode<S>>>,
+ node: &Arc<RwLock<CommandNode<S, R>>>,
original_reader: &StringReader,
- context_so_far: CommandContextBuilder<'a, S>,
- ) -> Result<ParseResults<'a, S>, CommandSyntaxError> {
+ context_so_far: CommandContextBuilder<'a, S, R>,
+ ) -> Result<ParseResults<'a, S, R>, CommandSyntaxError> {
let source = context_so_far.source.clone();
#[allow(clippy::mutable_key_type)] // this is fine because we don't mutate the key
- let mut errors = HashMap::<Rc<CommandNode<S>>, CommandSyntaxError>::new();
- let mut potentials: Vec<ParseResults<S>> = vec![];
+ let mut errors = HashMap::<Rc<CommandNode<S, R>>, CommandSyntaxError>::new();
+ let mut potentials: Vec<ParseResults<S, R>> = vec![];
let cursor = original_reader.cursor();
for child in node.read().get_relevant_nodes(&mut original_reader.clone()) {
@@ -183,7 +183,7 @@ impl<S> CommandDispatcher<S> {
&self,
input: impl Into<StringReader>,
source: S,
- ) -> Result<i32, CommandSyntaxError> {
+ ) -> Result<R, CommandSyntaxError> {
let input = input.into();
let parse = self.parse(input, source);
@@ -191,9 +191,9 @@ impl<S> CommandDispatcher<S> {
}
pub fn add_paths(
- node: Arc<RwLock<CommandNode<S>>>,
- result: &mut Vec<Vec<Arc<RwLock<CommandNode<S>>>>>,
- parents: Vec<Arc<RwLock<CommandNode<S>>>>,
+ node: Arc<RwLock<CommandNode<S, R>>>,
+ result: &mut Vec<Vec<Arc<RwLock<CommandNode<S, R>>>>>,
+ parents: Vec<Arc<RwLock<CommandNode<S, R>>>>,
) {
let mut current = parents;
current.push(node.clone());
@@ -204,9 +204,9 @@ impl<S> CommandDispatcher<S> {
}
}
- pub fn get_path(&self, target: CommandNode<S>) -> Vec<String> {
+ pub fn get_path(&self, target: CommandNode<S, R>) -> Vec<String> {
let rc_target = Arc::new(RwLock::new(target));
- let mut nodes: Vec<Vec<Arc<RwLock<CommandNode<S>>>>> = Vec::new();
+ let mut nodes: Vec<Vec<Arc<RwLock<CommandNode<S, R>>>>> = Vec::new();
Self::add_paths(self.root.clone(), &mut nodes, vec![]);
for list in nodes {
@@ -223,7 +223,7 @@ impl<S> CommandDispatcher<S> {
vec![]
}
- pub fn find_node(&self, path: &[&str]) -> Option<Arc<RwLock<CommandNode<S>>>> {
+ pub fn find_node(&self, path: &[&str]) -> Option<Arc<RwLock<CommandNode<S, R>>>> {
let mut node = self.root.clone();
for name in path {
match node.clone().read().child(name) {
@@ -239,7 +239,7 @@ impl<S> CommandDispatcher<S> {
}
/// Executes a given pre-parsed command.
- pub fn execute_parsed(&self, parse: ParseResults<S>) -> Result<i32, CommandSyntaxError> {
+ pub fn execute_parsed(&self, parse: ParseResults<S, R>) -> Result<R, CommandSyntaxError> {
if parse.reader.can_read() {
return Err(if parse.exceptions.len() == 1 {
parse.exceptions.values().next().unwrap().clone()
@@ -263,7 +263,7 @@ impl<S> CommandDispatcher<S> {
pub fn get_all_usage(
&self,
- node: &CommandNode<S>,
+ node: &CommandNode<S, R>,
source: &S,
restricted: bool,
) -> Vec<String> {
@@ -274,7 +274,7 @@ impl<S> CommandDispatcher<S> {
fn get_all_usage_recursive(
&self,
- node: &CommandNode<S>,
+ node: &CommandNode<S, R>,
source: &S,
result: &mut Vec<String>,
prefix: &str,
@@ -325,9 +325,9 @@ impl<S> CommandDispatcher<S> {
/// command tree.
pub fn get_smart_usage(
&self,
- node: &CommandNode<S>,
+ node: &CommandNode<S, R>,
source: &S,
- ) -> Vec<(Arc<RwLock<CommandNode<S>>>, String)> {
+ ) -> Vec<(Arc<RwLock<CommandNode<S, R>>>, String)> {
let mut result = Vec::new();
let optional = node.command.is_some();
@@ -343,7 +343,7 @@ impl<S> CommandDispatcher<S> {
fn get_smart_usage_recursive(
&self,
- node: &CommandNode<S>,
+ node: &CommandNode<S, R>,
source: &S,
optional: bool,
deep: bool,
@@ -435,13 +435,13 @@ impl<S> CommandDispatcher<S> {
Some(this)
}
- pub fn get_completion_suggestions(parse: ParseResults<S>) -> Suggestions {
+ pub fn get_completion_suggestions(parse: ParseResults<S, R>) -> Suggestions {
let cursor = parse.reader.total_length();
Self::get_completion_suggestions_with_cursor(parse, cursor)
}
pub fn get_completion_suggestions_with_cursor(
- parse: ParseResults<S>,
+ parse: ParseResults<S, R>,
cursor: usize,
) -> Suggestions {
let context = parse.context;
@@ -471,7 +471,7 @@ impl<S> CommandDispatcher<S> {
}
}
-impl<S> Default for CommandDispatcher<S> {
+impl<S, R: CommandResultTrait> Default for CommandDispatcher<S, R> {
fn default() -> Self {
Self::new()
}
diff --git a/azalea-brigadier/src/context/command_context.rs b/azalea-brigadier/src/context/command_context.rs
index a9959895..0139f7e9 100644
--- a/azalea-brigadier/src/context/command_context.rs
+++ b/azalea-brigadier/src/context/command_context.rs
@@ -15,20 +15,20 @@ use crate::{
};
/// A built `CommandContextBuilder`.
-pub struct CommandContext<S> {
+pub struct CommandContext<S, R = i32> {
pub source: Arc<S>,
pub(super) input: String,
pub(super) arguments: HashMap<String, ParsedArgument>,
- pub(super) command: Command<S>,
- pub(super) root_node: Arc<RwLock<CommandNode<S>>>,
- pub(super) nodes: Vec<ParsedCommandNode<S>>,
+ pub(super) command: Command<S, R>,
+ pub(super) root_node: Arc<RwLock<CommandNode<S, R>>>,
+ pub(super) nodes: Vec<ParsedCommandNode<S, R>>,
pub(super) range: StringRange,
- pub(super) child: Option<Rc<CommandContext<S>>>,
- pub(super) modifier: Option<Arc<RedirectModifier<S>>>,
+ pub(super) child: Option<Rc<CommandContext<S, R>>>,
+ pub(super) modifier: Option<Arc<RedirectModifier<S, R>>>,
pub(super) forks: bool,
}
-impl<S> Clone for CommandContext<S> {
+impl<S, R> Clone for CommandContext<S, R> {
fn clone(&self) -> Self {
Self {
source: self.source.clone(),
@@ -45,7 +45,7 @@ impl<S> Clone for CommandContext<S> {
}
}
-impl<S> Debug for CommandContext<S> {
+impl<S, R> Debug for CommandContext<S, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CommandContext")
// .field("source", &self.source)
@@ -62,7 +62,7 @@ impl<S> Debug for CommandContext<S> {
}
}
-impl<S> CommandContext<S> {
+impl<S, R> CommandContext<S, R> {
pub fn copy_for(&self, source: Arc<S>) -> Self {
if Arc::ptr_eq(&source, &self.source) {
// fast path
@@ -83,11 +83,11 @@ impl<S> CommandContext<S> {
}
}
- pub fn child(&self) -> Option<&CommandContext<S>> {
+ pub fn child(&self) -> Option<&CommandContext<S, R>> {
self.child.as_ref().map(|c| c.as_ref())
}
- pub fn last_child(&self) -> &CommandContext<S> {
+ pub fn last_child(&self) -> &CommandContext<S, R> {
let mut result = self;
while let Some(child) = result.child() {
result = child;
@@ -95,7 +95,7 @@ impl<S> CommandContext<S> {
result
}
- pub fn command(&self) -> &Command<S> {
+ pub fn command(&self) -> &Command<S, R> {
&self.command
}
@@ -104,7 +104,7 @@ impl<S> CommandContext<S> {
argument.map(|a| a.result.as_ref())
}
- pub fn redirect_modifier(&self) -> Option<&RedirectModifier<S>> {
+ pub fn redirect_modifier(&self) -> Option<&RedirectModifier<S, R>> {
self.modifier.as_ref().map(|m| m.as_ref())
}
@@ -116,11 +116,11 @@ impl<S> CommandContext<S> {
&self.input
}
- pub fn root_node(&self) -> &Arc<RwLock<CommandNode<S>>> {
+ pub fn root_node(&self) -> &Arc<RwLock<CommandNode<S, R>>> {
&self.root_node
}
- pub fn nodes(&self) -> &[ParsedCommandNode<S>] {
+ pub fn nodes(&self) -> &[ParsedCommandNode<S, R>] {
&self.nodes
}
diff --git a/azalea-brigadier/src/context/command_context_builder.rs b/azalea-brigadier/src/context/command_context_builder.rs
index a3819246..e3b09fc1 100644
--- a/azalea-brigadier/src/context/command_context_builder.rs
+++ b/azalea-brigadier/src/context/command_context_builder.rs
@@ -17,20 +17,20 @@ use crate::{
tree::{Command, CommandNode},
};
-pub struct CommandContextBuilder<'a, S> {
+pub struct CommandContextBuilder<'a, S, R> {
pub arguments: HashMap<String, ParsedArgument>,
- pub root: Arc<RwLock<CommandNode<S>>>,
- pub nodes: Vec<ParsedCommandNode<S>>,
- pub dispatcher: &'a CommandDispatcher<S>,
+ pub root: Arc<RwLock<CommandNode<S, R>>>,
+ pub nodes: Vec<ParsedCommandNode<S, R>>,
+ pub dispatcher: &'a CommandDispatcher<S, R>,
pub source: Arc<S>,
- pub command: Command<S>,
- pub child: Option<Rc<CommandContextBuilder<'a, S>>>,
+ pub command: Command<S, R>,
+ pub child: Option<Rc<CommandContextBuilder<'a, S, R>>>,
pub range: StringRange,
- pub modifier: Option<Arc<RedirectModifier<S>>>,
+ pub modifier: Option<Arc<RedirectModifier<S, R>>>,
pub forks: bool,
}
-impl<S> Clone for CommandContextBuilder<'_, S> {
+impl<S, R> Clone for CommandContextBuilder<'_, S, R> {
fn clone(&self) -> Self {
Self {
arguments: self.arguments.clone(),
@@ -47,11 +47,11 @@ impl<S> Clone for CommandContextBuilder<'_, S> {
}
}
-impl<'a, S> CommandContextBuilder<'a, S> {
+impl<'a, S, R> CommandContextBuilder<'a, S, R> {
pub fn new(
- dispatcher: &'a CommandDispatcher<S>,
+ dispatcher: &'a CommandDispatcher<S, R>,
source: Arc<S>,
- root_node: Arc<RwLock<CommandNode<S>>>,
+ root_node: Arc<RwLock<CommandNode<S, R>>>,
start: usize,
) -> Self {
Self {
@@ -68,11 +68,11 @@ impl<'a, S> CommandContextBuilder<'a, S> {
}
}
- pub fn with_command(&mut self, command: &Command<S>) -> &Self {
+ pub fn with_command(&mut self, command: &Command<S, R>) -> &Self {
self.command.clone_from(command);
self
}
- pub fn with_child(&mut self, child: Rc<CommandContextBuilder<'a, S>>) -> &Self {
+ pub fn with_child(&mut self, child: Rc<CommandContextBuilder<'a, S, R>>) -> &Self {
self.child = Some(child);
self
}
@@ -80,7 +80,7 @@ impl<'a, S> CommandContextBuilder<'a, S> {
self.arguments.insert(name.to_owned(), argument);
self
}
- pub fn with_node(&mut self, node: Arc<RwLock<CommandNode<S>>>, range: StringRange) -> &Self {
+ pub fn with_node(&mut self, node: Arc<RwLock<CommandNode<S, R>>>, range: StringRange) -> &Self {
self.nodes.push(ParsedCommandNode {
node: node.clone(),
range,
@@ -91,7 +91,7 @@ impl<'a, S> CommandContextBuilder<'a, S> {
self
}
- pub fn build(&self, input: &str) -> CommandContext<S> {
+ pub fn build(&self, input: &str) -> CommandContext<S, R> {
CommandContext {
arguments: self.arguments.clone(),
root_node: self.root.clone(),
@@ -106,7 +106,7 @@ impl<'a, S> CommandContextBuilder<'a, S> {
}
}
- pub fn find_suggestion_context(&self, cursor: usize) -> SuggestionContext<S> {
+ pub fn find_suggestion_context(&self, cursor: usize) -> SuggestionContext<S, R> {
if self.range.start() > cursor {
panic!("Can't find node before cursor");
}
@@ -144,7 +144,7 @@ impl<'a, S> CommandContextBuilder<'a, S> {
}
}
-impl<S> Debug for CommandContextBuilder<'_, S> {
+impl<S, R> Debug for CommandContextBuilder<'_, S, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CommandContextBuilder")
// .field("arguments", &self.arguments)
diff --git a/azalea-brigadier/src/context/context_chain.rs b/azalea-brigadier/src/context/context_chain.rs
index 8ad5b320..ab66fcf9 100644
--- a/azalea-brigadier/src/context/context_chain.rs
+++ b/azalea-brigadier/src/context/context_chain.rs
@@ -1,16 +1,22 @@
use std::{rc::Rc, sync::Arc};
use super::CommandContext;
-use crate::{errors::CommandSyntaxError, result_consumer::ResultConsumer};
-
-pub struct ContextChain<S> {
- modifiers: Vec<Rc<CommandContext<S>>>,
- executable: Rc<CommandContext<S>>,
- next_stage_cache: Option<Rc<ContextChain<S>>>,
+use crate::{
+ errors::{CommandResultTrait, CommandSyntaxError},
+ result_consumer::ResultConsumer,
+};
+
+pub struct ContextChain<S, R> {
+ modifiers: Vec<Rc<CommandContext<S, R>>>,
+ executable: Rc<CommandContext<S, R>>,
+ next_stage_cache: Option<Rc<ContextChain<S, R>>>,
}
-impl<S> ContextChain<S> {
- pub fn new(modifiers: Vec<Rc<CommandContext<S>>>, executable: Rc<CommandContext<S>>) -> Self {
+impl<S, R: CommandResultTrait> ContextChain<S, R> {
+ pub fn new(
+ modifiers: Vec<Rc<CommandContext<S, R>>>,
+ executable: Rc<CommandContext<S, R>>,
+ ) -> Self {
if executable.command.is_none() {
panic!("Last command in chain must be executable");
}
@@ -21,7 +27,7 @@ impl<S> ContextChain<S> {
}
}
- pub fn try_flatten(root_context: Rc<CommandContext<S>>) -> Option<Self> {
+ pub fn try_flatten(root_context: Rc<CommandContext<S, R>>) -> Option<Self> {
let mut modifiers = Vec::new();
let mut current = root_context;
loop {
@@ -39,9 +45,9 @@ impl<S> ContextChain<S> {
}
pub fn run_modifier(
- modifier: Rc<CommandContext<S>>,
+ modifier: Rc<CommandContext<S, R>>,
source: Arc<S>,
- result_consumer: &dyn ResultConsumer<S>,
+ result_consumer: &dyn ResultConsumer<S, R>,
forked_mode: bool,
) -> Result<Vec<Arc<S>>, CommandSyntaxError> {
let source_modifier = modifier.redirect_modifier();
@@ -64,33 +70,43 @@ impl<S> ContextChain<S> {
pub fn run_executable(
&self,
- executable: Rc<CommandContext<S>>,
+ executable: Rc<CommandContext<S, R>>,
source: Arc<S>,
- result_consumer: &dyn ResultConsumer<S>,
+ result_consumer: &dyn ResultConsumer<S, R>,
forked_mode: bool,
- ) -> Result<i32, CommandSyntaxError> {
+ ) -> Result<R, CommandSyntaxError> {
let context_to_use = Rc::new(executable.copy_for(source));
let Some(command) = &executable.command else {
unimplemented!();
};
- let err = match (command)(&context_to_use) {
- Ok(result) => {
- result_consumer.on_command_complete(context_to_use, true, result);
- return if forked_mode { Ok(1) } else { Ok(result) };
+ let res = (command)(&context_to_use);
+ let err = match res {
+ Ok(res) => {
+ let Some(res) = res.as_i32() else {
+ // these are treated as exceptions, so they can bubble up without doing anything
+ // else
+ return Ok(res);
+ };
+ result_consumer.on_command_complete(context_to_use, true, res);
+ return if forked_mode {
+ Ok(R::new(1))
+ } else {
+ Ok(R::new(res))
+ };
}
Err(err) => err,
};
result_consumer.on_command_complete(context_to_use, false, 0);
- if forked_mode { Ok(0) } else { Err(err) }
+ if forked_mode { Ok(R::new(0)) } else { Err(err) }
}
pub fn execute_all(
&self,
source: Arc<S>,
- result_consumer: &dyn ResultConsumer<S>,
- ) -> Result<i32, CommandSyntaxError> {
+ result_consumer: &dyn ResultConsumer<S, R>,
+ ) -> Result<R, CommandSyntaxError> {
if self.modifiers.is_empty() {
return self.run_executable(self.executable.clone(), source, result_consumer, false);
}
@@ -103,30 +119,37 @@ impl<S> ContextChain<S> {
let mut next_sources = Vec::new();
for source_to_run in current_sources {
- next_sources.extend(Self::run_modifier(
+ match Self::run_modifier(
modifier.clone(),
source_to_run.clone(),
result_consumer,
forked_mode,
- )?);
+ ) {
+ Ok(res) => next_sources.extend(res),
+ Err(err) => return Err(err),
+ }
}
if next_sources.is_empty() {
- return Ok(0);
+ return Ok(R::new(0));
}
current_sources = next_sources;
}
- let mut result = 0;
+ let mut summed = 0;
for execution_source in current_sources {
- result += self.run_executable(
+ let res = self.run_executable(
self.executable.clone(),
execution_source,
result_consumer,
forked_mode,
)?;
+ match res.as_i32() {
+ Some(res) => summed += res,
+ None => return Ok(res),
+ }
}
- Ok(result)
+ Ok(R::new(summed))
}
pub fn stage(&self) -> Stage {
@@ -137,14 +160,14 @@ impl<S> ContextChain<S> {
}
}
- pub fn top_context(&self) -> Rc<CommandContext<S>> {
+ pub fn top_context(&self) -> Rc<CommandContext<S, R>> {
self.modifiers
.first()
.cloned()
.unwrap_or_else(|| self.executable.clone())
}
- pub fn next_stage(&mut self) -> Option<Rc<ContextChain<S>>> {
+ pub fn next_stage(&mut self) -> Option<Rc<ContextChain<S, R>>> {
let modifier_count = self.modifiers.len();
if modifier_count == 0 {
return None;
diff --git a/azalea-brigadier/src/context/parsed_command_node.rs b/azalea-brigadier/src/context/parsed_command_node.rs
index 62da13d8..8dbadb49 100644
--- a/azalea-brigadier/src/context/parsed_command_node.rs
+++ b/azalea-brigadier/src/context/parsed_command_node.rs
@@ -6,12 +6,12 @@ use super::string_range::StringRange;
use crate::tree::CommandNode;
#[derive(Debug)]
-pub struct ParsedCommandNode<S> {
- pub node: Arc<RwLock<CommandNode<S>>>,
+pub struct ParsedCommandNode<S, R> {
+ pub node: Arc<RwLock<CommandNode<S, R>>>,
pub range: StringRange,
}
-impl<S> Clone for ParsedCommandNode<S> {
+impl<S, R> Clone for ParsedCommandNode<S, R> {
fn clone(&self) -> Self {
Self {
node: self.node.clone(),
diff --git a/azalea-brigadier/src/context/suggestion_context.rs b/azalea-brigadier/src/context/suggestion_context.rs
index 58a73fdb..ed3dbfa2 100644
--- a/azalea-brigadier/src/context/suggestion_context.rs
+++ b/azalea-brigadier/src/context/suggestion_context.rs
@@ -5,7 +5,7 @@ use parking_lot::RwLock;
use crate::tree::CommandNode;
#[derive(Debug)]
-pub struct SuggestionContext<S> {
- pub parent: Arc<RwLock<CommandNode<S>>>,
+pub struct SuggestionContext<S, R> {
+ pub parent: Arc<RwLock<CommandNode<S, R>>>,
pub start_pos: usize,
}
diff --git a/azalea-brigadier/src/errors/command_syntax_error.rs b/azalea-brigadier/src/errors/command_syntax_error.rs
index fd97b050..a29c95ee 100644
--- a/azalea-brigadier/src/errors/command_syntax_error.rs
+++ b/azalea-brigadier/src/errors/command_syntax_error.rs
@@ -77,8 +77,8 @@ impl CommandSyntaxError {
&self.kind
}
- pub fn input(&self) -> &Option<String> {
- &self.input
+ pub fn input(&self) -> Option<&str> {
+ self.input.as_deref()
}
pub fn cursor(&self) -> Option<usize> {
diff --git a/azalea-brigadier/src/errors/mod.rs b/azalea-brigadier/src/errors/mod.rs
index facebe5e..49d0f956 100644
--- a/azalea-brigadier/src/errors/mod.rs
+++ b/azalea-brigadier/src/errors/mod.rs
@@ -3,3 +3,24 @@ mod command_syntax_error;
pub use builtin_errors::BuiltInError;
pub use command_syntax_error::CommandSyntaxError;
+
+pub trait CommandResultTrait {
+ fn new(i: i32) -> Self;
+ fn as_i32(&self) -> Option<i32>;
+}
+impl CommandResultTrait for i32 {
+ fn new(i: i32) -> Self {
+ i
+ }
+ fn as_i32(&self) -> Option<i32> {
+ Some(*self)
+ }
+}
+impl<E> CommandResultTrait for Result<i32, E> {
+ fn new(i: i32) -> Self {
+ Ok(i)
+ }
+ fn as_i32(&self) -> Option<i32> {
+ self.as_ref().copied().ok()
+ }
+}
diff --git a/azalea-brigadier/src/modifier.rs b/azalea-brigadier/src/modifier.rs
index aba5e95f..90be22db 100644
--- a/azalea-brigadier/src/modifier.rs
+++ b/azalea-brigadier/src/modifier.rs
@@ -2,5 +2,5 @@ use std::sync::Arc;
use crate::{context::CommandContext, errors::CommandSyntaxError};
-pub type RedirectModifier<S> =
- dyn Fn(&CommandContext<S>) -> Result<Vec<Arc<S>>, CommandSyntaxError> + Send + Sync;
+pub type RedirectModifier<S, R> =
+ dyn Fn(&CommandContext<S, R>) -> Result<Vec<Arc<S>>, CommandSyntaxError> + Send + Sync;
diff --git a/azalea-brigadier/src/parse_results.rs b/azalea-brigadier/src/parse_results.rs
index 7d3cf98a..f77b0b6f 100644
--- a/azalea-brigadier/src/parse_results.rs
+++ b/azalea-brigadier/src/parse_results.rs
@@ -9,13 +9,13 @@ use crate::{
tree::CommandNode,
};
-pub struct ParseResults<'a, S> {
- pub context: CommandContextBuilder<'a, S>,
+pub struct ParseResults<'a, S, R> {
+ pub context: CommandContextBuilder<'a, S, R>,
pub reader: StringReader,
- pub exceptions: HashMap<Rc<CommandNode<S>>, CommandSyntaxError>,
+ pub exceptions: HashMap<Rc<CommandNode<S, R>>, CommandSyntaxError>,
}
-impl<S> Debug for ParseResults<'_, S> {
+impl<S, R> Debug for ParseResults<'_, S, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ParseResults")
.field("context", &self.context)
diff --git a/azalea-brigadier/src/result_consumer.rs b/azalea-brigadier/src/result_consumer.rs
index fb9dd135..bd84dab5 100644
--- a/azalea-brigadier/src/result_consumer.rs
+++ b/azalea-brigadier/src/result_consumer.rs
@@ -2,11 +2,17 @@ use std::rc::Rc;
use crate::context::CommandContext;
-pub trait ResultConsumer<S> {
- fn on_command_complete(&self, context: Rc<CommandContext<S>>, success: bool, result: i32);
+pub trait ResultConsumer<S, R> {
+ fn on_command_complete(&self, context: Rc<CommandContext<S, R>>, success: bool, result: i32);
}
pub struct DefaultResultConsumer;
-impl<S> ResultConsumer<S> for DefaultResultConsumer {
- fn on_command_complete(&self, _context: Rc<CommandContext<S>>, _success: bool, _result: i32) {}
+impl<S, R> ResultConsumer<S, R> for DefaultResultConsumer {
+ fn on_command_complete(
+ &self,
+ _context: Rc<CommandContext<S, R>>,
+ _success: bool,
+ _result: i32,
+ ) {
+ }
}
diff --git a/azalea-brigadier/src/suggestion/suggestion_provider.rs b/azalea-brigadier/src/suggestion/suggestion_provider.rs
index 4c3a9f41..058acefb 100644
--- a/azalea-brigadier/src/suggestion/suggestion_provider.rs
+++ b/azalea-brigadier/src/suggestion/suggestion_provider.rs
@@ -1,10 +1,10 @@
use super::{Suggestions, SuggestionsBuilder};
use crate::context::CommandContext;
-pub trait SuggestionProvider<S> {
+pub trait SuggestionProvider<S, R> {
fn get_suggestions(
&self,
- context: CommandContext<S>,
+ context: CommandContext<S, R>,
builder: SuggestionsBuilder,
) -> Suggestions;
}
diff --git a/azalea-brigadier/src/tree/mod.rs b/azalea-brigadier/src/tree/mod.rs
index b9b11fd6..4cbe2dd5 100644
--- a/azalea-brigadier/src/tree/mod.rs
+++ b/azalea-brigadier/src/tree/mod.rs
@@ -20,27 +20,27 @@ use crate::{
suggestion::{Suggestions, SuggestionsBuilder},
};
-pub type Command<S> =
- Option<Arc<dyn Fn(&CommandContext<S>) -> Result<i32, CommandSyntaxError> + Send + Sync>>;
+pub type Command<S, R> =
+ Option<Arc<dyn Fn(&CommandContext<S, R>) -> Result<R, CommandSyntaxError> + Send + Sync>>;
/// An ArgumentBuilder that has been built.
#[non_exhaustive]
-pub struct CommandNode<S> {
- pub value: ArgumentBuilderType<S>,
+pub struct CommandNode<S, R = i32> {
+ pub value: ArgumentBuilderType<S, R>,
// this is a BTreeMap because children need to be ordered when getting command suggestions
- pub children: BTreeMap<String, Arc<RwLock<CommandNode<S>>>>,
- pub literals: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
- pub arguments: HashMap<String, Arc<RwLock<CommandNode<S>>>>,
+ pub children: BTreeMap<String, Arc<RwLock<CommandNode<S, R>>>>,
+ pub literals: HashMap<String, Arc<RwLock<CommandNode<S, R>>>>,
+ pub arguments: HashMap<String, Arc<RwLock<CommandNode<S, R>>>>,
- pub command: Command<S>,
+ pub command: Command<S, R>,
pub requirement: Arc<dyn Fn(&S) -> bool + Send + Sync>,
- pub redirect: Option<Arc<RwLock<CommandNode<S>>>>,
+ pub redirect: Option<Arc<RwLock<CommandNode<S, R>>>>,
pub forks: bool,
- pub modifier: Option<Arc<RedirectModifier<S>>>,
+ pub modifier: Option<Arc<RedirectModifier<S, R>>>,
}
-impl<S> Clone for CommandNode<S> {
+impl<S, R> Clone for CommandNode<S, R> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
@@ -56,7 +56,7 @@ impl<S> Clone for CommandNode<S> {
}
}
-impl<S> CommandNode<S> {
+impl<S, R> CommandNode<S, R> {
/// Returns the value as a literal from this command node, assuming it's
/// already been checked.
///
@@ -77,14 +77,17 @@ impl<S> CommandNode<S> {
///
/// Will panic if this node is not an argument. Consider using a match
/// statement instead.
- pub fn argument(&self) -> &Argument<S> {
+ pub fn argument(&self) -> &Argument<S, R> {
match self.value {
ArgumentBuilderType::Argument(ref argument) => argument,
_ => panic!("CommandNode::argument() called on non-argument node"),
}
}
- pub fn get_relevant_nodes(&self, input: &mut StringReader) -> Vec<Arc<RwLock<CommandNode<S>>>> {
+ pub fn get_relevant_nodes(
+ &self,
+ input: &mut StringReader,
+ ) -> Vec<Arc<RwLock<CommandNode<S, R>>>> {
let literals = &self.literals;
if literals.is_empty() {
@@ -114,7 +117,7 @@ impl<S> CommandNode<S> {
(self.requirement)(source)
}
- pub fn add_child(&mut self, node: &Arc<RwLock<CommandNode<S>>>) {
+ pub fn add_child(&mut self, node: &Arc<RwLock<CommandNode<S, R>>>) {
let child = self.children.get(node.read().name());
if let Some(child) = child {
// We've found something to merge onto
@@ -152,14 +155,14 @@ impl<S> CommandNode<S> {
}
}
- pub fn child(&self, name: &str) -> Option<Arc<RwLock<CommandNode<S>>>> {
+ pub fn child(&self, name: &str) -> Option<Arc<RwLock<CommandNode<S, R>>>> {
self.children.get(name).cloned()
}
pub fn parse_with_context(
&self,
reader: &mut StringReader,
- context_builder: &mut CommandContextBuilder<S>,
+ context_builder: &mut CommandContextBuilder<S, R>,
) -> Result<(), CommandSyntaxError> {
match self.value {
ArgumentBuilderType::Argument(ref argument) => {
@@ -225,7 +228,7 @@ impl<S> CommandNode<S> {
pub fn list_suggestions(
&self,
- context: CommandContext<S>,
+ context: CommandContext<S, R>,
builder: SuggestionsBuilder,
) -> Suggestions {
match &self.value {
@@ -245,7 +248,7 @@ impl<S> CommandNode<S> {
}
}
-impl<S> Debug for CommandNode<S> {
+impl<S, R> Debug for CommandNode<S, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CommandNode")
// .field("value", &self.value)
@@ -259,7 +262,7 @@ impl<S> Debug for CommandNode<S> {
}
}
-impl<S> Default for CommandNode<S> {
+impl<S, R> Default for CommandNode<S, R> {
fn default() -> Self {
Self {
value: ArgumentBuilderType::Literal(Literal::default()),
@@ -277,7 +280,7 @@ impl<S> Default for CommandNode<S> {
}
}
-impl<S> Hash for CommandNode<S> {
+impl<S, R> Hash for CommandNode<S, R> {
fn hash<H: Hasher>(&self, state: &mut H) {
// hash the children
for (k, v) in &self.children {
@@ -289,7 +292,7 @@ impl<S> Hash for CommandNode<S> {
}
}
-impl<S> PartialEq for CommandNode<S> {
+impl<S, R> PartialEq for CommandNode<S, R> {
fn eq(&self, other: &Self) -> bool {
if self.children.len() != other.children.len() {
return false;
@@ -324,4 +327,4 @@ impl<S> PartialEq for CommandNode<S> {
true
}
}
-impl<S> Eq for CommandNode<S> {}
+impl<S, R> Eq for CommandNode<S, R> {}
diff --git a/azalea-brigadier/tests/bevy_app_usage.rs b/azalea-brigadier/tests/bevy_app_usage.rs
index e0bd8d23..af2cea3b 100644
--- a/azalea-brigadier/tests/bevy_app_usage.rs
+++ b/azalea-brigadier/tests/bevy_app_usage.rs
@@ -86,7 +86,7 @@ impl DispatchStorage {
let result = storage.dispatch.execute("spawn_entity", source.clone());
// Ensure the command was successful
- assert_eq!(result, Ok(0));
+ assert_eq!(result.unwrap(), 0);
// Query the World for the spawned entity
let mut world = source.write();
@@ -106,7 +106,7 @@ impl DispatchStorage {
.execute("spawn_entity_num 3", source.clone());
// Ensure the command was successful
- assert_eq!(result, Ok(0));
+ assert_eq!(result.unwrap(), 0);
// Query the World for spawned entities
let mut world = source.write();
diff --git a/azalea-brigadier/tests/command_dispatcher_test.rs b/azalea-brigadier/tests/command_dispatcher_test.rs
index d6a3f2cc..f2766ced 100644
--- a/azalea-brigadier/tests/command_dispatcher_test.rs
+++ b/azalea-brigadier/tests/command_dispatcher_test.rs
@@ -43,7 +43,7 @@ fn create_and_merge_commands() {
#[test]
fn execute_unknown_command() {
- let mut subject = CommandDispatcher::new();
+ let mut subject = CommandDispatcher::<_, i32>::new();
subject.register(literal("bar"));
subject.register(literal("baz"));
@@ -56,7 +56,7 @@ fn execute_unknown_command() {
#[test]
fn execute_impermissible_command() {
- let mut subject = CommandDispatcher::new();
+ let mut subject = CommandDispatcher::<_, i32>::new();
subject.register(literal("foo").requires(|_| false));
let execute_result = subject.execute("foo", &CommandSource {});
@@ -68,7 +68,7 @@ fn execute_impermissible_command() {
#[test]
fn execute_empty_command() {
- let mut subject = CommandDispatcher::new();
+ let mut subject = CommandDispatcher::<_, i32>::new();
subject.register(literal(""));
let execute_result = subject.execute("", &CommandSource {});
diff --git a/azalea/examples/steal.rs b/azalea/examples/steal.rs
index d5c0b913..44d2eb57 100644
--- a/azalea/examples/steal.rs
+++ b/azalea/examples/steal.rs
@@ -53,9 +53,9 @@ async fn steal(bot: Client, state: State) -> eyre::Result<()> {
loop {
let chest_block = bot
- .world()
+ .world()?
.read()
- .find_blocks(bot.position(), &BlockKind::Chest.into())
+ .find_blocks(bot.position()?, &BlockKind::Chest.into())
.find(
// find the closest chest that hasn't been checked
|block_pos| !state.checked_chests.lock().contains(block_pos),
@@ -68,7 +68,7 @@ async fn steal(bot: Client, state: State) -> eyre::Result<()> {
bot.goto(RadiusGoal::new(chest_block.center(), 3.)).await;
- let Some(chest) = bot.open_container_at(chest_block).await else {
+ let Some(chest) = bot.open_container_at(chest_block).await? else {
println!("Couldn't open chest at {chest_block:?}");
continue;
};
diff --git a/azalea/examples/testbot/commands.rs b/azalea/examples/testbot/commands.rs
index 03705617..599c693d 100644
--- a/azalea/examples/testbot/commands.rs
+++ b/azalea/examples/testbot/commands.rs
@@ -11,7 +11,8 @@ use parking_lot::Mutex;
use crate::State;
-pub type Ctx = CommandContext<Mutex<CommandSource>>;
+pub type Ctx = CommandContext<Mutex<CommandSource>, eyre::Result<i32>>;
+pub type Dispatcher = CommandDispatcher<Mutex<CommandSource>, eyre::Result<i32>>;
pub struct CommandSource {
pub bot: Client,
@@ -42,7 +43,7 @@ impl CommandSource {
}
}
-pub fn register_commands(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register_commands(commands: &mut Dispatcher) {
combat::register(commands);
debug::register(commands);
movement::register(commands);
diff --git a/azalea/examples/testbot/commands/combat.rs b/azalea/examples/testbot/commands/combat.rs
index af147e96..617ac1bd 100644
--- a/azalea/examples/testbot/commands/combat.rs
+++ b/azalea/examples/testbot/commands/combat.rs
@@ -1,22 +1,21 @@
use azalea::brigadier::prelude::*;
-use parking_lot::Mutex;
-use super::{CommandSource, Ctx};
-use crate::State;
+use super::Ctx;
+use crate::{State, commands::Dispatcher};
-pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register(commands: &mut Dispatcher) {
commands.register(
literal("killaura").then(argument("enabled", bool()).executes(|ctx: &Ctx| {
let enabled = get_bool(ctx, "enabled").unwrap();
let source = ctx.source.lock();
let bot = source.bot.clone();
- bot.query_self::<&mut State, _>(|mut state| state.killaura = enabled);
+ bot.query_self::<&mut State, _>(|mut state| state.killaura = enabled)?;
source.reply(if enabled {
"Enabled killaura"
} else {
"Disabled killaura"
});
- 1
+ Ok(1)
})),
);
}
diff --git a/azalea/examples/testbot/commands/debug.rs b/azalea/examples/testbot/commands/debug.rs
index 55b41403..50821a8b 100644
--- a/azalea/examples/testbot/commands/debug.rs
+++ b/azalea/examples/testbot/commands/debug.rs
@@ -14,72 +14,72 @@ use azalea::{
},
};
use azalea_core::hit_result::HitResult;
-use azalea_entity::{EntityKindComponent, metadata};
+use azalea_entity::metadata;
use azalea_inventory::{Menu, components::MaxStackSize};
use azalea_world::{Worlds, chunk::storage::WeakChunkStorage};
use bevy_app::AppExit;
use bevy_ecs::{message::Messages, query::With, world::EntityRef};
-use parking_lot::Mutex;
-use super::{CommandSource, Ctx};
+use super::Ctx;
+use crate::commands::Dispatcher;
-pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register(commands: &mut Dispatcher) {
commands.register(literal("ping").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.reply("pong!");
- 1
+ Ok(1)
}));
commands.register(
literal("say").then(argument("message", greedy_string()).executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let message = get_string(ctx, "message").unwrap();
source.bot.chat(message);
- 1
+ Ok(1)
})),
);
commands.register(literal("disconnect").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.bot.disconnect();
- 1
+ Ok(1)
}));
commands.register(literal("whereami").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("You aren't in render distance!");
- return 0;
+ return Ok(0);
};
- let position = entity.position();
+ let position = entity.position()?;
source.reply(format!(
"You are at {}, {}, {}",
position.x, position.y, position.z
));
- 1
+ Ok(1)
}));
commands.register(literal("entityid").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("You aren't in render distance!");
- return 0;
+ return Ok(0);
};
- let entity_id = entity.minecraft_id();
+ let entity_id = entity.minecraft_id()?;
source.reply(format!(
"Your Minecraft ID is {} and your ECS ID is {entity:?}",
*entity_id
));
- 1
+ Ok(1)
}));
let whereareyou = |ctx: &Ctx| {
let source = ctx.source.lock();
- let position = source.bot.position();
+ let position = source.bot.position()?;
source.reply(format!(
"I'm at {}, {}, {}",
position.x, position.y, position.z
));
- 1
+ Ok(1)
};
commands.register(literal("whereareyou").executes(whereareyou));
commands.register(literal("pos").executes(whereareyou));
@@ -92,45 +92,45 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
source.bot.uuid(),
source.bot.entity
));
- 1
+ Ok(1)
}));
commands.register(literal("getdirection").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let direction = source.bot.direction();
+ let direction = source.bot.direction()?;
source.reply(format!(
"I'm looking at {}, {}",
direction.y_rot(),
direction.x_rot()
));
- 1
+ Ok(1)
}));
commands.register(literal("health").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let health = source.bot.health();
+ let health = source.bot.health()?;
source.reply(format!("I have {health} health"));
- 1
+ Ok(1)
}));
commands.register(literal("lookingat").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let hit_result = source.bot.hit_result();
+ let hit_result = source.bot.hit_result()?;
match &hit_result {
HitResult::Block(r) => {
if r.miss {
source.reply("I'm not looking at anything");
- return 0;
+ return Ok(0);
}
let block_pos = r.block_pos;
- let block = source.bot.world().read().get_block_state(block_pos);
+ let block = source.bot.world()?.read().get_block_state(block_pos);
source.reply(format!("I'm looking at {block:?} at {block_pos:?}"));
}
HitResult::Entity(r) => {
- let entity_kind = **source.bot.entity_component::<EntityKindComponent>(r.entity);
+ let entity_kind = source.bot.entity_ref_for(r.entity).kind()?;
source.reply(format!(
"I'm looking at {entity_kind} ({:?}) at {}",
r.entity, r.location
@@ -138,7 +138,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
}
}
- 1
+ Ok(1)
}));
commands.register(literal("getblock").then(argument("x", integer()).then(
@@ -149,9 +149,9 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let z = get_integer(ctx, "z").unwrap();
println!("getblock xyz {x} {y} {z}");
let block_pos = BlockPos::new(x, y, z);
- let block = source.bot.world().read().get_block_state(block_pos);
+ let block = source.bot.world()?.read().get_block_state(block_pos);
source.reply(format!("BlockKind at {block_pos} is {block:?}"));
- 1
+ Ok(1)
})),
)));
commands.register(literal("getfluid").then(argument("x", integer()).then(
@@ -162,15 +162,15 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let z = get_integer(ctx, "z").unwrap();
println!("getfluid xyz {x} {y} {z}");
let block_pos = BlockPos::new(x, y, z);
- let block = source.bot.world().read().get_fluid_state(block_pos);
+ let block = source.bot.world()?.read().get_fluid_state(block_pos);
source.reply(format!("Fluid at {block_pos} is {block:?}"));
- 1
+ Ok(1)
})),
)));
commands.register(literal("inventory").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- for item in source.bot.menu().slots() {
+ for item in source.bot.menu()?.slots() {
if item.is_empty() {
continue;
}
@@ -181,25 +181,25 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
}
}
}
- 1
+ Ok(1)
}));
commands.register(literal("pathfinderstate").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- let pathfinder = source.bot.get_component::<Pathfinder>();
- let Some(pathfinder) = pathfinder else {
+ let pathfinder = source.bot.component::<Pathfinder>();
+ let Ok(pathfinder) = pathfinder else {
source.reply("I don't have the Pathfinder component");
- return 1;
+ return Ok(1);
};
source.reply(format!(
"pathfinder.is_calculating: {}",
pathfinder.is_calculating
));
- let executing_path = source.bot.get_component::<ExecutingPath>();
- let Some(executing_path) = executing_path else {
+ let executing_path = source.bot.component::<ExecutingPath>();
+ let Ok(executing_path) = executing_path else {
source.reply("I'm not executing a path");
- return 1;
+ return Ok(1);
};
source.reply(format!(
"is_path_partial: {}, path.len: {}, queued_path.len: {}",
@@ -211,20 +211,20 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
"n/a".to_owned()
},
));
- 1
+ Ok(1)
}));
commands.register(literal("pathfindermoves").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("You aren't in render distance!");
- return 0;
+ return Ok(0);
};
- let position = entity.position();
+ let position = entity.position()?;
let position = BlockPos::from(position);
let mut edges = Vec::new();
- let cached_world = CachedWorld::new(source.bot.world(), position);
+ let cached_world = CachedWorld::new(source.bot.world()?, position);
let mining_cache = MiningCache::new(Some(Menu::Player(inventory::Player::default())));
let custom_state = CustomPathfinderStateRef::default();
@@ -247,41 +247,41 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
}
}
- 1
+ Ok(1)
}));
commands.register(literal("startuseitem").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.bot.start_use_item();
source.reply("Ok!");
- 1
+ Ok(1)
}));
commands.register(literal("maxstacksize").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let max_stack_size = source
.bot
- .get_held_item()
+ .get_held_item()?
.get_component::<MaxStackSize>()
.map_or(-1, |s| s.count);
source.reply(format!("{max_stack_size}"));
- 1
+ Ok(1)
}));
commands.register(literal("dimensions").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let bot_dimensions = source.bot.dimensions();
source.reply(format!("{bot_dimensions:?}"));
- 1
+ Ok(1)
}));
commands.register(literal("players").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let player_entities = source
.bot
- .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true);
- let tab_list = source.bot.tab_list();
+ .nearest_entities_by::<(), With<metadata::Player>>(|_: ()| true)?;
+ let tab_list = source.bot.tab_list()?;
for player_entity in player_entities {
- let uuid = player_entity.uuid();
+ let uuid = player_entity.uuid()?;
source.reply(format!(
"{} - {} ({:?})",
player_entity.id(),
@@ -289,7 +289,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
uuid
));
}
- 1
+ Ok(1)
}));
commands.register(literal("enchants").executes(|ctx: &Ctx| {
@@ -297,15 +297,15 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
source.bot.with_registry_holder(|r| {
let enchants = &r.enchantment;
println!("enchants: {enchants:?}");
- });
- 1
+ })?;
+ Ok(1)
}));
commands.register(literal("attributes").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
let attributes = source.bot.attributes();
println!("attributes: {attributes:?}");
- 1
+ Ok(1)
}));
commands.register(literal("debugecsleak").executes(|ctx: &Ctx| {
@@ -406,7 +406,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("\x1b[1mWrote report to {}\x1b[m", report_path.display());
});
- 1
+ Ok(1)
}));
commands.register(literal("exit").executes(|ctx: &Ctx| {
@@ -427,6 +427,6 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
.write_message(AppExit::Success);
});
- 1
+ Ok(1)
}));
}
diff --git a/azalea/examples/testbot/commands/movement.rs b/azalea/examples/testbot/commands/movement.rs
index 500e17b0..df3b7418 100644
--- a/azalea/examples/testbot/commands/movement.rs
+++ b/azalea/examples/testbot/commands/movement.rs
@@ -6,11 +6,11 @@ use azalea::{
pathfinder::goals::{BlockPosGoal, RadiusGoal, XZGoal},
prelude::*,
};
-use parking_lot::Mutex;
-use super::{CommandSource, Ctx};
+use super::Ctx;
+use crate::commands::Dispatcher;
-pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
+pub fn register(commands: &mut Dispatcher) {
commands.register(
literal("goto")
.executes(|ctx: &Ctx| {
@@ -19,14 +19,14 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
// look for the sender
let Some(entity) = source.entity() else {
source.reply("I can't see you!");
- return 0;
+ return Ok(0);
};
- let position = entity.position();
+ let position = entity.position()?;
source.reply("ok");
source
.bot
.start_goto(BlockPosGoal(BlockPos::from(position.up(0.5))));
- 1
+ Ok(1)
})
.then(literal("xz").then(argument("x", integer()).then(
argument("z", integer()).executes(|ctx: &Ctx| {
@@ -36,7 +36,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("goto xz {x} {z}");
source.reply("ok");
source.bot.start_goto(XZGoal { x, z });
- 1
+ Ok(1)
}),
)))
.then(literal("radius").then(argument("radius", float()).then(
@@ -53,7 +53,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
pos: BlockPos::new(x, y, z).center(),
radius,
});
- 1
+ Ok(1)
}),
)),
)))
@@ -66,7 +66,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("goto xyz {x} {y} {z}");
source.reply("ok");
source.bot.start_goto(BlockPosGoal(BlockPos::new(x, y, z)));
- 1
+ Ok(1)
}),
))),
);
@@ -77,23 +77,23 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
// look for the sender
let Some(entity) = source.entity() else {
source.reply("I can't see you!");
- return 0;
+ return Ok(0);
};
source.reply("ok");
*source.state.following_entity.lock() = Some(entity);
- 1
+ Ok(1)
}));
commands.register(literal("down").executes(|ctx: &Ctx| {
let source = ctx.source.clone();
+ let bot = source.lock().bot.clone();
+ let position = BlockPos::from(bot.position()?);
tokio::spawn(async move {
- let bot = source.lock().bot.clone();
- let position = BlockPos::from(bot.position());
source.lock().reply("mining...");
bot.mine(position.down(1)).await;
source.lock().reply("done");
});
- 1
+ Ok(1)
}));
commands.register(
@@ -103,11 +103,11 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let source = ctx.source.lock();
let Some(entity) = source.entity() else {
source.reply("I can't see you!");
- return 0;
+ return Ok(0);
};
- let eye_position = entity.eye_position();
+ let eye_position = entity.eye_position()?;
source.bot.look_at(eye_position);
- 1
+ Ok(1)
})
.then(argument("x", integer()).then(argument("y", integer()).then(
argument("z", integer()).executes(|ctx: &Ctx| {
@@ -119,7 +119,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
println!("{pos:?}");
let source = ctx.source.lock();
source.bot.look_at(pos.center());
- 1
+ Ok(1)
}),
))),
);
@@ -142,7 +142,7 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
bot.walk(WalkDirection::None);
});
source.reply(format!("ok, walking for {seconds} seconds"));
- 1
+ Ok(1)
})),
);
commands.register(
@@ -156,33 +156,33 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
bot.walk(WalkDirection::None);
});
source.reply(format!("ok, sprinting for {seconds} seconds"));
- 1
+ Ok(1)
})),
);
commands.register(literal("north").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(180., 0.);
+ source.bot.set_direction(180., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(literal("south").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(0., 0.);
+ source.bot.set_direction(0., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(literal("east").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(-90., 0.);
+ source.bot.set_direction(-90., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(literal("west").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_direction(90., 0.);
+ source.bot.set_direction(90., 0.)?;
source.reply("ok");
- 1
+ Ok(1)
}));
commands.register(
literal("jump")
@@ -190,27 +190,27 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
let source = ctx.source.lock();
source.bot.jump();
source.reply("ok");
- 1
+ Ok(1)
})
.then(argument("enabled", bool()).executes(|ctx: &Ctx| {
let jumping = get_bool(ctx, "enabled").unwrap();
let source = ctx.source.lock();
- source.bot.set_jumping(jumping);
- 1
+ source.bot.set_jumping(jumping)?;
+ Ok(1)
})),
);
let sneak = |ctx: &Ctx| {
let source = ctx.source.lock();
- source.bot.set_crouching(!source.bot.crouching());
+ source.bot.set_crouching(!source.bot.crouching())?;
source.reply("ok");
- 1
+ Ok(1)
};
let sneak_enabled = argument("enabled", bool()).executes(|ctx: &Ctx| {
let sneaking = get_bool(ctx, "enabled").unwrap();
let source = ctx.source.lock();
- source.bot.set_crouching(sneaking);
- 1
+ source.bot.set_crouching(sneaking)?;
+ Ok(1)
});
commands.register(literal("sneak").executes(sneak).then(sneak_enabled.clone()));
commands.register(literal("crouch").executes(sneak).then(sneak_enabled));
@@ -220,13 +220,13 @@ pub fn register(commands: &mut CommandDispatcher<Mutex<CommandSource>>) {
source.bot.stop_pathfinding();
source.reply("ok");
*source.state.following_entity.lock() = None;
- 1
+ Ok(1)
}));
commands.register(literal("forcestop").executes(|ctx: &Ctx| {
let source = ctx.source.lock();
source.bot.force_stop_pathfinding();
source.reply("ok");
*source.state.following_entity.lock() = None;
- 1
+ Ok(1)
}));
}
diff --git a/azalea/examples/testbot/main.rs b/azalea/examples/testbot/main.rs
index 806780b7..7778463c 100644
--- a/azalea/examples/testbot/main.rs
+++ b/azalea/examples/testbot/main.rs
@@ -126,7 +126,7 @@ impl State {
#[derive(Clone, Default, Resource)]
struct SwarmState {
pub args: Arc<Args>,
- pub commands: Arc<CommandDispatcher<Mutex<CommandSource>>>,
+ pub commands: Arc<commands::Dispatcher>,
}
async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result<()> {
@@ -137,7 +137,7 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result
bot.set_client_information(ClientInformation {
view_distance: 32,
..Default::default()
- });
+ })?;
if swarm.args.pathfinder_debug_particles {
bot.ecs
.write()
@@ -169,7 +169,16 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result
state: state.clone(),
}),
) {
- Ok(_) => {}
+ Ok(Ok(_)) => {}
+ Ok(Err(err)) => {
+ eprintln!("azalea error: {err:?}");
+ let command_source = CommandSource {
+ bot,
+ chat: chat.clone(),
+ state: state.clone(),
+ };
+ command_source.reply(format!("azalea error: {err:?}"));
+ }
Err(err) => {
eprintln!("{err:?}");
let command_source = CommandSource {
@@ -200,7 +209,7 @@ async fn handle(bot: Client, event: azalea::Event, state: State) -> eyre::Result
.max_timeout(Duration::from_secs(1)),
);
} else {
- following.look_at();
+ following.look_at()?;
}
}
}
diff --git a/azalea/src/_docs/performance.md b/azalea/src/_docs/performance.md
index bf580f73..d7d2f596 100644
--- a/azalea/src/_docs/performance.md
+++ b/azalea/src/_docs/performance.md
@@ -91,14 +91,15 @@ If the client doesn't need a high view distance, then you can reduce the number
This can be done by having something like the following in your handler function:
```rust,no_run
# use azalea::ClientInformation;
-# async fn handle(bot: azalea::Client, event: azalea::Event, state: azalea::NoState) {
+# async fn handle(bot: azalea::Client, event: azalea::Event, state: azalea::NoState) -> azalea::error::AzaleaResult<()> {
# match event {
azalea::Event::Init => bot.set_client_information(ClientInformation {
view_distance: 2,
..Default::default()
-}),
+})?,
# _ => {}
# }
+# Ok(())
# }
```
diff --git a/azalea/src/client_impl/mod.rs b/azalea/src/client_impl/mod.rs
index ba78fee4..e0718375 100644
--- a/azalea/src/client_impl/mod.rs
+++ b/azalea/src/client_impl/mod.rs
@@ -355,9 +355,10 @@ impl Client {
///
/// ```
/// # use azalea_core::position::ChunkPos;
- /// # fn example(client: &azalea::Client) {
- /// let world = client.partial_world();
+ /// # fn example(client: &azalea::Client) -> azalea::error::AzaleaResult<()> {
+ /// let world = client.partial_world()?;
/// let is_0_0_loaded = world.read().chunks.limited_get(&ChunkPos::new(0, 0)).is_some();
+ /// # Ok(())
/// # }
pub fn partial_world(&self) -> AzaleaResult<Arc<RwLock<PartialWorld>>> {
let world_holder = self.component::<WorldHolder>()?;
diff --git a/azalea/src/container.rs b/azalea/src/container.rs
index 0a5c8c01..1a8b1721 100644
--- a/azalea/src/container.rs
+++ b/azalea/src/container.rs
@@ -37,14 +37,14 @@ impl Client {
///
/// ```
/// # use azalea::{prelude::*, registry::builtin::BlockKind};
- /// # async fn example(mut bot: azalea::Client) -> AzaleaResult<()> {
+ /// # async fn example(mut bot: azalea::Client) -> azalea::error::AzaleaResult<()> {
/// let target_pos = bot
- /// .world()
+ /// .world()?
/// .read()
- /// .find_block(bot.position(), &BlockKind::Chest.into());
+ /// .find_block(bot.position()?, &BlockKind::Chest.into());
/// let Some(target_pos) = target_pos else {
/// bot.chat("no chest found");
- /// return;
+ /// return Ok(());
/// };
/// let container = bot.open_container_at(target_pos).await?;
/// # Ok(())
@@ -256,11 +256,12 @@ impl ContainerHandleRef {
///
/// ```no_run
/// # use azalea::prelude::*;
- /// # fn example(bot: &Client) {
- /// let inventory = bot.get_inventory();
+ /// # fn example(bot: &Client) -> azalea::error::AzaleaResult<()> {
+ /// let inventory = bot.get_inventory()?;
/// let inventory_title = inventory.title().unwrap_or_default().to_string();
/// // would be true if an unnamed chest is open
/// assert_eq!(inventory_title, "Chest");
+ /// # Ok(())
/// # }
/// ```
pub fn title(&self) -> Option<FormattedText> {
diff --git a/azalea/src/lib.rs b/azalea/src/lib.rs
index 3a2bc0cf..8c29e2c3 100644
--- a/azalea/src/lib.rs
+++ b/azalea/src/lib.rs
@@ -66,11 +66,16 @@ use futures::future::BoxFuture;
pub use join_opts::JoinOpts;
pub use crate::{
- client_impl::{Client, StartClientOpts},
+ client_impl::{Client, StartClientOpts, error},
entity_ref::EntityRef,
events::Event,
};
+// for convenience, adds the alias `azalea::Result` instead of
+// `azalea::error::AzaleaResult`. the user should probably be using anyhow/eyre,
+// but in some cases they may prefer to have the errors more strictly defined.
+pub type Result<T> = error::AzaleaResult<T>;
+
pub type BoxHandleFn<S, R> = Box<dyn Fn(Client, Event, S) -> BoxFuture<'static, R> + Send>;
pub type HandleFn<S, Fut> = fn(Client, Event, S) -> Fut;