Skip to content

Commit

Permalink
big refactor - some optimisations - also a special type for modifiers…
Browse files Browse the repository at this point in the history
…. new gamemode command.
  • Loading branch information
JackCrumpLeys committed Sep 5, 2023
1 parent a7a5f04 commit 6cbeb5a
Show file tree
Hide file tree
Showing 9 changed files with 521 additions and 216 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ num = "0.4.0"
num-bigint = "0.4.3"
num-integer = "0.1.45"
owo-colors = "3.5.0"
ordered-float = "3.7.0"
parking_lot = "0.12.1"
paste = "1.0.11"
petgraph = "0.6.3"
Expand Down
9 changes: 5 additions & 4 deletions crates/valence_command/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ edition.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
valence_server.workspace = true
anyhow.workspace = true
bevy_app.workspace = true
bevy_ecs.workspace = true
tracing.workspace = true
anyhow.workspace = true
byteorder.workspace = true
ordered-float.workspace = true
petgraph.workspace = true
thiserror.workspace = true
serde-value.workspace = true
thiserror.workspace = true
tracing.workspace = true
valence_server.workspace = true
10 changes: 5 additions & 5 deletions crates/valence_command/src/command_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ use valence_server::protocol::{VarInt};

use crate::arg_parser::{CommandArg, ParseInput};
use crate::{CommandRegistry};
use crate::modifier_value::ModifierValue;

/// This struct is used to store the command graph.(see module level docs for
/// more info)
Expand Down Expand Up @@ -338,7 +339,7 @@ pub struct CommandGraphBuilder<'a, T> {
current_node: NodeIndex,
executables: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> T>,
parsers: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> bool>,
modifiers: &'a mut HashMap<NodeIndex, fn(String, &mut HashMap<&str, String>)>,
modifiers: &'a mut HashMap<NodeIndex, fn(String, &mut HashMap<ModifierValue, ModifierValue>)>,
}

