Make handling of shortcut keywords for commands clearer and revise a TODO related to how chat messages are stored and renamed with alias changes

This commit is contained in:
Imbris 2021-06-06 18:36:25 -04:00
parent bb0bb38108
commit 0b92c72096
4 changed files with 55 additions and 112 deletions

View File

@ -52,12 +52,12 @@ fn complete_player(part: &str, client: &Client) -> Vec<String> {
}
fn complete_command(part: &str) -> Vec<String> {
CHAT_SHORTCUTS
.keys()
.map(ToString::to_string)
.chain(CHAT_COMMANDS.iter().map(ToString::to_string))
.filter(|kwd| kwd.starts_with(part) || format!("/{}", kwd).starts_with(part))
.map(|c| format!("/{}", c))
let part = part.strip_prefix('/').unwrap_or(part);
ChatCommand::iter_with_keywords()
.map(|(kwd, _)| kwd)
.filter(|kwd| kwd.starts_with(part))
.map(|kwd| format!("/{}", kwd))
.collect()
}

View File

@ -1712,8 +1712,12 @@ impl Client {
// Instead of removing players, mark them as offline because we need to
// remember the names of disconnected players in chat.
//
// TODO the server should re-use uids of players that log out and log back
// in.
// TODO: consider alternatives since this leads to an ever growing list as
// players log out and in. Keep in mind we might only want to
// keep only so many messages in chat the history. We could
// potentially use an ID that's more persistent than the Uid.
// One of the reasons we don't just store the string of the player name
// into the message is to make alias changes reflected in older messages.
if let Some(player_info) = self.player_list.get_mut(&uid) {
if player_info.is_online {

View File

@ -40,7 +40,7 @@ impl ChatCommandData {
}
// Please keep this sorted alphabetically :-)
#[derive(Copy, Clone)]
#[derive(Copy, Clone, strum_macros::EnumIter)]
pub enum ChatCommand {
Adminify,
Airship,
@ -106,72 +106,6 @@ pub enum ChatCommand {
World,
}
// Thank you for keeping this sorted alphabetically :-)
pub static CHAT_COMMANDS: &[ChatCommand] = &[
ChatCommand::Adminify,
ChatCommand::Airship,
ChatCommand::Alias,
ChatCommand::ApplyBuff,
ChatCommand::Ban,
ChatCommand::Build,
ChatCommand::BuildAreaAdd,
ChatCommand::BuildAreaList,
ChatCommand::BuildAreaRemove,
ChatCommand::Campfire,
ChatCommand::DebugColumn,
ChatCommand::DisconnectAllPlayers,
ChatCommand::DropAll,
ChatCommand::Dummy,
ChatCommand::Explosion,
ChatCommand::Faction,
ChatCommand::GiveItem,
ChatCommand::Goto,
ChatCommand::Group,
ChatCommand::GroupInvite,
ChatCommand::GroupKick,
ChatCommand::GroupLeave,
ChatCommand::GroupPromote,
ChatCommand::Health,
ChatCommand::Help,
ChatCommand::Home,
ChatCommand::JoinFaction,
ChatCommand::Jump,
ChatCommand::Kick,
ChatCommand::Kill,
ChatCommand::KillNpcs,
ChatCommand::Kit,
ChatCommand::Lantern,
ChatCommand::Light,
ChatCommand::MakeBlock,
ChatCommand::MakeSprite,
ChatCommand::Motd,
ChatCommand::Object,
ChatCommand::PermitBuild,
ChatCommand::Players,
ChatCommand::Region,
ChatCommand::RemoveLights,
ChatCommand::RevokeBuild,
ChatCommand::RevokeBuildAll,
ChatCommand::Safezone,
ChatCommand::Say,
ChatCommand::ServerPhysics,
ChatCommand::SetMotd,
ChatCommand::Site,
ChatCommand::SkillPoint,
ChatCommand::SkillPreset,
ChatCommand::Spawn,
ChatCommand::Sudo,
ChatCommand::Tell,
ChatCommand::Time,
ChatCommand::Tp,
ChatCommand::Unban,
ChatCommand::Version,
ChatCommand::Waypoint,
ChatCommand::Whitelist,
ChatCommand::Wiring,
ChatCommand::World,
];
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct KitManifest(pub HashMap<String, Vec<(String, u32)>>);
impl assets::Asset for KitManifest {
@ -189,15 +123,6 @@ impl assets::Asset for SkillPresetManifest {
}
lazy_static! {
pub static ref CHAT_SHORTCUTS: HashMap<char, ChatCommand> = [
('f', ChatCommand::Faction),
('g', ChatCommand::Group),
('r', ChatCommand::Region),
('s', ChatCommand::Say),
('t', ChatCommand::Tell),
('w', ChatCommand::World),
].iter().cloned().collect();
static ref ALIGNMENTS: Vec<String> = vec!["wild", "enemy", "npc", "pet"]
.iter()
.map(|s| s.to_string())
@ -740,6 +665,20 @@ impl ChatCommand {
}
}
/// The short keyword used to invoke the command, omitting the leading '/'.
/// Returns None if the command doesn't have a short keyword
pub fn short_keyword(&self) -> Option<&'static str> {
Some(match self {
ChatCommand::Faction => "f",
ChatCommand::Group => "g",
ChatCommand::Region => "r",
ChatCommand::Say => "s",
ChatCommand::Tell => "t",
ChatCommand::World => "w",
_ => return None,
})
}
/// A message that explains what the command does
pub fn help_string(&self) -> String {
let data = self.data();
@ -773,6 +712,19 @@ impl ChatCommand {
.collect::<Vec<_>>()
.join(" ")
}
/// Produce an iterator over all the available commands
pub fn iter() -> impl Iterator<Item = Self> { <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
/// keywords with their associated commands
pub fn iter_with_keywords() -> impl Iterator<Item = (&'static str, Self)> {
Self::iter()
// Go through all the shortcuts first
.filter_map(|c| c.short_keyword().map(|s| (s, c)))
.chain(Self::iter().map(|c| (c.keyword(), c)))
}
}
impl Display for ChatCommand {
@ -785,28 +737,13 @@ impl FromStr for ChatCommand {
type Err = ();
fn from_str(keyword: &str) -> Result<ChatCommand, ()> {
let kwd = if let Some(stripped) = keyword.strip_prefix('/') {
stripped
} else {
&keyword
};
if keyword.len() == 1 {
if let Some(c) = keyword
.chars()
.next()
.as_ref()
.and_then(|k| CHAT_SHORTCUTS.get(k))
{
return Ok(*c);
}
} else {
for c in CHAT_COMMANDS {
if kwd == c.keyword() {
return Ok(*c);
}
}
}
Err(())
let keyword = keyword.strip_prefix('/').unwrap_or(keyword);
Self::iter_with_keywords()
// Find command with matching string as keyword
.find_map(|(kwd, command)| (kwd == keyword).then(|| command))
// Return error if not found
.ok_or(())
}
}

View File

@ -14,7 +14,7 @@ use authc::Uuid;
use chrono::{NaiveTime, Timelike, Utc};
use common::{
assets,
cmd::{ChatCommand, BUFF_PACK, BUFF_PARSER, CHAT_COMMANDS, CHAT_SHORTCUTS},
cmd::{ChatCommand, BUFF_PACK, BUFF_PARSER},
comp::{
self,
aura::{Aura, AuraKind, AuraTarget},
@ -1526,17 +1526,19 @@ fn handle_help(
let entity_role = server.entity_admin_role(client);
// Iterate through all commands you have permission to use.
CHAT_COMMANDS
.iter()
ChatCommand::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:";
for (k, v) in CHAT_SHORTCUTS.iter() {
message += &format!(" /{} => /{}", k, v.keyword());
}
ChatCommand::iter()
.filter_map(|cmd| cmd.short_keyword().map(|k| (k, cmd)))
.for_each(|(k, cmd)| {
message += &format!(" /{} => /{}", k, cmd.keyword());
});
server.notify_client(
client,
ServerGeneral::server_msg(ChatType::CommandInfo, message),