Implement /sudo command

This commit is contained in:
CapsizeGlimmer 2020-04-23 22:36:19 -04:00
parent 1831c0e20c
commit bbbe03a033
2 changed files with 329 additions and 124 deletions

View File

@ -61,6 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added waypoints next to dungeons - Added waypoints next to dungeons
- Made players spawn in towns - Made players spawn in towns
- Added non-uniform block heights - Added non-uniform block heights
- Added `/sudo` command
### Changed ### Changed

View File

@ -40,12 +40,14 @@ pub struct ChatCommand {
/// * `&mut Server` - the `Server` instance executing the command. /// * `&mut Server` - the `Server` instance executing the command.
/// * `EcsEntity` - an `Entity` corresponding to the player that invoked the /// * `EcsEntity` - an `Entity` corresponding to the player that invoked the
/// command. /// command.
/// * `EcsEntity` - an `Entity` for the player on whom the command is
/// invoked. This differs from the previous argument when using /sudo
/// * `String` - a `String` containing the part of the command after the /// * `String` - a `String` containing the part of the command after the
/// keyword. /// keyword.
/// * `&ChatCommand` - the command to execute with the above arguments. /// * `&ChatCommand` - the command to execute with the above arguments.
/// Handler functions must parse arguments from the the given `String` /// Handler functions must parse arguments from the the given `String`
/// (`scan_fmt!` is included for this purpose). /// (`scan_fmt!` is included for this purpose).
handler: fn(&mut Server, EcsEntity, String, &ChatCommand), handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand),
} }
impl ChatCommand { impl ChatCommand {
@ -55,7 +57,7 @@ impl ChatCommand {
arg_fmt: &'static str, arg_fmt: &'static str,
help_string: &'static str, help_string: &'static str,
needs_admin: bool, needs_admin: bool,
handler: fn(&mut Server, EcsEntity, String, &ChatCommand), handler: fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand),
) -> Self { ) -> Self {
Self { Self {
keyword, keyword,
@ -80,10 +82,10 @@ impl ChatCommand {
); );
return; return;
} else { } else {
(self.handler)(server, entity, args, self); (self.handler)(server, entity, entity, args, self);
} }
} else { } else {
(self.handler)(server, entity, args, self); (self.handler)(server, entity, entity, args, self);
} }
} }
} }
@ -261,79 +263,116 @@ lazy_static! {
true, true,
handle_debug, handle_debug,
), ),
ChatCommand::new(
"sudo",
"{} {} {/.*/}",
"/sudo <player> /<command> [args...] : Run command as if you were another player",
true,
handle_sudo,
),
]; ];
} }
fn handle_give(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { fn handle_give(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
_action: &ChatCommand,
) {
if let Ok(item) = assets::load_cloned(&args) { if let Ok(item) = assets::load_cloned(&args) {
server server
.state .state
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(target)
.map(|inv| inv.push(item)); .map(|inv| inv.push(item));
let _ = server let _ = server
.state .state
.ecs() .ecs()
.write_storage::<comp::InventoryUpdate>() .write_storage::<comp::InventoryUpdate>()
.insert( .insert(
entity, target,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given), comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Given),
); );
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from("Invalid item!"))); server.notify_client(client, ServerMsg::private(String::from("Invalid item!")));
} }
} }
fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_jump(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(current_pos) => { Some(current_pos) => {
server server
.state .state
.write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); .write_component(target, comp::Pos(current_pos.0 + Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position.")), ServerMsg::private(String::from("You have no position.")),
), ),
} }
} }
} }
fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_goto(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) {
if server if server
.state .state
.read_component_cloned::<comp::Pos>(entity) .read_component_cloned::<comp::Pos>(target)
.is_some() .is_some()
{ {
server server
.state .state
.write_component(entity, comp::Pos(Vec3::new(x, y, z))); .write_component(target, comp::Pos(Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position.")), ServerMsg::private(String::from("You have no position.")),
); );
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_kill(
server: &mut Server,
_client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
server server
.state .state
.ecs_mut() .ecs_mut()
.write_storage::<comp::Stats>() .write_storage::<comp::Stats>()
.get_mut(entity) .get_mut(target)
.map(|s| s.health.set_to(0, comp::HealthSource::Suicide)); .map(|s| s.health.set_to(0, comp::HealthSource::Suicide));
} }
fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_time(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let time = scan_fmt_some!(&args, action.arg_fmt, String); let time = scan_fmt_some!(&args, action.arg_fmt, String);
let new_time = match time.as_ref().map(|s| s.as_str()) { let new_time = match time.as_ref().map(|s| s.as_str()) {
Some("midnight") => NaiveTime::from_hms(0, 0, 0), Some("midnight") => NaiveTime::from_hms(0, 0, 0),
@ -349,7 +388,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
Ok(time) => time, Ok(time) => time,
Err(_) => { Err(_) => {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("'{}' is not a valid time.", n)), ServerMsg::private(format!("'{}' is not a valid time.", n)),
); );
return; return;
@ -368,7 +407,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
Some(time) => format!("It is {}", time.format("%H:%M").to_string()), Some(time) => format!("It is {}", time.format("%H:%M").to_string()),
None => String::from("Unknown Time"), None => String::from("Unknown Time"),
}; };
server.notify_client(entity, ServerMsg::private(msg)); server.notify_client(client, ServerMsg::private(msg));
return; return;
}, },
}; };
@ -377,7 +416,7 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
new_time.num_seconds_from_midnight() as f64; new_time.num_seconds_from_midnight() as f64;
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!( ServerMsg::private(format!(
"Time changed to: {}", "Time changed to: {}",
new_time.format("%H:%M").to_string() new_time.format("%H:%M").to_string()
@ -385,43 +424,55 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
); );
} }
fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_health(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) {
if let Some(stats) = server if let Some(stats) = server
.state .state
.ecs() .ecs()
.write_storage::<comp::Stats>() .write_storage::<comp::Stats>()
.get_mut(entity) .get_mut(target)
{ {
stats.health.set_to(hp, comp::HealthSource::Command); stats.health.set_to(hp, comp::HealthSource::Command);
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no health.")), ServerMsg::private(String::from("You have no health.")),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You must specify health amount!")), ServerMsg::private(String::from("You must specify health amount!")),
); );
} }
} }
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_alias(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
server server
.state .state
.ecs_mut() .ecs_mut()
.write_storage::<comp::Player>() .write_storage::<comp::Player>()
.get_mut(entity) .get_mut(target)
.map(|player| player.alias = alias); .map(|player| player.alias = alias);
// Update name on client player lists // Update name on client player lists
let ecs = server.state.ecs(); let ecs = server.state.ecs();
if let (Some(uid), Some(player)) = ( if let (Some(uid), Some(player)) = (
ecs.read_storage::<Uid>().get(entity), ecs.read_storage::<Uid>().get(target),
ecs.read_storage::<comp::Player>().get(entity), ecs.read_storage::<comp::Player>().get(target),
) { ) {
let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias( let msg = ServerMsg::PlayerListUpdate(PlayerListUpdate::Alias(
(*uid).into(), (*uid).into(),
@ -430,60 +481,72 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C
server.state.notify_registered_clients(msg); server.state.notify_registered_clients(msg);
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_tp(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>()) let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join() .join()
.find(|(_, player)| player.alias == alias) .find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity); .map(|(entity, _)| entity);
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(_pos) => match opt_player { Some(_pos) => match opt_player {
Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) { Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) {
Some(pos) => { Some(pos) => {
server.state.write_component(entity, pos); server.state.write_component(target, pos);
server.state.write_component(entity, comp::ForceUpdate); server.state.write_component(target, comp::ForceUpdate);
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)), ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)),
), ),
}, },
None => { None => {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Player '{}' not found!", alias)), ServerMsg::private(format!("Player '{}' not found!", alias)),
); );
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from(action.help_string)), ServerMsg::private(String::from(action.help_string)),
); );
}, },
}, },
None => { None => {
server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
}, },
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_spawn(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
match scan_fmt_some!(&args, action.arg_fmt, String, npc::NpcBody, String) { match scan_fmt_some!(&args, action.arg_fmt, String, npc::NpcBody, String) {
(Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount) => { (Some(opt_align), Some(npc::NpcBody(id, mut body)), opt_amount) => {
if let Some(alignment) = parse_alignment(entity, &opt_align) { if let Some(alignment) = parse_alignment(target, &opt_align) {
let amount = opt_amount let amount = opt_amount
.and_then(|a| a.parse().ok()) .and_then(|a| a.parse().ok())
.filter(|x| *x > 0) .filter(|x| *x > 0)
.unwrap_or(1) .unwrap_or(1)
.min(10); .min(10);
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let agent = let agent =
if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment { if let comp::Alignment::Owned(_) | comp::Alignment::Npc = alignment {
@ -517,7 +580,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
if let Some(uid) = server.state.ecs().uid_from_entity(new_entity) { if let Some(uid) = server.state.ecs().uid_from_entity(new_entity) {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private( ServerMsg::private(
format!("Spawned entity with ID: {}", uid).to_owned(), format!("Spawned entity with ID: {}", uid).to_owned(),
), ),
@ -525,24 +588,30 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
} }
} }
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Spawned {} entities", amount).to_owned()), ServerMsg::private(format!("Spawned {} entities", amount).to_owned()),
); );
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private("You have no position!".to_owned()), ServerMsg::private("You have no position!".to_owned()),
), ),
} }
} }
}, },
_ => { _ => {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
}, },
} }
} }
fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_players(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let players = ecs.read_storage::<comp::Player>(); let players = ecs.read_storage::<comp::Player>();
let count = players.join().count(); let count = players.join().count();
@ -560,26 +629,32 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action
s s
}); });
server.notify_client(entity, ServerMsg::private(header_message + &player_list)); server.notify_client(client, ServerMsg::private(header_message + &player_list));
} else { } else {
server.notify_client(entity, ServerMsg::private(header_message)); server.notify_client(client, ServerMsg::private(header_message));
} }
} }
fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_build(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
if server if server
.state .state
.read_storage::<comp::CanBuild>() .read_storage::<comp::CanBuild>()
.get(entity) .get(target)
.is_some() .is_some()
{ {
server server
.state .state
.ecs() .ecs()
.write_storage::<comp::CanBuild>() .write_storage::<comp::CanBuild>()
.remove(entity); .remove(target);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Toggled off build mode!")), ServerMsg::private(String::from("Toggled off build mode!")),
); );
} else { } else {
@ -587,18 +662,24 @@ fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action:
.state .state
.ecs() .ecs()
.write_storage::<comp::CanBuild>() .write_storage::<comp::CanBuild>()
.insert(entity, comp::CanBuild); .insert(target, comp::CanBuild);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Toggled on build mode!")), ServerMsg::private(String::from("Toggled on build mode!")),
); );
} }
} }
fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_help(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
for cmd in CHAT_COMMANDS.iter() { for cmd in CHAT_COMMANDS.iter() {
if !cmd.needs_admin || server.entity_is_admin(entity) { if !cmd.needs_admin || server.entity_is_admin(client) {
server.notify_client(entity, ServerMsg::private(String::from(cmd.help_string))); server.notify_client(client, ServerMsg::private(String::from(cmd.help_string)));
} }
} }
} }
@ -613,7 +694,13 @@ fn parse_alignment(owner: EcsEntity, alignment: &str) -> Option<comp::Alignment>
} }
} }
fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_killnpcs(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let mut stats = ecs.write_storage::<comp::Stats>(); let mut stats = ecs.write_storage::<comp::Stats>();
let players = ecs.read_storage::<comp::Player>(); let players = ecs.read_storage::<comp::Player>();
@ -627,23 +714,29 @@ fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _actio
} else { } else {
"No NPCs on server.".to_string() "No NPCs on server.".to_string()
}; };
server.notify_client(entity, ServerMsg::private(text)); server.notify_client(client, ServerMsg::private(text));
} }
fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { fn handle_object(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
_action: &ChatCommand,
) {
let obj_type = scan_fmt!(&args, _action.arg_fmt, String); let obj_type = scan_fmt!(&args, _action.arg_fmt, String);
let pos = server let pos = server
.state .state
.ecs() .ecs()
.read_storage::<comp::Pos>() .read_storage::<comp::Pos>()
.get(entity) .get(target)
.copied(); .copied();
let ori = server let ori = server
.state .state
.ecs() .ecs()
.read_storage::<comp::Ori>() .read_storage::<comp::Ori>()
.get(entity) .get(target)
.copied(); .copied();
/*let builder = server.state /*let builder = server.state
.create_object(pos, ori, obj_type) .create_object(pos, ori, obj_type)
@ -701,7 +794,7 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
Ok("crafting_bench") => comp::object::Body::CraftingBench, Ok("crafting_bench") => comp::object::Body::CraftingBench,
_ => { _ => {
return server.notify_client( return server.notify_client(
entity, client,
ServerMsg::private(String::from("Object not found!")), ServerMsg::private(String::from("Object not found!")),
); );
}, },
@ -723,18 +816,24 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action:
)) ))
.build(); .build();
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!( ServerMsg::private(format!(
"Spawned: {}", "Spawned: {}",
obj_str_res.unwrap_or("<Unknown object>") obj_str_res.unwrap_or("<Unknown object>")
)), )),
); );
} else { } else {
server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
} }
} }
fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_light(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (opt_r, opt_g, opt_b, opt_x, opt_y, opt_z, opt_s) = let (opt_r, opt_g, opt_b, opt_x, opt_y, opt_z, opt_s) =
scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32, f32, f32, f32); scan_fmt_some!(&args, action.arg_fmt, f32, f32, f32, f32, f32, f32, f32);
@ -756,7 +855,7 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C
.state .state
.ecs() .ecs()
.read_storage::<comp::Pos>() .read_storage::<comp::Pos>()
.get(entity) .get(target)
.copied(); .copied();
if let Some(pos) = pos { if let Some(pos) = pos {
server server
@ -767,19 +866,25 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C
.with(comp::ForceUpdate) .with(comp::ForceUpdate)
.with(light_emitter) .with(light_emitter)
.build(); .build();
server.notify_client(entity, ServerMsg::private(format!("Spawned object."))); server.notify_client(client, ServerMsg::private(format!("Spawned object.")));
} else { } else {
server.notify_client(entity, ServerMsg::private(format!("You have no position!"))); server.notify_client(client, ServerMsg::private(format!("You have no position!")));
} }
} }
fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_lantern(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let opt_s = scan_fmt_some!(&args, action.arg_fmt, f32); let opt_s = scan_fmt_some!(&args, action.arg_fmt, f32);
if server if server
.state .state
.read_storage::<comp::LightEmitter>() .read_storage::<comp::LightEmitter>()
.get(entity) .get(target)
.is_some() .is_some()
{ {
if let Some(s) = opt_s { if let Some(s) = opt_s {
@ -787,11 +892,11 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.state .state
.ecs() .ecs()
.write_storage::<comp::LightEmitter>() .write_storage::<comp::LightEmitter>()
.get_mut(entity) .get_mut(target)
{ {
light.strength = s.max(0.1).min(10.0); light.strength = s.max(0.1).min(10.0);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You adjusted flame strength.")), ServerMsg::private(String::from("You adjusted flame strength.")),
); );
} }
@ -800,9 +905,9 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.state .state
.ecs() .ecs()
.write_storage::<comp::LightEmitter>() .write_storage::<comp::LightEmitter>()
.remove(entity); .remove(target);
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You put out the lantern.")), ServerMsg::private(String::from("You put out the lantern.")),
); );
} }
@ -811,7 +916,7 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
.state .state
.ecs() .ecs()
.write_storage::<comp::LightEmitter>() .write_storage::<comp::LightEmitter>()
.insert(entity, comp::LightEmitter { .insert(target, comp::LightEmitter {
offset: Vec3::new(0.5, 0.2, 0.8), offset: Vec3::new(0.5, 0.2, 0.8),
col: Rgb::new(1.0, 0.75, 0.3), col: Rgb::new(1.0, 0.75, 0.3),
strength: if let Some(s) = opt_s { strength: if let Some(s) = opt_s {
@ -822,50 +927,68 @@ fn handle_lantern(server: &mut Server, entity: EcsEntity, args: String, action:
}); });
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("You lit your lantern.")), ServerMsg::private(String::from("You lit your lantern.")),
); );
} }
} }
fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_explosion(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let power = scan_fmt!(&args, action.arg_fmt, f32).unwrap_or(8.0); let power = scan_fmt!(&args, action.arg_fmt, f32).unwrap_or(8.0);
let ecs = server.state.ecs(); let ecs = server.state.ecs();
match server.state.read_component_cloned::<comp::Pos>(entity) { match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
ecs.read_resource::<EventBus<ServerEvent>>() ecs.read_resource::<EventBus<ServerEvent>>()
.emit_now(ServerEvent::Explosion { .emit_now(ServerEvent::Explosion {
pos: pos.0, pos: pos.0,
power, power,
owner: ecs.read_storage::<Uid>().get(entity).copied(), owner: ecs.read_storage::<Uid>().get(target).copied(),
}) })
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position!")), ServerMsg::private(String::from("You have no position!")),
), ),
} }
} }
fn handle_waypoint(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_waypoint(
match server.state.read_component_cloned::<comp::Pos>(entity) { server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
match server.state.read_component_cloned::<comp::Pos>(target) {
Some(pos) => { Some(pos) => {
let _ = server let _ = server
.state .state
.ecs() .ecs()
.write_storage::<comp::Waypoint>() .write_storage::<comp::Waypoint>()
.insert(entity, comp::Waypoint::new(pos.0)); .insert(target, comp::Waypoint::new(pos.0));
server.notify_client(entity, ServerMsg::private(String::from("Waypoint set!"))); server.notify_client(client, ServerMsg::private(String::from("Waypoint set!")));
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position!")), ServerMsg::private(String::from("You have no position!")),
), ),
} }
} }
fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_adminify(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>()) let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
@ -883,18 +1006,31 @@ fn handle_adminify(server: &mut Server, entity: EcsEntity, args: String, action:
}, },
None => { None => {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Player '{}' not found!", alias)), ServerMsg::private(format!("Player '{}' not found!", alias)),
); );
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
}, },
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_tell(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if client != target {
server.notify_client(
client,
ServerMsg::tell(String::from("It's rude to impersonate people")),
);
return;
}
if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) {
let ecs = server.state.ecs(); let ecs = server.state.ecs();
let msg = &args[alias.len()..args.len()]; let msg = &args[alias.len()..args.len()];
@ -903,11 +1039,11 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
.find(|(_, player)| player.alias == alias) .find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity) .map(|(entity, _)| entity)
{ {
if player != entity { if player != target {
if msg.len() > 1 { if msg.len() > 1 {
if let Some(name) = ecs if let Some(name) = ecs
.read_storage::<comp::Player>() .read_storage::<comp::Player>()
.get(entity) .get(target)
.map(|s| s.alias.clone()) .map(|s| s.alias.clone())
{ {
server.notify_client( server.notify_client(
@ -915,53 +1051,60 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
ServerMsg::tell(format!("[{}] tells:{}", name, msg)), ServerMsg::tell(format!("[{}] tells:{}", name, msg)),
); );
server.notify_client( server.notify_client(
entity, client,
ServerMsg::tell(format!("To [{}]:{}", alias, msg)), ServerMsg::tell(format!("To [{}]:{}", alias, msg)),
); );
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Failed to send message.")), ServerMsg::private(String::from("Failed to send message.")),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("[{}] wants to talk to you.", alias)), ServerMsg::private(format!("[{}] wants to talk to you.", alias)),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("You can't /tell yourself.")), ServerMsg::private(format!("You can't /tell yourself.")),
); );
} }
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(format!("Player '{}' not found!", alias)), ServerMsg::private(format!("Player '{}' not found!", alias)),
); );
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
#[cfg(not(feature = "worldgen"))] #[cfg(not(feature = "worldgen"))]
fn handle_debug_column( fn handle_debug_column(
server: &mut Server, server: &mut Server,
entity: EcsEntity, client: EcsEntity,
target: EcsEntity,
_args: String, _args: String,
_action: &ChatCommand, _action: &ChatCommand,
) { ) {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Unsupported without worldgen enabled")), ServerMsg::private(String::from("Unsupported without worldgen enabled")),
); );
} }
#[cfg(feature = "worldgen")] #[cfg(feature = "worldgen")]
fn handle_debug_column(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_debug_column(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let sim = server.world.sim(); let sim = server.world.sim();
let sampler = server.world.sample_columns(); let sampler = server.world.sample_columns();
if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) { if let Ok((x, y)) = scan_fmt!(&args, action.arg_fmt, i32, i32) {
@ -1020,15 +1163,15 @@ spawn_rate {:?} "#,
)) ))
}; };
if let Some(s) = foo() { if let Some(s) = foo() {
server.notify_client(entity, ServerMsg::private(s)); server.notify_client(client, ServerMsg::private(s));
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from("Not a pregenerated chunk.")), ServerMsg::private(String::from("Not a pregenerated chunk.")),
); );
} }
} else { } else {
server.notify_client(entity, ServerMsg::private(String::from(action.help_string))); server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
} }
} }
@ -1048,12 +1191,18 @@ fn find_target(
} }
} }
fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_exp(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String); let (a_exp, a_alias) = scan_fmt_some!(&args, action.arg_fmt, i64, String);
if let Some(exp) = a_exp { if let Some(exp) = a_exp {
let ecs = server.state.ecs_mut(); let ecs = server.state.ecs_mut();
let target = find_target(&ecs, a_alias, entity); let target = find_target(&ecs, a_alias, target);
let mut error_msg = None; let mut error_msg = None;
@ -1071,17 +1220,23 @@ fn handle_exp(server: &mut Server, entity: EcsEntity, args: String, action: &Cha
} }
if let Some(msg) = error_msg { if let Some(msg) = error_msg {
server.notify_client(entity, msg); server.notify_client(client, msg);
} }
} }
} }
fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { fn handle_level(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String); let (a_lvl, a_alias) = scan_fmt_some!(&args, action.arg_fmt, u32, String);
if let Some(lvl) = a_lvl { if let Some(lvl) = a_lvl {
let ecs = server.state.ecs_mut(); let ecs = server.state.ecs_mut();
let target = find_target(&ecs, a_alias, entity); let target = find_target(&ecs, a_alias, target);
let mut error_msg = None; let mut error_msg = None;
@ -1104,19 +1259,25 @@ fn handle_level(server: &mut Server, entity: EcsEntity, args: String, action: &C
} }
if let Some(msg) = error_msg { if let Some(msg) = error_msg {
server.notify_client(entity, msg); server.notify_client(client, msg);
} }
} }
} }
use common::comp::Item; use common::comp::Item;
fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { fn handle_debug(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
_args: String,
_action: &ChatCommand,
) {
if let Ok(items) = assets::load_glob::<Item>("common.items.debug.*") { if let Ok(items) = assets::load_glob::<Item>("common.items.debug.*") {
server server
.state() .state()
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(target)
// TODO: Consider writing a `load_glob_cloned` in `assets` and using that here // TODO: Consider writing a `load_glob_cloned` in `assets` and using that here
.map(|inv| inv.push_all_unique(items.iter().map(|item| item.as_ref().clone()))); .map(|inv| inv.push_all_unique(items.iter().map(|item| item.as_ref().clone())));
let _ = server let _ = server
@ -1124,12 +1285,12 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action:
.ecs() .ecs()
.write_storage::<comp::InventoryUpdate>() .write_storage::<comp::InventoryUpdate>()
.insert( .insert(
entity, target,
comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug), comp::InventoryUpdate::new(comp::InventoryUpdateEvent::Debug),
); );
} else { } else {
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from( ServerMsg::private(String::from(
"Debug items not found? Something is very broken.", "Debug items not found? Something is very broken.",
)), )),
@ -1139,12 +1300,13 @@ fn handle_debug(server: &mut Server, entity: EcsEntity, _args: String, _action:
fn handle_remove_lights( fn handle_remove_lights(
server: &mut Server, server: &mut Server,
entity: EcsEntity, client: EcsEntity,
target: EcsEntity,
args: String, args: String,
action: &ChatCommand, action: &ChatCommand,
) { ) {
let opt_radius = scan_fmt_some!(&args, action.arg_fmt, f32); let opt_radius = scan_fmt_some!(&args, action.arg_fmt, f32);
let opt_player_pos = server.state.read_component_cloned::<comp::Pos>(entity); let opt_player_pos = server.state.read_component_cloned::<comp::Pos>(target);
let mut to_delete = vec![]; let mut to_delete = vec![];
match opt_player_pos { match opt_player_pos {
@ -1168,7 +1330,7 @@ fn handle_remove_lights(
} }
}, },
None => server.notify_client( None => server.notify_client(
entity, client,
ServerMsg::private(String::from("You have no position.")), ServerMsg::private(String::from("You have no position.")),
), ),
} }
@ -1182,7 +1344,49 @@ fn handle_remove_lights(
} }
server.notify_client( server.notify_client(
entity, client,
ServerMsg::private(String::from(format!("Removed {} lights!", size))), ServerMsg::private(String::from(format!("Removed {} lights!", size))),
); );
} }
fn handle_sudo(
server: &mut Server,
client: EcsEntity,
_target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if let (Some(player_alias), Some(mut cmd), cmd_args) =
scan_fmt_some!(&args, action.arg_fmt, String, String, String)
{
let cmd_args = cmd_args.unwrap_or(String::from(""));
if cmd.chars().next() == Some('/') {
cmd.remove(0);
}
if let Some(action) = CHAT_COMMANDS.iter().find(|c| c.keyword == cmd) {
let mut entity_opt = None;
let ecs = server.state.ecs();
for (ent, player) in (&ecs.entities(), &ecs.read_storage::<comp::Player>()).join() {
if player.alias == player_alias {
entity_opt = Some(ent);
break;
}
}
if let Some(entity) = entity_opt {
(action.handler)(server, client, entity, cmd_args, action);
} else {
server.notify_client(
client,
ServerMsg::private(format!("Could not find that player")),
);
}
} else {
server.notify_client(
client,
ServerMsg::private(format!("Unknown command: /{}", cmd)),
);
}
} else {
server.notify_client(client, ServerMsg::private(String::from(action.help_string)));
}
}