Implement tab completion of enums (/object /time /spawn) and numbers

This commit is contained in:
CapsizeGlimmer
2020-05-08 22:42:51 -04:00
parent e00ca17f01
commit acd9f1bb6e
5 changed files with 273 additions and 211 deletions

View File

@ -57,17 +57,10 @@ impl From<std::io::Error> for Error {
lazy_static! { lazy_static! {
/// The HashMap where all loaded assets are stored in. /// The HashMap where all loaded assets are stored in.
static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> = pub static ref ASSETS: RwLock<HashMap<String, Arc<dyn Any + 'static + Sync + Send>>> =
RwLock::new(HashMap::new()); RwLock::new(HashMap::new());
} }
const ASSETS_TMP: [&'static str; 1] = ["common/items/lantern/black_0"];
pub fn iterate() -> impl Iterator<Item = &'static str> {
// TODO FIXME implement this
//ASSETS.read().iter().flat_map(|e| e.keys())
ASSETS_TMP.iter().map(|k| *k)
}
// TODO: Remove this function. It's only used in world/ in a really ugly way.To // TODO: Remove this function. It's only used in world/ in a really ugly way.To
// do this properly assets should have all their necessary data in one file. A // do this properly assets should have all their necessary data in one file. A
// ron file could be used to combine voxel data with positioning data for // ron file could be used to combine voxel data with positioning data for

View File

@ -1,5 +1,7 @@
use crate::{assets, comp::Player, state::State}; use crate::{assets, comp, npc, state::State};
use lazy_static::lazy_static;
use specs::prelude::{Join, WorldExt}; use specs::prelude::{Join, WorldExt};
use std::{ops::Deref, str::FromStr};
/// Struct representing a command that a user can run from server chat. /// Struct representing a command that a user can run from server chat.
pub struct ChatCommandData { pub struct ChatCommandData {
@ -85,63 +87,108 @@ pub static CHAT_COMMANDS: &'static [ChatCommand] = &[
ChatCommand::Waypoint, ChatCommand::Waypoint,
]; ];
lazy_static! {
static ref ALIGNMENTS: Vec<String> = vec!["wild", "enemy", "npc", "pet"]
.iter()
.map(|s| s.to_string())
.collect();
static ref ENTITIES: Vec<String> = {
let npc_names = &*npc::NPC_NAMES;
npc::ALL_NPCS
.iter()
.map(|&npc| npc_names[npc].keyword.clone())
.collect()
};
static ref OBJECTS: Vec<String> = comp::object::ALL_OBJECTS
.iter()
.map(|o| o.to_string().to_string())
.collect();
static ref TIMES: Vec<String> = vec![
"midnight", "night", "dawn", "morning", "day", "noon", "dusk"
]
.iter()
.map(|s| s.to_string())
.collect();
}
fn items() -> Vec<String> {
if let Ok(assets) = assets::ASSETS.read() {
assets
.iter()
.flat_map(|(k, v)| {
if v.is::<comp::item::Item>() {
Some(k.clone())
} else {
None
}
})
.collect()
} else {
error!("Assets not found");
vec![]
}
}
impl ChatCommand { impl ChatCommand {
pub fn data(&self) -> ChatCommandData { pub fn data(&self) -> ChatCommandData {
use ArgumentSpec::*; use ArgumentSpec::*;
use Requirement::*;
let cmd = ChatCommandData::new; let cmd = ChatCommandData::new;
match self { match self {
ChatCommand::Adminify => cmd( ChatCommand::Adminify => cmd(
vec![PlayerName(false)], vec![PlayerName(Required)],
"Temporarily gives a player admin permissions or removes them", "Temporarily gives a player admin permissions or removes them",
true, true,
), ),
ChatCommand::Alias => cmd(vec![Any("name", false)], "Change your alias", false), ChatCommand::Alias => cmd(vec![Any("name", Required)], "Change your alias", false),
ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", true), ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", true),
ChatCommand::Debug => cmd(vec![], "Place all debug items into your pack.", true), ChatCommand::Debug => cmd(vec![], "Place all debug items into your pack.", true),
ChatCommand::DebugColumn => cmd( ChatCommand::DebugColumn => cmd(
vec![Float("x", f32::NAN, false), Float("y", f32::NAN, false)], vec![
Integer("x", 15000, Required),
Integer("y", 15000, Required),
],
"Prints some debug information about a column", "Prints some debug information about a column",
false, false,
), ),
ChatCommand::Explosion => cmd( ChatCommand::Explosion => cmd(
vec![Float("radius", 5.0, false)], vec![Float("radius", 5.0, Required)],
"Explodes the ground around you", "Explodes the ground around you",
true, true,
), ),
ChatCommand::GiveExp => cmd( ChatCommand::GiveExp => cmd(
vec![Integer("amount", 50, false)], vec![Integer("amount", 50, Required)],
"Give experience to yourself", "Give experience to yourself",
true, true,
), ),
ChatCommand::GiveItem => cmd( ChatCommand::GiveItem => cmd(
vec![ItemSpec(false), Integer("num", 1, true)], vec![Enum("item", items(), Required), Integer("num", 1, Optional)],
"Give yourself some items", "Give yourself some items",
true, true,
), ),
ChatCommand::Goto => cmd( ChatCommand::Goto => cmd(
vec![ vec![
Float("x", 0.0, false), Float("x", 0.0, Required),
Float("y", 0.0, false), Float("y", 0.0, Required),
Float("z", 0.0, false), Float("z", 0.0, Required),
], ],
"Teleport to a position", "Teleport to a position",
true, true,
), ),
ChatCommand::Health => cmd( ChatCommand::Health => cmd(
vec![Integer("hp", 100, false)], vec![Integer("hp", 100, Required)],
"Set your current health", "Set your current health",
true, true,
), ),
ChatCommand::Help => ChatCommandData::new( ChatCommand::Help => ChatCommandData::new(
vec![Command(true)], vec![Command(Optional)],
"Display information about commands", "Display information about commands",
false, false,
), ),
ChatCommand::Jump => cmd( ChatCommand::Jump => cmd(
vec![ vec![
Float("x", 0.0, false), Float("x", 0.0, Required),
Float("y", 0.0, false), Float("y", 0.0, Required),
Float("z", 0.0, false), Float("z", 0.0, Required),
], ],
"Offset your current position", "Offset your current position",
true, true,
@ -150,50 +197,72 @@ impl ChatCommand {
ChatCommand::KillNpcs => cmd(vec![], "Kill the NPCs", true), ChatCommand::KillNpcs => cmd(vec![], "Kill the NPCs", true),
ChatCommand::Lantern => cmd( ChatCommand::Lantern => cmd(
vec![ vec![
Float("strength", 5.0, false), Float("strength", 5.0, Required),
Float("r", 1.0, true), Float("r", 1.0, Optional),
Float("g", 1.0, true), Float("g", 1.0, Optional),
Float("b", 1.0, true), Float("b", 1.0, Optional),
], ],
"Change your lantern's strength and color", "Change your lantern's strength and color",
true, true,
), ),
ChatCommand::Light => cmd( ChatCommand::Light => cmd(
vec![ vec![
Float("r", 1.0, true), Float("r", 1.0, Optional),
Float("g", 1.0, true), Float("g", 1.0, Optional),
Float("b", 1.0, true), Float("b", 1.0, Optional),
Float("x", 0.0, true), Float("x", 0.0, Optional),
Float("y", 0.0, true), Float("y", 0.0, Optional),
Float("z", 0.0, true), Float("z", 0.0, Optional),
Float("strength", 5.0, true), Float("strength", 5.0, Optional),
], ],
"Spawn entity with light", "Spawn entity with light",
true, true,
), ),
ChatCommand::Object => cmd(vec![/*TODO*/], "Spawn an object", true), ChatCommand::Object => cmd(
vec![Enum("object", OBJECTS.clone(), Required)],
"Spawn an object",
true,
),
ChatCommand::Players => cmd(vec![], "Lists players currently online", false), ChatCommand::Players => cmd(vec![], "Lists players currently online", false),
ChatCommand::RemoveLights => cmd( ChatCommand::RemoveLights => cmd(
vec![Float("radius", 20.0, true)], vec![Float("radius", 20.0, Optional)],
"Removes all lights spawned by players", "Removes all lights spawned by players",
true, true,
), ),
ChatCommand::SetLevel => { ChatCommand::SetLevel => cmd(
cmd(vec![Integer("level", 10, false)], "Set player Level", true) vec![Integer("level", 10, Required)],
}, "Set player Level",
ChatCommand::Spawn => cmd(vec![/*TODO*/], "Spawn a test entity", true), true,
),
ChatCommand::Spawn => cmd(
vec![
Enum("alignment", ALIGNMENTS.clone(), Required),
Enum("entity", ENTITIES.clone(), Required),
Integer("amount", 1, Optional),
],
"Spawn a test entity",
true,
),
ChatCommand::Sudo => cmd( ChatCommand::Sudo => cmd(
vec![PlayerName(false), SubCommand], vec![PlayerName(Required), SubCommand],
"Run command as if you were another player", "Run command as if you were another player",
true, true,
), ),
ChatCommand::Tell => cmd( ChatCommand::Tell => cmd(
vec![PlayerName(false), Message], vec![PlayerName(Required), Message],
"Send a message to another player", "Send a message to another player",
false, false,
), ),
ChatCommand::Time => cmd(vec![/*TODO*/], "Set the time of day", true), ChatCommand::Time => cmd(
ChatCommand::Tp => cmd(vec![PlayerName(true)], "Teleport to another player", true), vec![Enum("time", TIMES.clone(), Optional)],
"Set the time of day",
true,
),
ChatCommand::Tp => cmd(
vec![PlayerName(Optional)],
"Teleport to another player",
true,
),
ChatCommand::Version => cmd(vec![], "Prints server version", false), ChatCommand::Version => cmd(vec![], "Prints server version", false),
ChatCommand::Waypoint => { ChatCommand::Waypoint => {
cmd(vec![], "Set your waypoint to your current position", true) cmd(vec![], "Set your waypoint to your current position", true)
@ -250,21 +319,20 @@ impl ChatCommand {
.iter() .iter()
.map(|arg| match arg { .map(|arg| match arg {
ArgumentSpec::PlayerName(_) => "{}", ArgumentSpec::PlayerName(_) => "{}",
ArgumentSpec::ItemSpec(_) => "{}",
ArgumentSpec::Float(_, _, _) => "{}", ArgumentSpec::Float(_, _, _) => "{}",
ArgumentSpec::Integer(_, _, _) => "{d}", ArgumentSpec::Integer(_, _, _) => "{d}",
ArgumentSpec::Any(_, _) => "{}", ArgumentSpec::Any(_, _) => "{}",
ArgumentSpec::Command(_) => "{}", ArgumentSpec::Command(_) => "{}",
ArgumentSpec::Message => "{/.*/}", ArgumentSpec::Message => "{/.*/}",
ArgumentSpec::SubCommand => "{} {/.*/}", ArgumentSpec::SubCommand => "{} {/.*/}",
ArgumentSpec::OneOf(_, _, _, _) => "{}", // TODO ArgumentSpec::Enum(_, _, _) => "{}", // TODO
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" ") .join(" ")
} }
} }
impl std::str::FromStr for ChatCommand { impl FromStr for ChatCommand {
type Err = (); type Err = ();
fn from_str(keyword: &str) -> Result<ChatCommand, ()> { fn from_str(keyword: &str) -> Result<ChatCommand, ()> {
@ -282,26 +350,39 @@ impl std::str::FromStr for ChatCommand {
} }
} }
pub enum Requirement {
Required,
Optional,
}
impl Deref for Requirement {
type Target = bool;
fn deref(&self) -> &bool {
match self {
Requirement::Required => &true,
Requirement::Optional => &false,
}
}
}
/// Representation for chat command arguments /// Representation for chat command arguments
pub enum ArgumentSpec { pub enum ArgumentSpec {
/// The argument refers to a player by alias /// The argument refers to a player by alias
PlayerName(bool), PlayerName(Requirement),
/// The argument refers to an item asset by path
ItemSpec(bool),
/// The argument is a float. The associated values are /// The argument is a float. The associated values are
/// * label /// * label
/// * default tab-completion /// * suggested tab-completion
/// * whether it's optional /// * whether it's optional
Float(&'static str, f32, bool), Float(&'static str, f32, Requirement),
/// The argument is a float. The associated values are /// The argument is a float. The associated values are
/// * label /// * label
/// * default tab-completion /// * suggested tab-completion
/// * whether it's optional /// * whether it's optional
Integer(&'static str, i32, bool), Integer(&'static str, i32, Requirement),
/// The argument is any string that doesn't contain spaces /// The argument is any string that doesn't contain spaces
Any(&'static str, bool), Any(&'static str, Requirement),
/// The argument is a command name /// The argument is a command name (such as in /help)
Command(bool), Command(Requirement),
/// This is the final argument, consuming all characters until the end of /// This is the final argument, consuming all characters until the end of
/// input. /// input.
Message, Message,
@ -310,68 +391,55 @@ pub enum ArgumentSpec {
/// The argument is likely an enum. The associated values are /// The argument is likely an enum. The associated values are
/// * label /// * label
/// * Predefined string completions /// * Predefined string completions
/// * Other completion types
/// * whether it's optional /// * whether it's optional
OneOf( Enum(&'static str, Vec<String>, Requirement),
&'static str,
&'static [&'static str],
Vec<Box<ArgumentSpec>>,
bool,
),
} }
impl ArgumentSpec { impl ArgumentSpec {
pub fn usage_string(&self) -> String { pub fn usage_string(&self) -> String {
match self { match self {
ArgumentSpec::PlayerName(optional) => { ArgumentSpec::PlayerName(req) => {
if *optional { if **req {
"[player]".to_string()
} else {
"<player>".to_string() "<player>".to_string()
} else {
"[player]".to_string()
} }
}, },
ArgumentSpec::ItemSpec(optional) => { ArgumentSpec::Float(label, _, req) => {
if *optional { if **req {
"[item]".to_string()
} else {
"<item>".to_string()
}
},
ArgumentSpec::Float(label, _, optional) => {
if *optional {
format!("[{}]", label)
} else {
format!("<{}>", label) format!("<{}>", label)
} } else {
},
ArgumentSpec::Integer(label, _, optional) => {
if *optional {
format!("[{}]", label) format!("[{}]", label)
} else {
format!("<{}>", label)
} }
}, },
ArgumentSpec::Any(label, optional) => { ArgumentSpec::Integer(label, _, req) => {
if *optional { if **req {
format!("<{}>", label)
} else {
format!("[{}]", label) format!("[{}]", label)
} else {
format!("<{}>", label)
} }
}, },
ArgumentSpec::Command(optional) => { ArgumentSpec::Any(label, req) => {
if *optional { if **req {
"[[/]command]".to_string() format!("<{}>", label)
} else { } else {
format!("[{}]", label)
}
},
ArgumentSpec::Command(req) => {
if **req {
"<[/]command>".to_string() "<[/]command>".to_string()
} else {
"[[/]command]".to_string()
} }
}, },
ArgumentSpec::Message => "<message>".to_string(), ArgumentSpec::Message => "<message>".to_string(),
ArgumentSpec::SubCommand => "<[/]command> [args...]".to_string(), ArgumentSpec::SubCommand => "<[/]command> [args...]".to_string(),
ArgumentSpec::OneOf(label, _, _, optional) => { ArgumentSpec::Enum(label, _, req) => {
if *optional { if **req {
format! {"[{}]", label}
} else {
format! {"<{}>", label} format! {"<{}>", label}
} else {
format! {"[{}]", label}
} }
}, },
} }
@ -380,33 +448,35 @@ impl ArgumentSpec {
pub fn complete(&self, part: &str, state: &State) -> Vec<String> { pub fn complete(&self, part: &str, state: &State) -> Vec<String> {
match self { match self {
ArgumentSpec::PlayerName(_) => complete_player(part, &state), ArgumentSpec::PlayerName(_) => complete_player(part, &state),
ArgumentSpec::ItemSpec(_) => assets::iterate() ArgumentSpec::Float(_, x, _) => {
.filter(|asset| asset.starts_with(part)) if part.is_empty() {
.map(|c| c.to_string()) vec![format!("{:.1}", x)]
.collect(), } else {
ArgumentSpec::Float(_, x, _) => vec![format!("{}", x)], vec![]
ArgumentSpec::Integer(_, x, _) => vec![format!("{}", x)], }
},
ArgumentSpec::Integer(_, x, _) => {
if part.is_empty() {
vec![format!("{}", x)]
} else {
vec![]
}
},
ArgumentSpec::Any(_, _) => vec![], ArgumentSpec::Any(_, _) => vec![],
ArgumentSpec::Command(_) => complete_command(part), ArgumentSpec::Command(_) => complete_command(part),
ArgumentSpec::Message => complete_player(part, &state), ArgumentSpec::Message => complete_player(part, &state),
ArgumentSpec::SubCommand => complete_command(part), ArgumentSpec::SubCommand => complete_command(part),
ArgumentSpec::OneOf(_, strings, alts, _) => { ArgumentSpec::Enum(_, strings, _) => strings
let string_completions = strings
.iter() .iter()
.filter(|string| string.starts_with(part)) .filter(|string| string.starts_with(part))
.map(|c| c.to_string()); .map(|c| c.to_string())
let alt_completions = alts .collect(),
.iter()
.flat_map(|b| (*b).complete(part, &state))
.map(|c| c.to_string());
string_completions.chain(alt_completions).collect()
},
} }
} }
} }
fn complete_player(part: &str, state: &State) -> Vec<String> { fn complete_player(part: &str, state: &State) -> Vec<String> {
let storage = state.ecs().read_storage::<Player>(); let storage = state.ecs().read_storage::<comp::Player>();
let mut iter = storage.join(); let mut iter = storage.join();
if let Some(first) = iter.next() { if let Some(first) = iter.next() {
std::iter::once(first) std::iter::once(first)

View File

@ -65,7 +65,7 @@ impl Body {
} }
} }
const ALL_OBJECTS: [Body; 52] = [ pub const ALL_OBJECTS: [Body; 53] = [
Body::Arrow, Body::Arrow,
Body::Bomb, Body::Bomb,
Body::Scarecrow, Body::Scarecrow,
@ -114,8 +114,69 @@ const ALL_OBJECTS: [Body; 52] = [
Body::CarpetHumanSquare, Body::CarpetHumanSquare,
Body::CarpetHumanSquare2, Body::CarpetHumanSquare2,
Body::CarpetHumanSquircle, Body::CarpetHumanSquircle,
Body::Pouch,
Body::CraftingBench, Body::CraftingBench,
Body::BoltFire, Body::BoltFire,
Body::BoltFireBig, Body::BoltFireBig,
Body::ArrowSnake, Body::ArrowSnake,
]; ];
impl Body {
pub fn to_string(&self) -> &str {
match self {
Body::Arrow => "arrow",
Body::Bomb => "bomb",
Body::Scarecrow => "scarecrow",
Body::Cauldron => "cauldron",
Body::ChestVines => "chest_vines",
Body::Chest => "chest",
Body::ChestDark => "chest_dark",
Body::ChestDemon => "chest_demon",
Body::ChestGold => "chest_gold",
Body::ChestLight => "chest_light",
Body::ChestOpen => "chest_open",
Body::ChestSkull => "chest_skull",
Body::Pumpkin => "pumpkin",
Body::Pumpkin2 => "pumpkin_2",
Body::Pumpkin3 => "pumpkin_3",
Body::Pumpkin4 => "pumpkin_4",
Body::Pumpkin5 => "pumpkin_5",
Body::Campfire => "campfire",
Body::CampfireLit => "campfire_lit",
Body::LanternGround => "lantern_ground",
Body::LanternGroundOpen => "lantern_ground_open",
Body::LanternStanding => "lantern_standing",
Body::LanternStanding2 => "lantern_standing_2",
Body::PotionRed => "potion_red",
Body::PotionBlue => "potion_blue",
Body::PotionGreen => "potion_green",
Body::Crate => "crate",
Body::Tent => "tent",
Body::WindowSpooky => "window_spooky",
Body::DoorSpooky => "door_spooky",
Body::Anvil => "anvil",
Body::Gravestone => "gravestone",
Body::Gravestone2 => "gravestone_2",
Body::Bench => "bench",
Body::Chair => "chair",
Body::Chair2 => "chair_2",
Body::Chair3 => "chair_3",
Body::Table => "table",
Body::Table2 => "table_2",
Body::Table3 => "table_3",
Body::Drawer => "drawer",
Body::BedBlue => "bed_blue",
Body::Carpet => "carpet",
Body::Bedroll => "bedroll",
Body::CarpetHumanRound => "carpet_human_round",
Body::CarpetHumanSquare => "carpet_human_square",
Body::CarpetHumanSquare2 => "carpet_human_square_2",
Body::CarpetHumanSquircle => "carpet_human_squircle",
Body::Pouch => "pouch",
Body::CraftingBench => "crafting_bench",
Body::BoltFire => "bolt_fire",
Body::BoltFireBig => "bolt_fire_big",
Body::ArrowSnake => "arrow_snake",
}
}
}

View File

@ -78,6 +78,7 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler {
ChatCommand::Waypoint => handle_waypoint, ChatCommand::Waypoint => handle_waypoint,
} }
} }
fn handle_give_item( fn handle_give_item(
server: &mut Server, server: &mut Server,
client: EcsEntity, client: EcsEntity,
@ -616,68 +617,16 @@ fn handle_object(
.with(ori);*/ .with(ori);*/
if let (Some(pos), Some(ori)) = (pos, ori) { if let (Some(pos), Some(ori)) = (pos, ori) {
let obj_str_res = obj_type.as_ref().map(String::as_str); let obj_str_res = obj_type.as_ref().map(String::as_str);
let obj_type = match obj_str_res { if let Some(obj_type) = comp::object::ALL_OBJECTS
Ok("scarecrow") => comp::object::Body::Scarecrow, .iter()
Ok("cauldron") => comp::object::Body::Cauldron, .find(|o| Ok(o.to_string()) == obj_str_res)
Ok("chest_vines") => comp::object::Body::ChestVines, {
Ok("chest") => comp::object::Body::Chest,
Ok("chest_dark") => comp::object::Body::ChestDark,
Ok("chest_demon") => comp::object::Body::ChestDemon,
Ok("chest_gold") => comp::object::Body::ChestGold,
Ok("chest_light") => comp::object::Body::ChestLight,
Ok("chest_open") => comp::object::Body::ChestOpen,
Ok("chest_skull") => comp::object::Body::ChestSkull,
Ok("pumpkin") => comp::object::Body::Pumpkin,
Ok("pumpkin_2") => comp::object::Body::Pumpkin2,
Ok("pumpkin_3") => comp::object::Body::Pumpkin3,
Ok("pumpkin_4") => comp::object::Body::Pumpkin4,
Ok("pumpkin_5") => comp::object::Body::Pumpkin5,
Ok("campfire") => comp::object::Body::Campfire,
Ok("campfire_lit") => comp::object::Body::CampfireLit,
Ok("lantern_ground") => comp::object::Body::LanternGround,
Ok("lantern_ground_open") => comp::object::Body::LanternGroundOpen,
Ok("lantern_2") => comp::object::Body::LanternStanding2,
Ok("lantern") => comp::object::Body::LanternStanding,
Ok("potion_blue") => comp::object::Body::PotionBlue,
Ok("potion_green") => comp::object::Body::PotionGreen,
Ok("potion_red") => comp::object::Body::PotionRed,
Ok("crate") => comp::object::Body::Crate,
Ok("tent") => comp::object::Body::Tent,
Ok("bomb") => comp::object::Body::Bomb,
Ok("window_spooky") => comp::object::Body::WindowSpooky,
Ok("door_spooky") => comp::object::Body::DoorSpooky,
Ok("carpet") => comp::object::Body::Carpet,
Ok("table_human") => comp::object::Body::Table,
Ok("table_human_2") => comp::object::Body::Table2,
Ok("table_human_3") => comp::object::Body::Table3,
Ok("drawer") => comp::object::Body::Drawer,
Ok("bed_human_blue") => comp::object::Body::BedBlue,
Ok("anvil") => comp::object::Body::Anvil,
Ok("gravestone") => comp::object::Body::Gravestone,
Ok("gravestone_2") => comp::object::Body::Gravestone2,
Ok("chair") => comp::object::Body::Chair,
Ok("chair_2") => comp::object::Body::Chair2,
Ok("chair_3") => comp::object::Body::Chair3,
Ok("bench_human") => comp::object::Body::Bench,
Ok("bedroll") => comp::object::Body::Bedroll,
Ok("carpet_human_round") => comp::object::Body::CarpetHumanRound,
Ok("carpet_human_square") => comp::object::Body::CarpetHumanSquare,
Ok("carpet_human_square_2") => comp::object::Body::CarpetHumanSquare2,
Ok("carpet_human_squircle") => comp::object::Body::CarpetHumanSquircle,
Ok("crafting_bench") => comp::object::Body::CraftingBench,
_ => {
return server.notify_client(
client,
ServerMsg::private(String::from("Object not found!")),
);
},
};
server server
.state .state
.create_object(pos, obj_type) .create_object(pos, *obj_type)
.with(comp::Ori( .with(comp::Ori(
// converts player orientation into a 90° rotation for the object by using the axis // converts player orientation into a 90° rotation for the object by using the
// with the highest value // axis with the highest value
Dir::from_unnormalized(ori.0.map(|e| { Dir::from_unnormalized(ori.0.map(|e| {
if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() { if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() {
e e
@ -695,6 +644,12 @@ fn handle_object(
obj_str_res.unwrap_or("<Unknown object>") obj_str_res.unwrap_or("<Unknown object>")
)), )),
); );
} else {
return server.notify_client(
client,
ServerMsg::private(String::from("Object not found!")),
);
}
} else { } else {
server.notify_client(client, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
} }

View File

@ -29,8 +29,6 @@ widget_ids! {
} }
const MAX_MESSAGES: usize = 100; const MAX_MESSAGES: usize = 100;
// Maximum completions shown at once
const MAX_COMPLETIONS: usize = 10;
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Chat<'a> { pub struct Chat<'a> {
@ -456,21 +454,6 @@ fn do_tab_completion(cursor: usize, input: &str, word: &str) -> (String, usize)
} }
} }
fn cursor_index_to_offset(
index: text::cursor::Index,
text: &str,
ui: &Ui,
fonts: &ConrodVoxygenFonts,
) -> Option<usize> {
// Width and font must match that of the chat TextEdit
let width = 460.0;
let font = ui.fonts.get(fonts.opensans.conrod_id)?;
let font_size = fonts.opensans.scale(15);
let infos = text::line::infos(&text, &font, font_size).wrap_by_whitespace(width);
text::glyph::index_after_cursor(infos, index)
}
fn cursor_offset_to_index( fn cursor_offset_to_index(
offset: usize, offset: usize,
text: &str, text: &str,