impl<'a, T> CommandGraphBuilder<'a, T> {
Expand All @@ -351,7 +352,7 @@ impl<'a, T> CommandGraphBuilder<'a, T> {
registry: &'a mut CommandRegistry,

Check warning on line 352 in crates/valence_command/src/command_graph.rs

View workflow job for this annotation

GitHub Actions / valence-fmt

Diff in /home/runner/work/valence/valence/crates/valence_command/src/command_graph.rs
executables: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> T>,
parsers: &'a mut HashMap<NodeIndex, fn(&mut ParseInput) -> bool>,
modifiers: &'a mut HashMap<NodeIndex, fn(String, &mut HashMap<&str, String>)>,
modifiers: &'a mut HashMap<NodeIndex, fn(String, &mut HashMap<ModifierValue, ModifierValue>)>,
) -> Self {
CommandGraphBuilder {
current_node: registry.graph.root,
Expand Down Expand Up @@ -506,13 +507,12 @@ impl<'a, T> CommandGraphBuilder<'a, T> {
/// .root() // transition to the root node
/// .literal("test") // add a literal node then transition to it ///
/// .with_modifier(|_, modifiers| {
/// modifiers.insert("test", "test".into()); // this will trigger when the node is passed
/// modifiers.insert("test".into(), "test".into()); // this will trigger when the node is passed
/// })
/// .literal("command") // add a literal node then transition to it

Check warning on line 512 in crates/valence_command/src/command_graph.rs

View workflow job for this annotation

GitHub Actions / valence-fmt

Diff in /home/runner/work/valence/valence/crates/valence_command/src/command_graph.rs
/// .with_executable(|_| TestCommand);
/// ```
pub fn with_modifier(&mut self, modifier: fn(String, &mut HashMap<&str, String>)) -> &mut Self {
let graph = &mut self.graph.graph;
pub fn with_modifier(&mut self, modifier: fn(String, &mut HashMap<ModifierValue, ModifierValue>)) -> &mut Self {
let current_node = &mut self.current_node;

self.modifiers.insert(*current_node, modifier);
Expand Down
201 changes: 14 additions & 187 deletions crates/valence_command/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use tracing::trace;
use crate::arg_parser::ParseInput;
use crate::command_graph::{CommandEdgeType, CommandGraphBuilder, CommandNode, NodeData};

Check failure on line 17 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-docs

unused imports: `CommandEdgeType`, `CommandNode`, `NodeData`

Check failure on line 17 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-clippy

unused imports: `CommandEdgeType`, `CommandNode`, `NodeData`

Check failure on line 17 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-tests (ubuntu-latest, default, stable)

unused imports: `CommandEdgeType`, `CommandNode`, `NodeData`

Check failure on line 17 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / Check for unused dependencies

unused imports: `CommandEdgeType`, `CommandNode`, `NodeData`
use crate::command_scopes::CommandScopes;

Check failure on line 18 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-docs

unused import: `crate::command_scopes::CommandScopes`

Check failure on line 18 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-clippy

unused import: `crate::command_scopes::CommandScopes`

Check failure on line 18 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-tests (ubuntu-latest, default, stable)

unused import: `crate::command_scopes::CommandScopes`

Check failure on line 18 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / Check for unused dependencies

unused import: `crate::command_scopes::CommandScopes`
use crate::{Command, CommandExecutionEvent, CommandRegistry, CommandScopeRegistry};
use crate::{Command, CommandExecutionEvent, CommandProcessedEvent, CommandRegistry, CommandScopeRegistry};

Check failure on line 19 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-docs

unused imports: `CommandExecutionEvent`, `CommandScopeRegistry`

Check failure on line 19 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-clippy

unused imports: `CommandExecutionEvent`, `CommandScopeRegistry`

Check failure on line 19 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / valence-tests (ubuntu-latest, default, stable)

unused imports: `CommandExecutionEvent`, `CommandScopeRegistry`

Check failure on line 19 in crates/valence_command/src/handler.rs

View workflow job for this annotation

GitHub Actions / Check for unused dependencies

unused imports: `CommandExecutionEvent`, `CommandScopeRegistry`
use crate::modifier_value::ModifierValue;

pub struct CommandHandler<T>
where
Expand Down Expand Up @@ -59,7 +60,7 @@ where
{
pub result: T,
pub executor: Entity,
pub modifiers: HashMap<&'static str, String>,
pub modifiers: HashMap<ModifierValue, ModifierValue>,
}

impl<T> Plugin for CommandHandler<T>
Expand All @@ -86,206 +87,32 @@ fn command_startup_system<T>(
let graph_builder =
&mut CommandGraphBuilder::new(&mut registry, &mut executables, &mut parsers, &mut modifiers);
T::assemble_graph(graph_builder);
command.executables.extend(executables);
command.executables.extend(executables.clone());
registry.parsers.extend(parsers);
registry.modifiers.extend(modifiers);
registry.executables.extend(executables.keys());

println!("Command graph: {}", registry.graph);
}

/// this system reads incoming command events and prints them to the console
fn command_event_system<T>(
mut commands_executed: EventReader<CommandExecutionEvent>,
registry: Res<CommandRegistry>,
mut commands_executed: EventReader<CommandProcessedEvent>,
mut events: EventWriter<CommandResultEvent<T>>,
command: ResMut<CommandResource<T>>,
scope_registry: Res<CommandScopeRegistry>,
scopes: Query<&CommandScopes>,
) where
T: Command + Send + Sync + Debug,
{
for command_event in commands_executed.iter() {
let timer = Instant::now();
let executor = command_event.executor;
println!("Received command: {:?}", command_event);
// theese are the leafs of the graph that are executable under this command group
let executable_leafs = command.executables.keys().collect::<Vec<&NodeIndex>>();
println!("Executable leafs: {:?}", executable_leafs);
let root = registry.graph.root;

let command_input = &command_event.command;
let graph = &registry.graph.graph;
let mut input = ParseInput::new(command_input);

let mut to_be_executed = Vec::new();

let mut args = Vec::new();
let mut modifiers_to_be_executed = Vec::new();

parse_command_args(
&mut args,
&mut modifiers_to_be_executed,
&mut input,
graph,
&executable_leafs,
registry.as_ref(),
&mut to_be_executed,
root,
executor,
&scopes,
scope_registry.as_ref(),
false,
);

let mut modifiers = HashMap::new();
for (node, modifier) in modifiers_to_be_executed {
println!("Executing modifier with data: {:?}", modifier);
registry.modifiers.get(&node).unwrap()(modifier, &mut modifiers);
}

println!("modifiers: {:?}", modifiers);

for executable in to_be_executed {
println!("Executing command with args: {:?}", args);
let result = command.executables.get(&executable).unwrap()(&mut ParseInput::new(
&args.join(" "),
if command.executables.contains_key(&command_event.node) {
let timer = Instant::now();
let result = command.executables.get(&command_event.node).unwrap()(&mut ParseInput::new(
&command_event.command,
));
println!("executing command with info {:#?}", result);
events.send(CommandResultEvent { result, executor, modifiers: modifiers.clone() });
events.send(CommandResultEvent { result, executor: command_event.executor, modifiers: command_event.modifiers.clone() });
println!("Command took: {:?}", timer.elapsed());
}
println!("Command took: {:?}", timer.elapsed());
}
}

#[allow(clippy::too_many_arguments)]
/// recursively parse the command args.
fn parse_command_args(
command_args: &mut Vec<String>,
modifiers_to_be_executed: &mut Vec<(NodeIndex, String)>,
input: &mut ParseInput,
graph: &Graph<CommandNode, CommandEdgeType>,
executable_leafs: &[&NodeIndex],
command_registry: &CommandRegistry,
to_be_executed: &mut Vec<NodeIndex>,
curent_node: NodeIndex,
executor: Entity,
scopes: &Query<&CommandScopes>,
scope_registry: &CommandScopeRegistry,
coming_from_redirect: bool,
) -> bool {
let node_scopes = &graph[curent_node].scopes;
let default_scopes = CommandScopes::new();
let client_scopes = &scopes.get(executor).unwrap_or(&default_scopes).scopes;
// if empty, we assume the node is global
if !node_scopes.is_empty() {
let mut has_scope = false;
for scope in node_scopes {
if scope_registry.any_grants(client_scopes, scope) {
has_scope = true;
break;
}
}
if !has_scope {
return false;
}
}

if !coming_from_redirect {
// we want to skip whitespace before matching the node
input.skip_whitespace();
match &graph[curent_node].data {
// no real need to check for root node
NodeData::Root => {
if command_registry.modifiers.contains_key(&curent_node) {
modifiers_to_be_executed.push((curent_node, String::new()));
}
}
// if the node is a literal, we want to match the name of the literal
// to the input
NodeData::Literal { name } => {
match input.match_next(name) {
true => {
input.pop(); // we want to pop the whitespace after the literal
if command_registry.modifiers.contains_key(&curent_node) {
modifiers_to_be_executed.push((curent_node, String::new()));
}
}
false => return false,
}
}
// if the node is an argument, we want to parse the argument
NodeData::Argument { .. } => {

let parser = match command_registry.parsers.get(&curent_node) {
Some(parser) => parser,
None => {
return false;
}
};
// we want to save the cursor position before and after parsing
// this is so we can save the argument to the command args
let before_cursor = input.cursor;
let valid = parser(input);
let after_cursor = input.cursor;
if valid {
command_args.push(input.input[before_cursor..after_cursor].to_string());
if command_registry.modifiers.contains_key(&curent_node) {
modifiers_to_be_executed.push((curent_node, input.input[before_cursor..after_cursor].to_string()));
}
} else {
return false;
}
}
}
} else {
command_args.clear();
}

let pre_cursor = input.cursor;
input.skip_whitespace();
if input.is_done() && executable_leafs.contains(&&curent_node) {
to_be_executed.push(curent_node);
return true;
} else {
input.cursor = pre_cursor;
}

let mut all_invalid = true;
for neighbor in graph.neighbors(curent_node) {
let pre_cursor = input.cursor;
let mut args = command_args.clone();
let mut modifiers = modifiers_to_be_executed.clone();
let valid = parse_command_args(
&mut args,
&mut modifiers,
input,
graph,
executable_leafs,
command_registry,
to_be_executed,
neighbor,
executor,
scopes,
scope_registry,
{
let edge = graph.find_edge(curent_node, neighbor).unwrap();
match &graph[edge] {
CommandEdgeType::Redirect => {
true
}
_ => false,
}
},
);
if valid {
*command_args = args;
*modifiers_to_be_executed = modifiers;
all_invalid = false;
break;
} else {
input.cursor = pre_cursor;
}
}
if all_invalid {
return false;
}
true
}
22 changes: 19 additions & 3 deletions crates/valence_command/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ pub mod command_graph;
pub mod command_scopes;
pub mod handler;
pub mod manager;
mod modifier_value;

use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use bevy_ecs::event::Event;
use bevy_ecs::prelude::{Entity, Resource};
use petgraph::prelude::NodeIndex;
use serde_value::Value;
pub use command_scopes::CommandScopeRegistry;
pub use modifier_value::ModifierValue;
use crate::arg_parser::ParseInput;

use crate::command_graph::{CommandGraph, CommandGraphBuilder};
Expand All @@ -27,9 +28,24 @@ pub struct CommandExecutionEvent {
pub executor: Entity,
}

/// this will only be sent if the command was successfully parsed and an executable was found
#[derive(Debug, Clone, PartialEq, Eq, Event)]
pub struct CommandProcessedEvent {
/// the command that was executed eg. "teleport @p 0 ~ 0"
pub command: String,
/// usually the Client entity but it could be a command block or something
/// (whatever the library user wants)
pub executor: Entity,
/// the modifiers that were applied to the command
pub modifiers: HashMap<ModifierValue, ModifierValue>,
/// the node that was executed
pub node: NodeIndex,
}

#[derive(Resource, Default)]
pub struct CommandRegistry {
pub graph: CommandGraph,
pub parsers: HashMap<NodeIndex, fn(&mut ParseInput) -> bool>,
pub modifiers: HashMap<NodeIndex, fn(String, &mut HashMap<&str, String>)>,
pub modifiers: HashMap<NodeIndex, fn(String, &mut HashMap<ModifierValue, ModifierValue>)>,
pub executables: HashSet<NodeIndex>,
}
Loading

0 comments on commit 6cbeb5a

Please sign in to comment.