diff --git a/common/src/comp/admin.rs b/common/src/comp/admin.rs new file mode 100644 index 0000000000..025fcc9bf2 --- /dev/null +++ b/common/src/comp/admin.rs @@ -0,0 +1,8 @@ +use specs::{Component, NullStorage}; + +#[derive(Default)] +pub struct Admin; + +impl Component for Admin { + type Storage = NullStorage; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index bee5458a2c..da82ee09af 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -1,4 +1,5 @@ mod action_state; +mod admin; mod agent; mod animation; mod body; @@ -13,6 +14,7 @@ mod visual; // Reexports pub use action_state::ActionState; +pub use admin::Admin; pub use agent::Agent; pub use animation::{Animation, AnimationInfo}; pub use body::{humanoid, object, quadruped, quadruped_medium, Body}; diff --git a/common/src/npc.rs b/common/src/npc.rs index b896e6f1d3..3f95c17cca 100644 --- a/common/src/npc.rs +++ b/common/src/npc.rs @@ -36,8 +36,7 @@ impl FromStr for NpcKind { } lazy_static! { - static ref NPC_NAMES_JSON: Arc = - assets::load_expect("common/npc_names.json"); + static ref NPC_NAMES_JSON: Arc = assets::load_expect("common.npc_names"); } pub fn get_npc_name(npc_type: NpcKind) -> String { diff --git a/common/src/state.rs b/common/src/state.rs index 7c38442b2f..c73a3602b6 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -145,6 +145,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Controller effects ecs.register::(); ecs.register::(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 9078851e6e..3853b0c48e 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -26,6 +26,8 @@ pub struct ChatCommand { arg_fmt: &'static str, /// A message that explains how the command is used. help_string: &'static str, + /// A boolean that is used to check whether the command requires administrator permissions or not. + needs_admin: bool, /// Handler function called when the command is executed. /// # Arguments /// * `&mut Server` - the `Server` instance executing the command. @@ -42,18 +44,32 @@ impl ChatCommand { keyword: &'static str, arg_fmt: &'static str, help_string: &'static str, + needs_admin: bool, handler: fn(&mut Server, EcsEntity, String, &ChatCommand), ) -> Self { Self { keyword, arg_fmt, help_string, + needs_admin, handler, } } /// Calls the contained handler function, passing `&self` as the last argument. pub fn execute(&self, server: &mut Server, entity: EcsEntity, args: String) { - (self.handler)(server, entity, args, self); + if self.needs_admin { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that.")), + ); + return; + } else { + (self.handler)(server, entity, args, self); + } + } else { + (self.handler)(server, entity, args, self); + } } } @@ -64,98 +80,114 @@ lazy_static! { "jump", "{d} {d} {d}", "/jump : Offset your current position", + true, handle_jump, ), ChatCommand::new( "goto", "{d} {d} {d}", "/goto : Teleport to a position", + true, handle_goto, ), ChatCommand::new( "alias", "{}", "/alias : Change your alias", + false, handle_alias, ), ChatCommand::new( "tp", "{}", "/tp : Teleport to another player", + true, handle_tp, ), ChatCommand::new( "kill", "{}", "/kill : Kill yourself", + false, handle_kill, ), ChatCommand::new( "time", "{} {s}", "/time or [Time of day] : Set the time of day", + true, handle_time, ), ChatCommand::new( "spawn", "{} {} {d}", "/spawn [amount] : Spawn a test entity", + true, handle_spawn, ), ChatCommand::new( "players", "{}", "/players : Lists players currently online", + false, handle_players, ), ChatCommand::new( - "help", "", "/help: Display this message", handle_help), + "help", "", "/help: Display this message", false, handle_help), ChatCommand::new( "health", "{}", "/health : Set your current health", + true, handle_health, ), ChatCommand::new( "build", "", "/build : Toggles build mode on and off", + true, handle_build, ), ChatCommand::new( "tell", "{}", "/tell : Send a message to another player", + false, handle_tell, ), ChatCommand::new( "killnpcs", "{}", "/killnpcs : Kill the NPCs", + true, handle_killnpcs, ), ChatCommand::new( "object", "{}", "/object [Name]: Spawn an object", + true, handle_object, ), ChatCommand::new( "light", "{} {} {} {} {} {} {}", "/light > < > <>>: Spawn entity with light", + true, handle_light, ), ChatCommand::new( "lantern", "{}", "/lantern : adds/remove light near player", + false, handle_lantern, ), ChatCommand::new( "explosion", "{}", "/explosion : Explodes the ground around you", + false, handle_explosion, ), ]; @@ -272,7 +304,7 @@ fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: & } else { server.clients.notify( entity, - ServerMsg::private(String::from("You have no position.")), + ServerMsg::private(String::from("You have no health.")), ); } } else { @@ -439,6 +471,7 @@ fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: } } +// TODO: Don't display commands that the player cannot use. fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { for cmd in CHAT_COMMANDS.iter() { server diff --git a/server/src/lib.rs b/server/src/lib.rs index b1ab1ca702..e7a44c5930 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -188,6 +188,7 @@ impl Server { client: &mut Client, name: String, body: comp::Body, + server_settings: &ServerSettings, ) { let spawn_point = state.ecs().read_resource::().0; @@ -203,6 +204,17 @@ impl Server { // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); + // Give the Admin component to the player if their name exists in admin list + if server_settings.admins.contains( + &state + .ecs() + .read_storage::() + .get(entity) + .unwrap() + .alias, + ) { + state.write_component(entity, comp::Admin); + } // Tell the client its request was successful. client.allow_state(ClientState::Character); } @@ -518,6 +530,7 @@ impl Server { let mut frontend_events = Vec::new(); let accounts = &mut self.accounts; + let server_settings = &self.server_settings; let state = &mut self.state; let mut new_chat_msgs = Vec::new(); @@ -669,7 +682,14 @@ impl Server { ClientState::Registered | ClientState::Spectator | ClientState::Dead => { - Self::create_player_character(state, entity, client, name, body); + Self::create_player_character( + state, + entity, + client, + name, + body, + &server_settings, + ); if let Some(player) = state.ecs().read_storage::().get(entity) { @@ -802,7 +822,13 @@ impl Server { } else { let message = match self.state.ecs().read_storage::().get(entity) { - Some(player) => format!("[{}] {}", &player.alias, message), + Some(player) => { + if self.entity_is_admin(entity) { + format!("[ADMIN][{}] {}", &player.alias, message) + } else { + format!("[{}] {}", &player.alias, message) + } + } None => format!("[] {}", message), }; self.clients @@ -1152,6 +1178,13 @@ impl Server { } } } + + fn entity_is_admin(&self, entity: EcsEntity) -> bool { + self.state + .read_storage::() + .get(entity) + .is_some() + } } impl Drop for Server { diff --git a/server/src/settings.rs b/server/src/settings.rs index 2ffc475f00..0897c5da0e 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -12,6 +12,7 @@ pub struct ServerSettings { pub server_description: String, //pub login_server: whatever pub start_time: f64, + pub admins: Vec, } impl Default for ServerSettings { @@ -23,6 +24,7 @@ impl Default for ServerSettings { server_description: "This is the best Veloren server.".to_owned(), max_players: 100, start_time: 9.0 * 3600.0, + admins: vec![], } } } @@ -59,6 +61,18 @@ impl ServerSettings { Ok(()) } + pub fn singleplayer() -> Self { + Self { + address: SocketAddr::from(([0; 4], 14004)), + world_seed: 1337, + server_name: "Singleplayer".to_owned(), + server_description: "The main feature is loneliness!".to_owned(), + max_players: 100, + start_time: 9.0 * 3600.0, + admins: vec!["singleplayer".to_string()], // TODO: Let the player choose if they want to use admin commands or not + } + } + fn get_settings_path() -> PathBuf { PathBuf::from(r"settings.ron") } diff --git a/voxygen/src/singleplayer.rs b/voxygen/src/singleplayer.rs index 0b294b02f6..66b6b5aaf1 100644 --- a/voxygen/src/singleplayer.rs +++ b/voxygen/src/singleplayer.rs @@ -36,7 +36,7 @@ impl Singleplayer { )); // Create server - let server = Server::bind(sock.clone(), ServerSettings::default()) + let server = Server::bind(sock.clone(), ServerSettings::singleplayer()) .expect("Failed to create server instance!"); let server = match client {