Add MakeNPC admin command

This commit is contained in:
juliancoffee 2021-08-15 14:45:50 +03:00
parent e3798e5d2a
commit e9dde3ecec
3 changed files with 55 additions and 18 deletions

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
assets, assets,
comp::{self, buff::BuffKind, inventory::item::try_all_item_defs, AdminRole as Role, Skill}, comp::{self, buff::BuffKind, inventory::item::try_all_item_defs, AdminRole as Role, Skill},
generation::try_all_entity_configs,
npc, terrain, npc, terrain,
}; };
use assets::AssetExt; use assets::AssetExt;
@ -76,6 +77,7 @@ pub enum ChatCommand {
Lantern, Lantern,
Light, Light,
MakeBlock, MakeBlock,
MakeNpc,
MakeSprite, MakeSprite,
Motd, Motd,
Object, Object,
@ -242,6 +244,15 @@ lazy_static! {
items items
}; };
/// List of all entity configs. Useful for tab completing
static ref ENTITY_CONFIGS: Vec<String> = {
try_all_entity_configs()
.unwrap_or_else(|e| {
warn!(?e, "Failed to load entity configs");
Vec::new()
})
};
static ref KITS: Vec<String> = { static ref KITS: Vec<String> = {
if let Ok(kits) = KitManifest::load(KIT_MANIFEST_PATH) { if let Ok(kits) = KitManifest::load(KIT_MANIFEST_PATH) {
kits.read().0.keys().cloned().collect() kits.read().0.keys().cloned().collect()
@ -461,6 +472,14 @@ impl ChatCommand {
"Make a block at your location with a color", "Make a block at your location with a color",
Some(Admin), Some(Admin),
), ),
ChatCommand::MakeNpc => cmd(
vec![
Enum("entity_config", ENTITY_CONFIGS.clone(), Required),
Integer("num", 1, Optional),
],
"Spawn entity from config near you",
Some(Admin),
),
ChatCommand::MakeSprite => cmd( ChatCommand::MakeSprite => cmd(
vec![Enum("sprite", SPRITE_KINDS.clone(), Required)], vec![Enum("sprite", SPRITE_KINDS.clone(), Required)],
"Make a sprite at your location", "Make a sprite at your location",
@ -521,8 +540,8 @@ impl ChatCommand {
"Set the server description", "Set the server description",
Some(Admin), Some(Admin),
), ),
// Uses Message because site names can contain spaces, which would be assumed to be // Uses Message because site names can contain spaces,
// separators otherwise // which would be assumed to be separators otherwise
ChatCommand::Site => cmd( ChatCommand::Site => cmd(
vec![Message(Required)], vec![Message(Required)],
"Teleport to a site", "Teleport to a site",
@ -634,6 +653,7 @@ impl ChatCommand {
ChatCommand::Lantern => "lantern", ChatCommand::Lantern => "lantern",
ChatCommand::Light => "light", ChatCommand::Light => "light",
ChatCommand::MakeBlock => "make_block", ChatCommand::MakeBlock => "make_block",
ChatCommand::MakeNpc => "make_npc",
ChatCommand::MakeSprite => "make_sprite", ChatCommand::MakeSprite => "make_sprite",
ChatCommand::Motd => "motd", ChatCommand::Motd => "motd",
ChatCommand::Object => "object", ChatCommand::Object => "object",
@ -788,7 +808,6 @@ pub enum ArgumentSpec {
} }
impl ArgumentSpec { impl ArgumentSpec {
#[allow(clippy::nonstandard_macro_braces)] //tmp as of false positive !?
pub fn usage_string(&self) -> String { pub fn usage_string(&self) -> String {
match self { match self {
ArgumentSpec::PlayerName(req) => { ArgumentSpec::PlayerName(req) => {
@ -836,9 +855,9 @@ impl ArgumentSpec {
ArgumentSpec::SubCommand => "<[/]command> [args...]".to_string(), ArgumentSpec::SubCommand => "<[/]command> [args...]".to_string(),
ArgumentSpec::Enum(label, _, req) => { ArgumentSpec::Enum(label, _, req) => {
if &Requirement::Required == req { if &Requirement::Required == req {
format! {"<{}>", label} format!("<{}>", label)
} else { } else {
format! {"[{}]", label} format!("[{}]", label)
} }
}, },
ArgumentSpec::Boolean(label, _, req) => { ArgumentSpec::Boolean(label, _, req) => {

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
assets::{self, AssetExt}, assets::{self, AssetExt, Error},
comp::{ comp::{
self, agent, humanoid, self, agent, humanoid,
inventory::loadout_builder::{ItemSpec, LoadoutBuilder}, inventory::loadout_builder::{ItemSpec, LoadoutBuilder},
@ -140,6 +140,12 @@ impl EntityConfig {
} }
} }
/// Return all entity config specifiers
pub fn try_all_entity_configs() -> Result<Vec<String>, Error> {
let configs = assets::load_dir::<EntityConfig>("common.entity", true)?;
Ok(configs.ids().map(|id| id.to_owned()).collect())
}
#[derive(Clone)] #[derive(Clone)]
pub struct EntityInfo { pub struct EntityInfo {
pub pos: Vec3<f32>, pub pos: Vec3<f32>,
@ -554,10 +560,10 @@ mod tests {
#[test] #[test]
fn test_all_entity_assets() { fn test_all_entity_assets() {
// It just load everything that could // Get list of entity configs, load everything, validate content.
let entity_configs = assets::load_dir::<EntityConfig>("common.entity", true) let entity_configs =
.expect("Failed to access entity directory"); try_all_entity_configs().expect("Failed to access entity configs directory");
for config_asset in entity_configs.ids() { for config_asset in entity_configs {
// print asset name so we don't need to find errors everywhere // print asset name so we don't need to find errors everywhere
// it'll be ignored by default so you'll see it only in case of errors // it'll be ignored by default so you'll see it only in case of errors
// //
@ -568,7 +574,7 @@ mod tests {
// 2) Add try_from_asset() for LoadoutBuilder and // 2) Add try_from_asset() for LoadoutBuilder and
// SkillSet builder which will return Result and we will happily // SkillSet builder which will return Result and we will happily
// panic in validate_meta() with the name of config_asset // panic in validate_meta() with the name of config_asset
println!("{}:", config_asset); println!("{}:", &config_asset);
let EntityConfig { let EntityConfig {
hands, hands,
@ -577,12 +583,12 @@ mod tests {
loot, loot,
meta, meta,
alignment: _alignment, // can't fail if serialized, it's a boring enum alignment: _alignment, // can't fail if serialized, it's a boring enum
} = EntityConfig::from_asset_expect(config_asset); } = EntityConfig::from_asset_expect(&config_asset);
validate_hands(hands, config_asset); validate_hands(hands, &config_asset);
validate_body_and_name(body, name, config_asset); validate_body_and_name(body, name, &config_asset);
validate_loot(loot, config_asset); validate_loot(loot, &config_asset);
validate_meta(meta, config_asset); validate_meta(meta, &config_asset);
} }
} }
} }

View File

@ -1,6 +1,6 @@
//! # Implementing new commands. //! # Implementing new commands.
//! To implement a new command, add an instance of `ChatCommand` to //! To implement a new command provide a handler function
//! `CHAT_COMMANDS` and provide a handler function. //! in [do_command].
use crate::{ use crate::{
settings::{ settings::{
@ -68,6 +68,7 @@ impl ChatCommandExt for ChatCommand {
} }
type CmdResult<T> = Result<T, String>; type CmdResult<T> = Result<T, String>;
/// Handler function called when the command is executed. /// Handler function called when the command is executed.
/// # Arguments /// # Arguments
/// * `&mut Server` - the `Server` instance executing the command. /// * `&mut Server` - the `Server` instance executing the command.
@ -140,6 +141,7 @@ fn do_command(
ChatCommand::Lantern => handle_lantern, ChatCommand::Lantern => handle_lantern,
ChatCommand::Light => handle_light, ChatCommand::Light => handle_light,
ChatCommand::MakeBlock => handle_make_block, ChatCommand::MakeBlock => handle_make_block,
ChatCommand::MakeNpc => handle_make_npc,
ChatCommand::MakeSprite => handle_make_sprite, ChatCommand::MakeSprite => handle_make_sprite,
ChatCommand::Motd => handle_motd, ChatCommand::Motd => handle_motd,
ChatCommand::Object => handle_object, ChatCommand::Object => handle_object,
@ -547,6 +549,16 @@ fn handle_make_block(
} }
} }
fn handle_make_npc(
_server: &mut Server,
_client: EcsEntity,
_target: EcsEntity,
_args: Vec<String>,
_action: &ChatCommand,
) -> CmdResult<()> {
Err("Not implemented".to_owned())
}
fn handle_make_sprite( fn handle_make_sprite(
server: &mut Server, server: &mut Server,
_client: EcsEntity, _client: EcsEntity,