Merge branch 'qutrin/admin-perms' into 'master'

Admin Permissions system

See merge request veloren/veloren!424
This commit is contained in:
Joshua Barretto 2019-08-15 15:30:44 +00:00
commit ef81e8efdc
8 changed files with 98 additions and 8 deletions

8
common/src/comp/admin.rs Normal file
View File

@ -0,0 +1,8 @@
use specs::{Component, NullStorage};
#[derive(Default)]
pub struct Admin;
impl Component for Admin {
type Storage = NullStorage<Self>;
}

View File

@ -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};

View File

@ -36,8 +36,7 @@ impl FromStr for NpcKind {
}
lazy_static! {
static ref NPC_NAMES_JSON: Arc<serde_json::Value> =
assets::load_expect("common/npc_names.json");
static ref NPC_NAMES_JSON: Arc<serde_json::Value> = assets::load_expect("common.npc_names");
}
pub fn get_npc_name(npc_type: NpcKind) -> String {

View File

@ -145,6 +145,7 @@ impl State {
ecs.register::<comp::ForceUpdate>();
ecs.register::<comp::InventoryUpdate>();
ecs.register::<comp::Inventory>();
ecs.register::<comp::Admin>();
// Controller effects
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();

View File

@ -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 <dx> <dy> <dz> : Offset your current position",
true,
handle_jump,
),
ChatCommand::new(
"goto",
"{d} {d} {d}",
"/goto <x> <y> <z> : Teleport to a position",
true,
handle_goto,
),
ChatCommand::new(
"alias",
"{}",
"/alias <name> : Change your alias",
false,
handle_alias,
),
ChatCommand::new(
"tp",
"{}",
"/tp <alias> : Teleport to another player",
true,
handle_tp,
),
ChatCommand::new(
"kill",
"{}",
"/kill : Kill yourself",
false,
handle_kill,
),
ChatCommand::new(
"time",
"{} {s}",
"/time <XY:XY> or [Time of day] : Set the time of day",
true,
handle_time,
),
ChatCommand::new(
"spawn",
"{} {} {d}",
"/spawn <alignment> <entity> [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 <alias> <message>: 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 <opt: <<cr> <cg> <cb>> <<ox> <oy> <oz>> <<strength>>>: Spawn entity with light",
true,
handle_light,
),
ChatCommand::new(
"lantern",
"{}",
"/lantern : adds/remove light near player",
false,
handle_lantern,
),
ChatCommand::new(
"explosion",
"{}",
"/explosion <radius> : 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

View File

@ -188,6 +188,7 @@ impl Server {
client: &mut Client,
name: String,
body: comp::Body,
server_settings: &ServerSettings,
) {
let spawn_point = state.ecs().read_resource::<SpawnPoint>().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::<comp::Player>()
.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::<comp::Player>().get(entity)
{
@ -802,7 +822,13 @@ impl Server {
} else {
let message =
match self.state.ecs().read_storage::<comp::Player>().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!("[<Unknown>] {}", message),
};
self.clients
@ -1152,6 +1178,13 @@ impl Server {
}
}
}
fn entity_is_admin(&self, entity: EcsEntity) -> bool {
self.state
.read_storage::<comp::Admin>()
.get(entity)
.is_some()
}
}
impl Drop for Server {

View File

@ -12,6 +12,7 @@ pub struct ServerSettings {
pub server_description: String,
//pub login_server: whatever
pub start_time: f64,
pub admins: Vec<String>,
}
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")
}

View File

@ -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 {