diff --git a/common/src/cmd.rs b/common/src/cmd.rs index e53c82609e..0e2ac0b221 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -68,6 +68,7 @@ pub enum ChatCommand { MakeSprite, Motd, Object, + PermitBuild, Players, Region, RemoveLights, @@ -122,6 +123,7 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[ ChatCommand::MakeSprite, ChatCommand::Motd, ChatCommand::Object, + ChatCommand::PermitBuild, ChatCommand::Players, ChatCommand::Region, ChatCommand::RemoveLights, @@ -235,7 +237,7 @@ impl ChatCommand { "Ban a player with a given username", Admin, ), - ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", Admin), + ChatCommand::Build => cmd(vec![], "Toggles build mode on and off", NoAdmin), ChatCommand::Campfire => cmd(vec![], "Spawns a campfire", Admin), ChatCommand::Debug => cmd(vec![], "Place all debug items into your pack.", Admin), ChatCommand::DebugColumn => cmd( @@ -368,6 +370,19 @@ impl ChatCommand { "Spawn an object", Admin, ), + ChatCommand::PermitBuild => cmd( + vec![ + PlayerName(Required), + Integer("xlo", 0, Required), + Integer("xhi", 10, Required), + Integer("ylo", 0, Required), + Integer("yhi", 10, Required), + Integer("ylo", 0, Required), + Integer("yhi", 10, Required), + ], + "Grants player a bounded box they can build in", + Admin, + ), ChatCommand::Players => cmd(vec![], "Lists players currently online", NoAdmin), ChatCommand::RemoveLights => cmd( vec![Float("radius", 20.0, Optional)], @@ -488,6 +503,7 @@ impl ChatCommand { ChatCommand::MakeSprite => "make_sprite", ChatCommand::Motd => "motd", ChatCommand::Object => "object", + ChatCommand::PermitBuild => "permit_build", ChatCommand::Players => "players", ChatCommand::Region => "region", ChatCommand::RemoveLights => "remove_lights", diff --git a/common/src/comp/inputs.rs b/common/src/comp/inputs.rs index 89e2b0b8b2..2590aef4b6 100644 --- a/common/src/comp/inputs.rs +++ b/common/src/comp/inputs.rs @@ -1,8 +1,11 @@ use serde::{Deserialize, Serialize}; -use specs::{Component, DerefFlaggedStorage, NullStorage}; +use specs::{Component, DerefFlaggedStorage}; +use specs_idvs::IdvStorage; #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] -pub struct CanBuild; -impl Component for CanBuild { - type Storage = DerefFlaggedStorage>; +pub struct CanBuild { + pub building_is_on: bool, +} +impl Component for CanBuild { + type Storage = DerefFlaggedStorage>; } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index c74c4005fb..f1e7deff02 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -109,6 +109,7 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler { ChatCommand::MakeSprite => handle_make_sprite, ChatCommand::Motd => handle_motd, ChatCommand::Object => handle_object, + ChatCommand::PermitBuild => handle_permit_build, ChatCommand::Players => handle_players, ChatCommand::Region => handle_region, ChatCommand::RemoveLights => handle_remove_lights, @@ -1116,6 +1117,76 @@ fn handle_safezone( } } +fn handle_permit_build( + server: &mut Server, + client: EcsEntity, + _target: EcsEntity, + args: String, + action: &ChatCommand, +) { + if let (Some(target_alias), xlo_opt, xhi_opt, ylo_opt, yhi_opt, zlo_opt, zhi_opt) = scan_fmt_some!( + &args, + &action.arg_fmt(), + String, + i32, + i32, + i32, + i32, + i32, + i32 + ) { + let ecs = server.state.ecs(); + let target_player_opt = (&ecs.entities(), &ecs.read_storage::()) + .join() + .find(|(_, player)| player.alias == target_alias) + .map(|(entity, _)| entity); + + if let Some(target_player) = target_player_opt { + if server + .state + .read_storage::() + .get(target_player) + .is_some() + { + ecs.write_storage::().remove(target_player); + server.notify_client( + client, + ServerGeneral::server_msg( + ChatType::CommandInfo, + format!("Removed {}'s permission to build", target_alias), + ), + ); + } else { + let _ = + ecs.write_storage::() + .insert(target_player, comp::CanBuild { + building_is_on: false, + }); + server.notify_client( + client, + ServerGeneral::server_msg( + ChatType::CommandInfo, + format!("Gave {} permission to build", target_alias), + ), + ); + } + } else { + server.notify_client( + client, + ServerGeneral::server_msg( + ChatType::CommandError, + format!("Player '{}' not found!", target_alias), + ), + ); + } + } else { + server.notify_client( + client, + ServerGeneral::server_msg(ChatType::CommandError, action.help_string()), + ); + } +} + fn handle_players( server: &mut Server, client: EcsEntity, @@ -1150,30 +1221,32 @@ fn handle_build( _args: String, _action: &ChatCommand, ) { - if server + if let Some(mut can_build) = server .state - .read_storage::() - .get(target) - .is_some() + .ecs() + .write_storage::() + .get_mut(target) { - server - .state - .ecs() - .write_storage::() - .remove(target); - server.notify_client( - client, - ServerGeneral::server_msg(ChatType::CommandInfo, "Toggled off build mode!"), - ); + if can_build.building_is_on { + can_build.building_is_on = false; + server.notify_client( + client, + ServerGeneral::server_msg(ChatType::CommandInfo, "Toggled off build mode!"), + ); + } else { + can_build.building_is_on = true; + server.notify_client( + client, + ServerGeneral::server_msg(ChatType::CommandInfo, "Toggled on build mode!"), + ); + } } else { - let _ = server - .state - .ecs() - .write_storage::() - .insert(target, comp::CanBuild); server.notify_client( client, - ServerGeneral::server_msg(ChatType::CommandInfo, "Toggled on build mode!"), + ServerGeneral::server_msg( + ChatType::CommandInfo, + "You do not have permission to build.", + ), ); } } diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index bcd2f3b90c..8dec535bef 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -102,13 +102,19 @@ impl Sys { } }, ClientGeneral::BreakBlock(pos) => { - if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) { - block_changes.set(pos, block.into_vacant()); + if let Some(comp_can_build) = can_build.get(entity) { + if comp_can_build.building_is_on { + if let Some(block) = terrain.get(pos).ok() { + block_changes.set(pos, block.into_vacant()); + } + } } }, ClientGeneral::PlaceBlock(pos, block) => { - if can_build.get(entity).is_some() { - block_changes.try_set(pos, block); + if let Some(comp_can_build) = can_build.get(entity) { + if comp_can_build.building_is_on { + block_changes.try_set(pos, block); + } } }, ClientGeneral::UnlockSkill(skill) => { diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 9c403a42e0..c20a66e555 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -317,7 +317,10 @@ impl PlayState for SessionState { .state() .read_storage::() .get(player_entity) - .is_some(); + .unwrap_or_else(|| &comp::CanBuild { + building_is_on: false, + }) + .building_is_on; let is_mining = self .client