mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'veloren-poggly/chat_cmd_suggestion_and_links' into 'master'
[ #1286] Chat command suggestions + Fixing /help + code refactor See merge request veloren/veloren!3699
This commit is contained in:
commit
0543c265c8
@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Command to toggle experimental shaders.
|
||||
- Faster Energy Regeneration while sitting.
|
||||
- Lantern glow for dropped lanterns.
|
||||
- Suggests commands when an invalid one is entered in chat and added Client-side commands to /help.
|
||||
|
||||
### Changed
|
||||
- Bats move slower and use a simple proportional controller to maintain altitude
|
||||
|
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -3216,6 +3216,12 @@ version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
|
||||
|
||||
[[package]]
|
||||
name = "levenshtein"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
|
||||
|
||||
[[package]]
|
||||
name = "lewton"
|
||||
version = "0.10.2"
|
||||
@ -7080,6 +7086,7 @@ dependencies = [
|
||||
"itertools",
|
||||
"keyboard-keynames",
|
||||
"lazy_static",
|
||||
"levenshtein",
|
||||
"mimalloc",
|
||||
"mumble-link",
|
||||
"native-dialog",
|
||||
|
@ -28,6 +28,7 @@ macro_rules! synced_components {
|
||||
health: Health,
|
||||
poise: Poise,
|
||||
light_emitter: LightEmitter,
|
||||
loot_owner: LootOwner,
|
||||
item: Item,
|
||||
scale: Scale,
|
||||
group: Group,
|
||||
@ -56,10 +57,10 @@ macro_rules! synced_components {
|
||||
|
||||
// Synced to the client only for its own entity
|
||||
|
||||
admin: Admin,
|
||||
combo: Combo,
|
||||
active_abilities: ActiveAbilities,
|
||||
can_build: CanBuild,
|
||||
loot_owner: LootOwner,
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -151,6 +152,10 @@ impl NetSync for LightEmitter {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
|
||||
impl NetSync for LootOwner {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
|
||||
impl NetSync for Item {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
@ -217,6 +222,10 @@ impl NetSync for SkillSet {
|
||||
|
||||
// These are synced only from the client's own entity.
|
||||
|
||||
impl NetSync for Admin {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
|
||||
}
|
||||
|
||||
impl NetSync for Combo {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
|
||||
}
|
||||
@ -228,7 +237,3 @@ impl NetSync for ActiveAbilities {
|
||||
impl NetSync for CanBuild {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::ClientEntity;
|
||||
}
|
||||
|
||||
impl NetSync for LootOwner {
|
||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||
}
|
||||
|
@ -242,18 +242,20 @@ pub enum ServerChatCommand {
|
||||
Adminify,
|
||||
Airship,
|
||||
Alias,
|
||||
ApplyBuff,
|
||||
Ban,
|
||||
BattleMode,
|
||||
BattleModeForce,
|
||||
Body,
|
||||
Buff,
|
||||
Build,
|
||||
BuildAreaAdd,
|
||||
BuildAreaList,
|
||||
BuildAreaRemove,
|
||||
Campfire,
|
||||
CreateLocation,
|
||||
DebugColumn,
|
||||
DebugWays,
|
||||
DeleteLocation,
|
||||
DisconnectAllPlayers,
|
||||
DropAll,
|
||||
Dummy,
|
||||
@ -277,9 +279,12 @@ pub enum ServerChatCommand {
|
||||
Kit,
|
||||
Lantern,
|
||||
Light,
|
||||
Lightning,
|
||||
Location,
|
||||
MakeBlock,
|
||||
MakeNpc,
|
||||
MakeSprite,
|
||||
MakeVolume,
|
||||
Motd,
|
||||
Object,
|
||||
PermitBuild,
|
||||
@ -305,15 +310,10 @@ pub enum ServerChatCommand {
|
||||
Unban,
|
||||
Version,
|
||||
Waypoint,
|
||||
WeatherZone,
|
||||
Whitelist,
|
||||
Wiring,
|
||||
World,
|
||||
MakeVolume,
|
||||
Location,
|
||||
CreateLocation,
|
||||
DeleteLocation,
|
||||
WeatherZone,
|
||||
Lightning,
|
||||
}
|
||||
|
||||
impl ServerChatCommand {
|
||||
@ -339,7 +339,7 @@ impl ServerChatCommand {
|
||||
"Change your alias",
|
||||
Some(Moderator),
|
||||
),
|
||||
ServerChatCommand::ApplyBuff => cmd(
|
||||
ServerChatCommand::Buff => cmd(
|
||||
vec![
|
||||
Enum("buff", BUFFS.clone(), Required),
|
||||
Float("strength", 0.01, Optional),
|
||||
@ -734,11 +734,11 @@ impl ServerChatCommand {
|
||||
ServerChatCommand::Adminify => "adminify",
|
||||
ServerChatCommand::Airship => "airship",
|
||||
ServerChatCommand::Alias => "alias",
|
||||
ServerChatCommand::ApplyBuff => "buff",
|
||||
ServerChatCommand::Ban => "ban",
|
||||
ServerChatCommand::BattleMode => "battlemode",
|
||||
ServerChatCommand::BattleModeForce => "battlemode_force",
|
||||
ServerChatCommand::Body => "body",
|
||||
ServerChatCommand::Buff => "buff",
|
||||
ServerChatCommand::Build => "build",
|
||||
ServerChatCommand::BuildAreaAdd => "build_area_add",
|
||||
ServerChatCommand::BuildAreaList => "build_area_list",
|
||||
@ -759,14 +759,14 @@ impl ServerChatCommand {
|
||||
ServerChatCommand::GroupPromote => "group_promote",
|
||||
ServerChatCommand::GroupLeave => "group_leave",
|
||||
ServerChatCommand::Health => "health",
|
||||
ServerChatCommand::JoinFaction => "join_faction",
|
||||
ServerChatCommand::Help => "help",
|
||||
ServerChatCommand::Home => "home",
|
||||
ServerChatCommand::JoinFaction => "join_faction",
|
||||
ServerChatCommand::Jump => "jump",
|
||||
ServerChatCommand::Kick => "kick",
|
||||
ServerChatCommand::Kill => "kill",
|
||||
ServerChatCommand::Kit => "kit",
|
||||
ServerChatCommand::KillNpcs => "kill_npcs",
|
||||
ServerChatCommand::Kit => "kit",
|
||||
ServerChatCommand::Lantern => "lantern",
|
||||
ServerChatCommand::Light => "light",
|
||||
ServerChatCommand::MakeBlock => "make_block",
|
||||
@ -824,7 +824,9 @@ impl ServerChatCommand {
|
||||
}
|
||||
|
||||
/// Produce an iterator over all the available commands
|
||||
pub fn iter() -> impl Iterator<Item = Self> { <Self as IntoEnumIterator>::iter() }
|
||||
pub fn iter() -> impl Iterator<Item = Self> + Clone {
|
||||
<Self as IntoEnumIterator>::iter()
|
||||
}
|
||||
|
||||
/// A message that explains what the command does
|
||||
pub fn help_string(&self) -> String {
|
||||
@ -1042,6 +1044,18 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::comp::Item;
|
||||
|
||||
#[test]
|
||||
fn verify_cmd_list_sorted() {
|
||||
let mut list = ServerChatCommand::iter()
|
||||
.map(|c| c.keyword())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Vec::is_sorted is unstable, so we do it the hard way
|
||||
let list2 = list.clone();
|
||||
list.sort_unstable();
|
||||
assert_eq!(list, list2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_loading_skill_presets() { SkillPresetManifest::load_expect(PRESET_MANIFEST_PATH); }
|
||||
|
||||
|
@ -1,17 +1,18 @@
|
||||
use clap::arg_enum;
|
||||
use specs::Component;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::{Component, DerefFlaggedStorage, VecStorage};
|
||||
|
||||
arg_enum! {
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize)]
|
||||
pub enum AdminRole {
|
||||
Moderator = 0,
|
||||
Admin = 1,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Admin(pub AdminRole);
|
||||
|
||||
impl Component for Admin {
|
||||
type Storage = specs::VecStorage<Self>;
|
||||
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use crate::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::Entity as EcsEntity;
|
||||
use std::{collections::VecDeque, ops::DerefMut, sync::Mutex};
|
||||
use uuid::Uuid;
|
||||
use vek::*;
|
||||
|
||||
pub type SiteId = u64;
|
||||
@ -227,6 +228,11 @@ pub enum ServerEvent {
|
||||
entity: EcsEntity,
|
||||
update: comp::MapMarkerChange,
|
||||
},
|
||||
MakeAdmin {
|
||||
entity: EcsEntity,
|
||||
admin: comp::Admin,
|
||||
uuid: Uuid,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct EventBus<E> {
|
||||
|
@ -203,6 +203,7 @@ impl State {
|
||||
ecs.register::<comp::BeamSegment>();
|
||||
ecs.register::<comp::Alignment>();
|
||||
ecs.register::<comp::LootOwner>();
|
||||
ecs.register::<comp::Admin>();
|
||||
|
||||
// Register components send from clients -> server
|
||||
ecs.register::<comp::Controller>();
|
||||
@ -236,7 +237,6 @@ impl State {
|
||||
ecs.register::<comp::WaypointArea>();
|
||||
ecs.register::<comp::ForceUpdate>();
|
||||
ecs.register::<comp::InventoryUpdate>();
|
||||
ecs.register::<comp::Admin>();
|
||||
ecs.register::<comp::Waypoint>();
|
||||
ecs.register::<comp::MapMarker>();
|
||||
ecs.register::<comp::Projectile>();
|
||||
|
@ -1,7 +1,6 @@
|
||||
//! # Implementing new commands.
|
||||
//! To implement a new command provide a handler function
|
||||
//! in [do_command].
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
location::Locations,
|
||||
@ -125,11 +124,11 @@ fn do_command(
|
||||
ServerChatCommand::Adminify => handle_adminify,
|
||||
ServerChatCommand::Airship => handle_spawn_airship,
|
||||
ServerChatCommand::Alias => handle_alias,
|
||||
ServerChatCommand::ApplyBuff => handle_apply_buff,
|
||||
ServerChatCommand::Ban => handle_ban,
|
||||
ServerChatCommand::BattleMode => handle_battlemode,
|
||||
ServerChatCommand::BattleModeForce => handle_battlemode_force,
|
||||
ServerChatCommand::Body => handle_body,
|
||||
ServerChatCommand::Buff => handle_buff,
|
||||
ServerChatCommand::Build => handle_build,
|
||||
ServerChatCommand::BuildAreaAdd => handle_build_area_add,
|
||||
ServerChatCommand::BuildAreaList => handle_build_area_list,
|
||||
@ -3510,7 +3509,7 @@ fn handle_server_physics(
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_apply_buff(
|
||||
fn handle_buff(
|
||||
server: &mut Server,
|
||||
_client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
|
@ -11,6 +11,7 @@ use crate::{
|
||||
sys::terrain::SAFE_ZONE_RADIUS,
|
||||
Server, SpawnPoint, StateExt,
|
||||
};
|
||||
use authc::Uuid;
|
||||
use common::{
|
||||
combat,
|
||||
combat::DamageContributor,
|
||||
@ -1458,3 +1459,16 @@ pub fn handle_update_map_marker(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_make_admin(server: &mut Server, entity: EcsEntity, admin: comp::Admin, uuid: Uuid) {
|
||||
if server
|
||||
.state
|
||||
.read_storage::<Player>()
|
||||
.get(entity)
|
||||
.map_or(false, |player| player.uuid() == uuid)
|
||||
{
|
||||
server
|
||||
.state
|
||||
.write_component_ignore_entity_dead(entity, admin);
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,8 @@ use entity_manipulation::{
|
||||
handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change,
|
||||
handle_delete, handle_destroy, handle_energy_change, handle_entity_attacked_hook,
|
||||
handle_explosion, handle_health_change, handle_knockback, handle_land_on_ground,
|
||||
handle_parry_hook, handle_poise, handle_respawn, handle_teleport_to, handle_update_map_marker,
|
||||
handle_make_admin, handle_parry_hook, handle_poise, handle_respawn, handle_teleport_to,
|
||||
handle_update_map_marker,
|
||||
};
|
||||
use group_manip::handle_group;
|
||||
use information::handle_site_info;
|
||||
@ -288,6 +289,11 @@ impl Server {
|
||||
ServerEvent::UpdateMapMarker { entity, update } => {
|
||||
handle_update_map_marker(self, entity, update)
|
||||
},
|
||||
ServerEvent::MakeAdmin {
|
||||
entity,
|
||||
admin,
|
||||
uuid,
|
||||
} => handle_make_admin(self, entity, admin, uuid),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,10 +61,10 @@ pub struct ReadData<'a> {
|
||||
pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Read<'a, EventBus<ServerEvent>>,
|
||||
ReadData<'a>,
|
||||
WriteStorage<'a, Client>,
|
||||
WriteStorage<'a, Player>,
|
||||
WriteStorage<'a, Admin>,
|
||||
WriteStorage<'a, PendingLogin>,
|
||||
);
|
||||
|
||||
@ -74,7 +74,7 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(
|
||||
_job: &mut Job<Self>,
|
||||
(read_data, mut clients, mut players, mut admins, mut pending_logins): Self::SystemData,
|
||||
(event_bus, read_data, mut clients, mut players, mut pending_logins): Self::SystemData,
|
||||
) {
|
||||
// Player list to send new players, and lookup from UUID to entity (so we don't
|
||||
// have to do a linear scan over all entities on each login to see if
|
||||
@ -87,7 +87,7 @@ impl<'a> System<'a> for Sys {
|
||||
&read_data.uids,
|
||||
&players,
|
||||
read_data.stats.maybe(),
|
||||
admins.maybe(),
|
||||
read_data.trackers.admin.maybe(),
|
||||
)
|
||||
.join()
|
||||
.map(|(entity, uid, player, stats, admin)| {
|
||||
@ -379,6 +379,7 @@ impl<'a> System<'a> for Sys {
|
||||
.into_values()
|
||||
.map(|(entity, player, admin, msg)| {
|
||||
let username = &player.alias;
|
||||
let uuid = player.uuid();
|
||||
info!(?username, "New User");
|
||||
// Add Player component to this client.
|
||||
//
|
||||
@ -396,9 +397,13 @@ impl<'a> System<'a> for Sys {
|
||||
// Give the Admin component to the player if their name exists in
|
||||
// admin list
|
||||
if let Some(admin) = admin {
|
||||
admins
|
||||
.insert(entity, Admin(admin.role.into()))
|
||||
.expect("Inserting into players proves the entity exists.");
|
||||
// We need to defer writing to the Admin storage since it's borrowed immutably
|
||||
// by this system via TrackedStorages.
|
||||
event_bus.emit_now(ServerEvent::MakeAdmin {
|
||||
entity,
|
||||
admin: Admin(admin.role.into()),
|
||||
uuid,
|
||||
});
|
||||
}
|
||||
msg
|
||||
})
|
||||
|
@ -87,6 +87,7 @@ specs = { version = "0.18", features = ["serde", "storage-event-control", "deriv
|
||||
|
||||
# Mathematics
|
||||
vek = {version = "0.15.8", features = ["serde"]}
|
||||
levenshtein = "1.0.5"
|
||||
|
||||
# Controller
|
||||
gilrs = {version = "0.10.0", features = ["serde-serialize"]}
|
||||
|
@ -4,13 +4,15 @@ use crate::{
|
||||
render::ExperimentalShader, session::settings_change::change_render_mode, GlobalState,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{cmd::*, parse_cmd_args, uuid::Uuid};
|
||||
use common::{cmd::*, comp::Admin, parse_cmd_args, uuid::Uuid};
|
||||
use levenshtein::levenshtein;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
// Please keep this sorted alphabetically, same as with server commands :-)
|
||||
#[derive(Clone, Copy, strum::EnumIter)]
|
||||
pub enum ClientChatCommand {
|
||||
ExperimentalShader,
|
||||
Help,
|
||||
Mute,
|
||||
Unmute,
|
||||
}
|
||||
@ -21,16 +23,6 @@ impl ClientChatCommand {
|
||||
use Requirement::*;
|
||||
let cmd = ChatCommandData::new;
|
||||
match self {
|
||||
ClientChatCommand::Mute => cmd(
|
||||
vec![PlayerName(Required)],
|
||||
"Mutes chat messages from a player.",
|
||||
None,
|
||||
),
|
||||
ClientChatCommand::Unmute => cmd(
|
||||
vec![PlayerName(Required)],
|
||||
"Unmutes a player muted with the 'mute' command.",
|
||||
None,
|
||||
),
|
||||
ClientChatCommand::ExperimentalShader => cmd(
|
||||
vec![Enum(
|
||||
"Shader",
|
||||
@ -42,14 +34,30 @@ impl ClientChatCommand {
|
||||
"Toggles an experimental shader.",
|
||||
None,
|
||||
),
|
||||
ClientChatCommand::Help => cmd(
|
||||
vec![Command(Optional)],
|
||||
"Display information about commands",
|
||||
None,
|
||||
),
|
||||
ClientChatCommand::Mute => cmd(
|
||||
vec![PlayerName(Required)],
|
||||
"Mutes chat messages from a player.",
|
||||
None,
|
||||
),
|
||||
ClientChatCommand::Unmute => cmd(
|
||||
vec![PlayerName(Required)],
|
||||
"Unmutes a player muted with the 'mute' command.",
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyword(&self) -> &'static str {
|
||||
match self {
|
||||
ClientChatCommand::ExperimentalShader => "experimental_shader",
|
||||
ClientChatCommand::Help => "help",
|
||||
ClientChatCommand::Mute => "mute",
|
||||
ClientChatCommand::Unmute => "unmute",
|
||||
ClientChatCommand::ExperimentalShader => "experimental_shader",
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +93,9 @@ impl ClientChatCommand {
|
||||
}
|
||||
|
||||
/// Produce an iterator over all the available commands
|
||||
pub fn iter() -> impl Iterator<Item = Self> { <Self as strum::IntoEnumIterator>::iter() }
|
||||
pub fn iter() -> impl Iterator<Item = Self> + Clone {
|
||||
<Self as strum::IntoEnumIterator>::iter()
|
||||
}
|
||||
|
||||
/// Produce an iterator that first goes over all the short keywords
|
||||
/// and their associated commands and then iterates over all the normal
|
||||
@ -113,15 +123,15 @@ pub enum ChatCommandKind {
|
||||
}
|
||||
|
||||
impl FromStr for ChatCommandKind {
|
||||
type Err = String;
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, String> {
|
||||
fn from_str(s: &str) -> Result<Self, ()> {
|
||||
if let Ok(cmd) = s.parse::<ClientChatCommand>() {
|
||||
Ok(ChatCommandKind::Client(cmd))
|
||||
} else if let Ok(cmd) = s.parse::<ServerChatCommand>() {
|
||||
Ok(ChatCommandKind::Server(cmd))
|
||||
} else {
|
||||
Err(format!("Could not find a command named {}.", s))
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -142,19 +152,49 @@ pub fn run_command(
|
||||
cmd: &str,
|
||||
args: Vec<String>,
|
||||
) -> CommandResult {
|
||||
let command = ChatCommandKind::from_str(cmd)?;
|
||||
let command = ChatCommandKind::from_str(cmd);
|
||||
|
||||
match command {
|
||||
ChatCommandKind::Server(cmd) => {
|
||||
Ok(ChatCommandKind::Server(cmd)) => {
|
||||
client.send_command(cmd.keyword().into(), args);
|
||||
Ok(None) // The server will provide a response when the command is run
|
||||
},
|
||||
ChatCommandKind::Client(cmd) => {
|
||||
Ok(ChatCommandKind::Client(cmd)) => {
|
||||
Ok(Some(run_client_command(client, global_state, cmd, args)?))
|
||||
},
|
||||
Err(()) => Err(invalid_command_message(client, cmd.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
fn invalid_command_message(client: &Client, user_entered_invalid_command: String) -> String {
|
||||
let entity_role = client
|
||||
.state()
|
||||
.read_storage::<Admin>()
|
||||
.get(client.entity())
|
||||
.map(|admin| admin.0);
|
||||
|
||||
let usable_commands = ServerChatCommand::iter()
|
||||
.filter(|cmd| cmd.needs_role() <= entity_role)
|
||||
.map(|cmd| cmd.keyword())
|
||||
.chain(ClientChatCommand::iter().map(|cmd| cmd.keyword()));
|
||||
|
||||
let most_similar_str = usable_commands
|
||||
.clone()
|
||||
.min_by_key(|cmd| levenshtein(&user_entered_invalid_command, cmd))
|
||||
.expect("At least one command exists.");
|
||||
|
||||
let commands_with_same_prefix = usable_commands
|
||||
.filter(|cmd| cmd.starts_with(&user_entered_invalid_command) && cmd != &most_similar_str);
|
||||
|
||||
format!(
|
||||
"Could not find a command named {}. Did you mean any of the following? \n/{} {} \n\nType \
|
||||
/help to see a list of all commands.",
|
||||
user_entered_invalid_command,
|
||||
most_similar_str,
|
||||
commands_with_same_prefix.fold(String::new(), |s, arg| s + "\n/" + arg)
|
||||
)
|
||||
}
|
||||
|
||||
fn run_client_command(
|
||||
client: &mut Client,
|
||||
global_state: &mut GlobalState,
|
||||
@ -162,14 +202,52 @@ fn run_client_command(
|
||||
args: Vec<String>,
|
||||
) -> Result<String, String> {
|
||||
let command = match command {
|
||||
ClientChatCommand::ExperimentalShader => handle_experimental_shader,
|
||||
ClientChatCommand::Help => handle_help,
|
||||
ClientChatCommand::Mute => handle_mute,
|
||||
ClientChatCommand::Unmute => handle_unmute,
|
||||
ClientChatCommand::ExperimentalShader => handle_experimental_shader,
|
||||
};
|
||||
|
||||
command(client, global_state, args)
|
||||
}
|
||||
|
||||
fn handle_help(
|
||||
client: &Client,
|
||||
_global_state: &mut GlobalState,
|
||||
args: Vec<String>,
|
||||
) -> Result<String, String> {
|
||||
if let Some(cmd) = parse_cmd_args!(args, ServerChatCommand) {
|
||||
Ok(cmd.help_string())
|
||||
} else {
|
||||
let mut message = String::new();
|
||||
let entity_role = client
|
||||
.state()
|
||||
.read_storage::<Admin>()
|
||||
.get(client.entity())
|
||||
.map(|admin| admin.0);
|
||||
|
||||
ClientChatCommand::iter().for_each(|cmd| {
|
||||
message += &cmd.help_string();
|
||||
message += "\n";
|
||||
});
|
||||
// Iterate through all ServerChatCommands you have permission to use.
|
||||
ServerChatCommand::iter()
|
||||
.filter(|cmd| cmd.needs_role() <= entity_role)
|
||||
.for_each(|cmd| {
|
||||
message += &cmd.help_string();
|
||||
message += "\n";
|
||||
});
|
||||
message += "Additionally, you can use the following shortcuts:";
|
||||
ServerChatCommand::iter()
|
||||
.filter(|cmd| cmd.needs_role() <= entity_role)
|
||||
.filter_map(|cmd| cmd.short_keyword().map(|k| (k, cmd)))
|
||||
.for_each(|(k, cmd)| {
|
||||
message += &format!(" /{} => /{}", k, cmd.keyword());
|
||||
});
|
||||
Ok(message)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_mute(
|
||||
client: &Client,
|
||||
global_state: &mut GlobalState,
|
||||
|
Loading…
Reference in New Issue
Block a user