mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
only preprocess for entity target argument
This commit is contained in:
parent
ef47ed6f62
commit
4d8bcf0a92
@ -1095,7 +1095,7 @@ impl FromStr for ServerChatCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Eq, PartialEq, Debug)]
|
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
|
||||||
pub enum Requirement {
|
pub enum Requirement {
|
||||||
Required,
|
Required,
|
||||||
Optional,
|
Optional,
|
||||||
@ -1216,6 +1216,22 @@ impl ArgumentSpec {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn requirement(&self) -> Requirement {
|
||||||
|
match self {
|
||||||
|
ArgumentSpec::PlayerName(r)
|
||||||
|
| ArgumentSpec::EntityTarget(r)
|
||||||
|
| ArgumentSpec::SiteName(r)
|
||||||
|
| ArgumentSpec::Float(_, _, r)
|
||||||
|
| ArgumentSpec::Integer(_, _, r)
|
||||||
|
| ArgumentSpec::Any(_, r)
|
||||||
|
| ArgumentSpec::Command(r)
|
||||||
|
| ArgumentSpec::Message(r)
|
||||||
|
| ArgumentSpec::Enum(_, _, r)
|
||||||
|
| ArgumentSpec::Boolean(_, _, r) => *r,
|
||||||
|
ArgumentSpec::SubCommand => Requirement::Required,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a series of command arguments into values, including collecting all
|
/// Parse a series of command arguments into values, including collecting all
|
||||||
|
@ -3692,6 +3692,8 @@ fn handle_sudo(
|
|||||||
(entity, player_uuid),
|
(entity, player_uuid),
|
||||||
"Cannot sudo players with roles higher than your own.",
|
"Cannot sudo players with roles higher than your own.",
|
||||||
)?;
|
)?;
|
||||||
|
} else if server.entity_admin_role(client) < Some(AdminRole::Admin) {
|
||||||
|
return Err("You don't have permission to sudo non-players.".into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,13 @@ use common::{
|
|||||||
mounting::{Mount, Rider, VolumeRider},
|
mounting::{Mount, Rider, VolumeRider},
|
||||||
parse_cmd_args,
|
parse_cmd_args,
|
||||||
resources::PlayerEntity,
|
resources::PlayerEntity,
|
||||||
|
uid::Uid,
|
||||||
uuid::Uuid,
|
uuid::Uuid,
|
||||||
};
|
};
|
||||||
use common_net::sync::WorldSyncExt;
|
use common_net::sync::WorldSyncExt;
|
||||||
use levenshtein::levenshtein;
|
use levenshtein::levenshtein;
|
||||||
use specs::WorldExt;
|
use specs::{Join, WorldExt};
|
||||||
use strum::IntoEnumIterator;
|
use strum::{EnumIter, IntoEnumIterator};
|
||||||
|
|
||||||
// Please keep this sorted alphabetically, same as with server commands :-)
|
// Please keep this sorted alphabetically, same as with server commands :-)
|
||||||
#[derive(Clone, Copy, strum::EnumIter)]
|
#[derive(Clone, Copy, strum::EnumIter)]
|
||||||
@ -155,37 +156,93 @@ impl FromStr for ChatCommandKind {
|
|||||||
/// text color
|
/// text color
|
||||||
type CommandResult = Result<Option<String>, String>;
|
type CommandResult = Result<Option<String>, String>;
|
||||||
|
|
||||||
/// Runs a command by either sending it to the server or processing it
|
#[derive(EnumIter)]
|
||||||
/// locally. Returns a String to be output to the chat.
|
enum ClientEntityTarget {
|
||||||
// Note: it's not clear what data future commands will need access to, so the
|
Target,
|
||||||
// signature of this function might change
|
Selected,
|
||||||
pub fn run_command(
|
Viewpoint,
|
||||||
|
Mount,
|
||||||
|
Rider,
|
||||||
|
TargetSelf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClientEntityTarget {
|
||||||
|
const PREFIX: char = '@';
|
||||||
|
|
||||||
|
fn keyword(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ClientEntityTarget::Target => "target",
|
||||||
|
ClientEntityTarget::Selected => "selected",
|
||||||
|
ClientEntityTarget::Viewpoint => "viewpoint",
|
||||||
|
ClientEntityTarget::Mount => "mount",
|
||||||
|
ClientEntityTarget::Rider => "rider",
|
||||||
|
ClientEntityTarget::TargetSelf => "self",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn preproccess_command(
|
||||||
session_state: &mut SessionState,
|
session_state: &mut SessionState,
|
||||||
global_state: &mut GlobalState,
|
command: &ChatCommandKind,
|
||||||
cmd: &str,
|
args: &mut Vec<String>,
|
||||||
mut args: Vec<String>,
|
|
||||||
) -> CommandResult {
|
) -> CommandResult {
|
||||||
let command = ChatCommandKind::from_str(cmd);
|
let mut cmd_args = match command {
|
||||||
|
ChatCommandKind::Client(cmd) => cmd.data().args,
|
||||||
|
ChatCommandKind::Server(cmd) => cmd.data().args,
|
||||||
|
};
|
||||||
let client = &mut session_state.client.borrow_mut();
|
let client = &mut session_state.client.borrow_mut();
|
||||||
let ecs = client.state().ecs();
|
let ecs = client.state().ecs();
|
||||||
let player = ecs.read_resource::<PlayerEntity>().0;
|
let player = ecs.read_resource::<PlayerEntity>().0;
|
||||||
|
let mut command_start = 0;
|
||||||
for arg in args.iter_mut() {
|
for (i, arg) in args.iter_mut().enumerate() {
|
||||||
if arg.starts_with('@') {
|
let mut could_be_entity_target = false;
|
||||||
let uid = match arg.trim_start_matches('@') {
|
if let Some(post_cmd_args) = cmd_args.get(i - command_start..) {
|
||||||
"target" => session_state
|
for (j, arg_spec) in post_cmd_args.iter().enumerate() {
|
||||||
|
match arg_spec {
|
||||||
|
ArgumentSpec::EntityTarget(_) => could_be_entity_target = true,
|
||||||
|
ArgumentSpec::SubCommand => {
|
||||||
|
if let Some(sub_command) =
|
||||||
|
ServerChatCommand::iter().find(|cmd| cmd.keyword() == arg)
|
||||||
|
{
|
||||||
|
cmd_args = sub_command.data().args;
|
||||||
|
command_start = i + j + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
if matches!(arg_spec.requirement(), Requirement::Required) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if matches!(cmd_args.last(), Some(ArgumentSpec::SubCommand)) {
|
||||||
|
could_be_entity_target = true;
|
||||||
|
}
|
||||||
|
if could_be_entity_target && arg.starts_with(ClientEntityTarget::PREFIX) {
|
||||||
|
let target_str = arg.trim_start_matches(ClientEntityTarget::PREFIX);
|
||||||
|
let target = ClientEntityTarget::iter()
|
||||||
|
.find(|t| t.keyword() == target_str)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
let help_string = ClientEntityTarget::iter()
|
||||||
|
.map(|t| t.keyword().to_string())
|
||||||
|
.reduce(|a, b| format!("{a}/{b}"))
|
||||||
|
.unwrap_or_default();
|
||||||
|
format!("Expected {help_string} after '@' found {target_str}")
|
||||||
|
})?;
|
||||||
|
let uid = match target {
|
||||||
|
ClientEntityTarget::Target => session_state
|
||||||
.target_entity
|
.target_entity
|
||||||
.and_then(|e| ecs.uid_from_entity(e))
|
.and_then(|e| ecs.uid_from_entity(e))
|
||||||
.ok_or("Not looking at a valid target".to_string())?,
|
.ok_or("Not looking at a valid target".to_string())?,
|
||||||
"selected" => session_state
|
ClientEntityTarget::Selected => session_state
|
||||||
.selected_entity
|
.selected_entity
|
||||||
.and_then(|(e, _)| ecs.uid_from_entity(e))
|
.and_then(|(e, _)| ecs.uid_from_entity(e))
|
||||||
.ok_or("You don't have a valid target selected".to_string())?,
|
.ok_or("You don't have a valid target selected".to_string())?,
|
||||||
"viewpoint" => session_state
|
ClientEntityTarget::Viewpoint => session_state
|
||||||
.viewpoint_entity
|
.viewpoint_entity
|
||||||
.and_then(|e| ecs.uid_from_entity(e))
|
.and_then(|e| ecs.uid_from_entity(e))
|
||||||
.ok_or("Not viewing from a valid viewpoint entity".to_string())?,
|
.ok_or("Not viewing from a valid viewpoint entity".to_string())?,
|
||||||
"mount" => {
|
ClientEntityTarget::Mount => {
|
||||||
if let Some(player) = player {
|
if let Some(player) = player {
|
||||||
ecs.read_storage::<Is<Rider>>()
|
ecs.read_storage::<Is<Rider>>()
|
||||||
.get(player)
|
.get(player)
|
||||||
@ -201,7 +258,7 @@ pub fn run_command(
|
|||||||
return Err("No player entity".to_string());
|
return Err("No player entity".to_string());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rider" => {
|
ClientEntityTarget::Rider => {
|
||||||
if let Some(player) = player {
|
if let Some(player) = player {
|
||||||
ecs.read_storage::<Is<Mount>>()
|
ecs.read_storage::<Is<Mount>>()
|
||||||
.get(player)
|
.get(player)
|
||||||
@ -211,30 +268,43 @@ pub fn run_command(
|
|||||||
return Err("No player entity".to_string());
|
return Err("No player entity".to_string());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"self" => player
|
ClientEntityTarget::TargetSelf => player
|
||||||
.and_then(|e| ecs.uid_from_entity(e))
|
.and_then(|e| ecs.uid_from_entity(e))
|
||||||
.ok_or("No player entity")?,
|
.ok_or("No player entity")?,
|
||||||
ident => {
|
|
||||||
return Err(format!(
|
|
||||||
"Expected target/selected/viewpoint/mount/rider/self after '@' found \
|
|
||||||
{ident}"
|
|
||||||
));
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let uid = u64::from(uid);
|
let uid = u64::from(uid);
|
||||||
*arg = format!("uid@{uid}");
|
*arg = format!("uid@{uid}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs a command by either sending it to the server or processing it
|
||||||
|
/// locally. Returns a String to be output to the chat.
|
||||||
|
// Note: it's not clear what data future commands will need access to, so the
|
||||||
|
// signature of this function might change
|
||||||
|
pub fn run_command(
|
||||||
|
session_state: &mut SessionState,
|
||||||
|
global_state: &mut GlobalState,
|
||||||
|
cmd: &str,
|
||||||
|
mut args: Vec<String>,
|
||||||
|
) -> CommandResult {
|
||||||
|
let command = ChatCommandKind::from_str(cmd)
|
||||||
|
.map_err(|_| invalid_command_message(&session_state.client.borrow(), cmd.to_string()))?;
|
||||||
|
|
||||||
|
preproccess_command(session_state, &command, &mut args)?;
|
||||||
|
|
||||||
|
let client = &mut session_state.client.borrow_mut();
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
Ok(ChatCommandKind::Server(cmd)) => {
|
ChatCommandKind::Server(cmd) => {
|
||||||
client.send_command(cmd.keyword().into(), args);
|
client.send_command(cmd.keyword().into(), args);
|
||||||
Ok(None) // The server will provide a response when the command is run
|
Ok(None) // The server will provide a response when the command is run
|
||||||
},
|
},
|
||||||
Ok(ChatCommandKind::Client(cmd)) => {
|
ChatCommandKind::Client(cmd) => {
|
||||||
Ok(Some(run_client_command(client, global_state, cmd, args)?))
|
Ok(Some(run_client_command(client, global_state, cmd, args)?))
|
||||||
},
|
},
|
||||||
Err(()) => Err(invalid_command_message(client, cmd.to_string())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +525,45 @@ impl TabComplete for ArgumentSpec {
|
|||||||
fn complete(&self, part: &str, client: &Client) -> Vec<String> {
|
fn complete(&self, part: &str, client: &Client) -> Vec<String> {
|
||||||
match self {
|
match self {
|
||||||
ArgumentSpec::PlayerName(_) => complete_player(part, client),
|
ArgumentSpec::PlayerName(_) => complete_player(part, client),
|
||||||
ArgumentSpec::EntityTarget(_) => complete_player(part, client),
|
ArgumentSpec::EntityTarget(_) => {
|
||||||
|
if let Some((spec, end)) = part.split_once(ClientEntityTarget::PREFIX) {
|
||||||
|
match spec {
|
||||||
|
"" => ClientEntityTarget::iter()
|
||||||
|
.filter_map(|target| {
|
||||||
|
let ident = target.keyword();
|
||||||
|
if ident.starts_with(end) {
|
||||||
|
Some(format!("@{ident}"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
"uid" => {
|
||||||
|
if let Ok(end) = u64::from_str(end) {
|
||||||
|
client
|
||||||
|
.state()
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<Uid>()
|
||||||
|
.join()
|
||||||
|
.filter_map(|uid| {
|
||||||
|
let uid = u64::from(*uid);
|
||||||
|
if end < uid {
|
||||||
|
Some(format!("uid@{uid}"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => vec![],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
complete_player(part, client)
|
||||||
|
}
|
||||||
|
},
|
||||||
ArgumentSpec::SiteName(_) => complete_site(part, client),
|
ArgumentSpec::SiteName(_) => complete_site(part, client),
|
||||||
ArgumentSpec::Float(_, x, _) => {
|
ArgumentSpec::Float(_, x, _) => {
|
||||||
if part.is_empty() {
|
if part.is_empty() {
|
||||||
|
Loading…
Reference in New Issue
Block a user