From 7fa5403aebc82b4ec484270e20d308a04378a5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Korg=C3=B3l?= Date: Fri, 9 Aug 2019 16:22:59 +0200 Subject: [PATCH 1/5] Add AdminPerms component, make some commands require it --- common/src/comp/admin.rs | 7 + common/src/comp/mod.rs | 2 + common/src/state.rs | 1 + server/src/cmd.rs | 754 +++++++++++++++++++++------------------ 4 files changed, 408 insertions(+), 356 deletions(-) create mode 100644 common/src/comp/admin.rs diff --git a/common/src/comp/admin.rs b/common/src/comp/admin.rs new file mode 100644 index 0000000000..fd4c30c5fb --- /dev/null +++ b/common/src/comp/admin.rs @@ -0,0 +1,7 @@ +use specs::{Component, VecStorage}; + +pub struct AdminPerms; + +impl Component for AdminPerms { + type Storage = VecStorage; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index bee5458a2c..7fd9c132fc 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -10,6 +10,7 @@ mod phys; mod player; mod stats; mod visual; +mod admin; // Reexports pub use action_state::ActionState; @@ -26,3 +27,4 @@ pub use phys::{ForceUpdate, Ori, Pos, Scale, Vel}; pub use player::Player; pub use stats::{Dying, Exp, HealthSource, Level, Stats}; pub use visual::LightEmitter; +pub use admin::AdminPerms; diff --git a/common/src/state.rs b/common/src/state.rs index 7c38442b2f..d6b4dbdace 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..7d4d51e07d 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -161,44 +161,65 @@ lazy_static! { ]; } +fn is_admin(server: &mut Server, entity: EcsEntity) -> bool { + if server + .state + .read_storage::() + .get(entity) + .is_some() + { + true + } else { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permissions to do that")), + ); + false + } +} + fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { - match server.state.read_component_cloned::(entity) { - Some(current_pos) => { - server - .state - .write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); - server.state.write_component(entity, comp::ForceUpdate); + if is_admin(server, entity) { + if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { + match server.state.read_component_cloned::(entity) { + Some(current_pos) => { + server + .state + .write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); + server.state.write_component(entity, comp::ForceUpdate); + } + None => server.clients.notify( + entity, + ServerMsg::private(String::from("You have no position!")), + ), } - None => server.clients.notify( - entity, - ServerMsg::private(String::from("You have no position!")), - ), } } } fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { - if server - .state - .read_component_cloned::(entity) - .is_some() - { - server + if is_admin(server, entity) { + if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { + if server .state - .write_component(entity, comp::Pos(Vec3::new(x, y, z))); - server.state.write_component(entity, comp::ForceUpdate); + .read_component_cloned::(entity) + .is_some() + { + server + .state + .write_component(entity, comp::Pos(Vec3::new(x, y, z))); + server.state.write_component(entity, comp::ForceUpdate); + } else { + server.clients.notify( + entity, + ServerMsg::private(String::from("You don't have a position!")), + ); + } } else { - server.clients.notify( - entity, - ServerMsg::private(String::from("You don't have a position!")), - ); + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); } - } else { - server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); } } @@ -212,74 +233,78 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: & } fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - let time = scan_fmt_some!(&args, action.arg_fmt, String); - let new_time = match time.as_ref().map(|s| s.as_str()) { - Some("night") => NaiveTime::from_hms(0, 0, 0), - Some("dawn") => NaiveTime::from_hms(5, 0, 0), - Some("day") => NaiveTime::from_hms(12, 0, 0), - Some("dusk") => NaiveTime::from_hms(17, 0, 0), - Some(n) => match n.parse() { - Ok(n) => n, - Err(_) => match NaiveTime::parse_from_str(n, "%H:%M") { - Ok(time) => time, - Err(_) => { - server.clients.notify( - entity, - ServerMsg::private(format!("'{}' is not a valid time.", n)), - ); - return; - } + if is_admin(server, entity) { + let time = scan_fmt_some!(&args, action.arg_fmt, String); + let new_time = match time.as_ref().map(|s| s.as_str()) { + Some("night") => NaiveTime::from_hms(0, 0, 0), + Some("dawn") => NaiveTime::from_hms(5, 0, 0), + Some("day") => NaiveTime::from_hms(12, 0, 0), + Some("dusk") => NaiveTime::from_hms(17, 0, 0), + Some(n) => match n.parse() { + Ok(n) => n, + Err(_) => match NaiveTime::parse_from_str(n, "%H:%M") { + Ok(time) => time, + Err(_) => { + server.clients.notify( + entity, + ServerMsg::private(format!("'{}' is not a valid time.", n)), + ); + return; + } + }, }, - }, - None => { - let time_in_seconds = server.state.ecs_mut().read_resource::().0; + None => { + let time_in_seconds = server.state.ecs_mut().read_resource::().0; - let current_time = NaiveTime::from_num_seconds_from_midnight_opt( - // Wraps around back to 0s if it exceeds 24 hours (24 hours = 86400s) - (time_in_seconds as u64 % 86400) as u32, - 0, - ); - let msg = match current_time { - Some(time) => format!("Current time is: {}", time.format("%H:%M").to_string()), - None => String::from("Unknown Time"), - }; - server.clients.notify(entity, ServerMsg::private(msg)); - return; - } - }; + let current_time = NaiveTime::from_num_seconds_from_midnight_opt( + // Wraps around back to 0s if it exceeds 24 hours (24 hours = 86400s) + (time_in_seconds as u64 % 86400) as u32, + 0, + ); + let msg = match current_time { + Some(time) => format!("Current time is: {}", time.format("%H:%M").to_string()), + None => String::from("Unknown Time"), + }; + server.clients.notify(entity, ServerMsg::private(msg)); + return; + } + }; - server.state.ecs_mut().write_resource::().0 = - new_time.num_seconds_from_midnight() as f64; + server.state.ecs_mut().write_resource::().0 = + new_time.num_seconds_from_midnight() as f64; - server.clients.notify( - entity, - ServerMsg::private(format!( - "Time changed to: {}", - new_time.format("%H:%M").to_string() - )), - ); + server.clients.notify( + entity, + ServerMsg::private(format!( + "Time changed to: {}", + new_time.format("%H:%M").to_string() + )), + ); + } } fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { - if let Some(stats) = server - .state - .ecs_mut() - .write_storage::() - .get_mut(entity) - { - stats.health.set_to(hp, comp::HealthSource::Command); + if is_admin(server, entity) { + if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { + if let Some(stats) = server + .state + .ecs_mut() + .write_storage::() + .get_mut(entity) + { + stats.health.set_to(hp, comp::HealthSource::Command); + } else { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no health.")), + ); + } } else { server.clients.notify( entity, - ServerMsg::private(String::from("You have no position.")), + ServerMsg::private(String::from("You must specify health amount!")), ); } - } else { - server.clients.notify( - entity, - ServerMsg::private(String::from("You must specify health amount!")), - ); } } @@ -299,89 +324,98 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C } fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { - let ecs = server.state.ecs(); - let opt_player = (&ecs.entities(), &ecs.read_storage::()) - .join() - .find(|(_, player)| player.alias == alias) - .map(|(entity, _)| entity); - match server.state.read_component_cloned::(entity) { - Some(_pos) => match opt_player { - Some(player) => match server.state.read_component_cloned::(player) { - Some(pos) => { - server.state.write_component(entity, pos); - server.state.write_component(entity, comp::ForceUpdate); + if is_admin(server, entity) { + if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { + let ecs = server.state.ecs(); + let opt_player = (&ecs.entities(), &ecs.read_storage::()) + .join() + .find(|(_, player)| player.alias == alias) + .map(|(entity, _)| entity); + match server.state.read_component_cloned::(entity) { + Some(_pos) => match opt_player { + Some(player) => match server.state.read_component_cloned::(player) { + Some(pos) => { + server.state.write_component(entity, pos); + server.state.write_component(entity, comp::ForceUpdate); + } + None => server.clients.notify( + entity, + ServerMsg::private(format!( + "Unable to teleport to player '{}'!", + alias + )), + ), + }, + None => { + server.clients.notify( + entity, + ServerMsg::private(format!("Player '{}' not found!", alias)), + ); + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); } - None => server.clients.notify( - entity, - ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)), - ), }, None => { - server.clients.notify( - entity, - ServerMsg::private(format!("Player '{}' not found!", alias)), - ); server .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); + .notify(entity, ServerMsg::private(format!("You have no position!"))); } - }, - None => { - server - .clients - .notify(entity, ServerMsg::private(format!("You have no position!"))); } + } else { + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); } - } else { - server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); } } fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - match scan_fmt_some!(&args, action.arg_fmt, String, NpcKind, String) { - (Some(opt_align), Some(id), opt_amount) => { - if let Some(agent) = alignment_to_agent(&opt_align, entity) { - let amount = opt_amount - .and_then(|a| a.parse().ok()) - .filter(|x| *x > 0) - .unwrap_or(1) - .min(10); + if is_admin(server, entity) { + match scan_fmt_some!(&args, action.arg_fmt, String, NpcKind, String) { + (Some(opt_align), Some(id), opt_amount) => { + if let Some(agent) = alignment_to_agent(&opt_align, entity) { + let amount = opt_amount + .and_then(|a| a.parse().ok()) + .filter(|x| *x > 0) + .unwrap_or(1) + .min(10); - match server.state.read_component_cloned::(entity) { - Some(pos) => { - for _ in 0..amount { - let vel = Vec3::new( - rand::thread_rng().gen_range(-2.0, 3.0), - rand::thread_rng().gen_range(-2.0, 3.0), - 10.0, + match server.state.read_component_cloned::(entity) { + Some(pos) => { + for _ in 0..amount { + let vel = Vec3::new( + rand::thread_rng().gen_range(-2.0, 3.0), + rand::thread_rng().gen_range(-2.0, 3.0), + 10.0, + ); + + let body = kind_to_body(id); + server + .create_npc(pos, get_npc_name(id), body) + .with(comp::Vel(vel)) + .with(agent) + .build(); + } + server.clients.notify( + entity, + ServerMsg::private( + format!("Spawned {} entities", amount).to_owned(), + ), ); - - let body = kind_to_body(id); - server - .create_npc(pos, get_npc_name(id), body) - .with(comp::Vel(vel)) - .with(agent) - .build(); } - server.clients.notify( + None => server.clients.notify( entity, - ServerMsg::private(format!("Spawned {} entities", amount).to_owned()), - ); + ServerMsg::private("You have no position!".to_owned()), + ), } - None => server.clients.notify( - entity, - ServerMsg::private("You have no position!".to_owned()), - ), } } - } - _ => { - server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); + _ => { + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); + } } } } @@ -411,31 +445,33 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action } fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - if server - .state - .read_storage::() - .get(entity) - .is_some() - { - server + if is_admin(server, entity) { + if server .state - .ecs() - .write_storage::() - .remove(entity); - server.clients.notify( - entity, - ServerMsg::private(String::from("Toggled off build mode!")), - ); - } else { - let _ = server - .state - .ecs() - .write_storage::() - .insert(entity, comp::CanBuild); - server.clients.notify( - entity, - ServerMsg::private(String::from("Toggled on build mode!")), - ); + .read_storage::() + .get(entity) + .is_some() + { + server + .state + .ecs() + .write_storage::() + .remove(entity); + server.clients.notify( + entity, + ServerMsg::private(String::from("Toggled off build mode!")), + ); + } else { + let _ = server + .state + .ecs() + .write_storage::() + .insert(entity, comp::CanBuild); + server.clients.notify( + entity, + ServerMsg::private(String::from("Toggled on build mode!")), + ); + } } } @@ -468,219 +504,225 @@ fn kind_to_body(kind: NpcKind) -> comp::Body { } fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - let ecs = server.state.ecs(); - let mut stats = ecs.write_storage::(); - let players = ecs.read_storage::(); - let mut count = 0; - for (stats, ()) in (&mut stats, !&players).join() { - count += 1; - stats.health.set_to(0, comp::HealthSource::Command); + if is_admin(server, entity) { + let ecs = server.state.ecs(); + let mut stats = ecs.write_storage::(); + let players = ecs.read_storage::(); + let mut count = 0; + for (stats, ()) in (&mut stats, !&players).join() { + count += 1; + stats.health.set_to(0, comp::HealthSource::Command); + } + let text = if count > 0 { + format!("Destroyed {} NPCs.", count) + } else { + "No NPCs on server.".to_string() + }; + server.clients.notify(entity, ServerMsg::private(text)); } - let text = if count > 0 { - format!("Destroyed {} NPCs.", count) - } else { - "No NPCs on server.".to_string() - }; - server.clients.notify(entity, ServerMsg::private(text)); } fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { - let obj_type = scan_fmt!(&args, _action.arg_fmt, String); + if is_admin(server, entity) { + let obj_type = scan_fmt!(&args, _action.arg_fmt, String); - let pos = server - .state - .ecs() - .read_storage::() - .get(entity) - .copied(); - let ori = server - .state - .ecs() - .read_storage::() - .get(entity) - .copied(); - /*let builder = server - .create_object(pos, ori, obj_type) - .with(ori);*/ - if let (Some(pos), Some(ori)) = (pos, ori) { - let obj_type = match obj_type.as_ref().map(String::as_str) { - Ok("scarecrow") => comp::object::Body::Scarecrow, - Ok("cauldron") => comp::object::Body::Cauldron, - Ok("chest_vines") => comp::object::Body::ChestVines, - Ok("chest") => comp::object::Body::Chest, - Ok("chest_dark") => comp::object::Body::ChestDark, - Ok("chest_demon") => comp::object::Body::ChestDemon, - Ok("chest_gold") => comp::object::Body::ChestGold, - Ok("chest_light") => comp::object::Body::ChestLight, - Ok("chest_open") => comp::object::Body::ChestOpen, - Ok("chest_skull") => comp::object::Body::ChestSkull, - Ok("pumpkin") => comp::object::Body::Pumpkin, - Ok("pumpkin_2") => comp::object::Body::Pumpkin2, - Ok("pumpkin_3") => comp::object::Body::Pumpkin3, - Ok("pumpkin_4") => comp::object::Body::Pumpkin4, - Ok("pumpkin_5") => comp::object::Body::Pumpkin5, - Ok("campfire") => comp::object::Body::Campfire, - Ok("lantern_ground") => comp::object::Body::LanternGround, - Ok("lantern_ground_open") => comp::object::Body::LanternGroundOpen, - Ok("lantern_2") => comp::object::Body::LanternStanding2, - Ok("lantern") => comp::object::Body::LanternStanding, - Ok("potion_blue") => comp::object::Body::PotionBlue, - Ok("potion_green") => comp::object::Body::PotionGreen, - Ok("potion_red") => comp::object::Body::PotionRed, - Ok("crate") => comp::object::Body::Crate, - Ok("tent") => comp::object::Body::Tent, - Ok("bomb") => comp::object::Body::Bomb, - Ok("window_spooky") => comp::object::Body::WindowSpooky, - Ok("door_spooky") => comp::object::Body::DoorSpooky, - Ok("carpet") => comp::object::Body::Carpet, - Ok("table_human") => comp::object::Body::Table, - Ok("table_human_2") => comp::object::Body::Table2, - Ok("table_human_3") => comp::object::Body::Table3, - Ok("drawer") => comp::object::Body::Drawer, - Ok("bed_human_blue") => comp::object::Body::BedBlue, - Ok("anvil") => comp::object::Body::Anvil, - Ok("gravestone") => comp::object::Body::Gravestone, - Ok("gravestone_2") => comp::object::Body::Gravestone2, - Ok("chair") => comp::object::Body::Chair, - Ok("chair_2") => comp::object::Body::Chair2, - Ok("chair_3") => comp::object::Body::Chair3, - Ok("bench_human") => comp::object::Body::Bench, - Ok("bedroll") => comp::object::Body::Bedroll, - Ok("carpet_human_round") => comp::object::Body::CarpetHumanRound, - Ok("carpet_human_square") => comp::object::Body::CarpetHumanSquare, - Ok("carpet_human_square_2") => comp::object::Body::CarpetHumanSquare2, - Ok("carpet_human_squircle") => comp::object::Body::CarpetHumanSquircle, - _ => { - return server.clients.notify( - entity, - ServerMsg::private(String::from("Object not found!")), - ); - } - }; - server - .create_object(pos, obj_type) - .with(comp::Ori( - // converts player orientation into a 90° rotation for the object by using the axis with the highest value - ori.0 - .map(|e| { - if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() { - e - } else { - 0.0 - } - }) - .normalized(), - )) - .build(); - server - .clients - .notify(entity, ServerMsg::private(format!("Spawned object."))); - } else { - server - .clients - .notify(entity, ServerMsg::private(format!("You have no position!"))); + let pos = server + .state + .ecs() + .read_storage::() + .get(entity) + .copied(); + let ori = server + .state + .ecs() + .read_storage::() + .get(entity) + .copied(); + /*let builder = server + .create_object(pos, ori, obj_type) + .with(ori);*/ + if let (Some(pos), Some(ori)) = (pos, ori) { + let obj_type = match obj_type.as_ref().map(String::as_str) { + Ok("scarecrow") => comp::object::Body::Scarecrow, + Ok("cauldron") => comp::object::Body::Cauldron, + Ok("chest_vines") => comp::object::Body::ChestVines, + Ok("chest") => comp::object::Body::Chest, + Ok("chest_dark") => comp::object::Body::ChestDark, + Ok("chest_demon") => comp::object::Body::ChestDemon, + Ok("chest_gold") => comp::object::Body::ChestGold, + Ok("chest_light") => comp::object::Body::ChestLight, + Ok("chest_open") => comp::object::Body::ChestOpen, + Ok("chest_skull") => comp::object::Body::ChestSkull, + Ok("pumpkin") => comp::object::Body::Pumpkin, + Ok("pumpkin_2") => comp::object::Body::Pumpkin2, + Ok("pumpkin_3") => comp::object::Body::Pumpkin3, + Ok("pumpkin_4") => comp::object::Body::Pumpkin4, + Ok("pumpkin_5") => comp::object::Body::Pumpkin5, + Ok("campfire") => comp::object::Body::Campfire, + Ok("lantern_ground") => comp::object::Body::LanternGround, + Ok("lantern_ground_open") => comp::object::Body::LanternGroundOpen, + Ok("lantern_2") => comp::object::Body::LanternStanding2, + Ok("lantern") => comp::object::Body::LanternStanding, + Ok("potion_blue") => comp::object::Body::PotionBlue, + Ok("potion_green") => comp::object::Body::PotionGreen, + Ok("potion_red") => comp::object::Body::PotionRed, + Ok("crate") => comp::object::Body::Crate, + Ok("tent") => comp::object::Body::Tent, + Ok("bomb") => comp::object::Body::Bomb, + Ok("window_spooky") => comp::object::Body::WindowSpooky, + Ok("door_spooky") => comp::object::Body::DoorSpooky, + Ok("carpet") => comp::object::Body::Carpet, + Ok("table_human") => comp::object::Body::Table, + Ok("table_human_2") => comp::object::Body::Table2, + Ok("table_human_3") => comp::object::Body::Table3, + Ok("drawer") => comp::object::Body::Drawer, + Ok("bed_human_blue") => comp::object::Body::BedBlue, + Ok("anvil") => comp::object::Body::Anvil, + Ok("gravestone") => comp::object::Body::Gravestone, + Ok("gravestone_2") => comp::object::Body::Gravestone2, + Ok("chair") => comp::object::Body::Chair, + Ok("chair_2") => comp::object::Body::Chair2, + Ok("chair_3") => comp::object::Body::Chair3, + Ok("bench_human") => comp::object::Body::Bench, + Ok("bedroll") => comp::object::Body::Bedroll, + Ok("carpet_human_round") => comp::object::Body::CarpetHumanRound, + Ok("carpet_human_square") => comp::object::Body::CarpetHumanSquare, + Ok("carpet_human_square_2") => comp::object::Body::CarpetHumanSquare2, + Ok("carpet_human_squircle") => comp::object::Body::CarpetHumanSquircle, + _ => { + return server.clients.notify( + entity, + ServerMsg::private(String::from("Object not found!")), + ); + } + }; + server + .create_object(pos, obj_type) + .with(comp::Ori( + // converts player orientation into a 90° rotation for the object by using the axis with the highest value + ori.0 + .map(|e| { + if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() { + e + } else { + 0.0 + } + }) + .normalized(), + )) + .build(); + server + .clients + .notify(entity, ServerMsg::private(format!("Spawned object."))); + } else { + server + .clients + .notify(entity, ServerMsg::private(format!("You have no position!"))); + } } } fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - 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); + if is_admin(server, entity) { + 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); - let mut light_emitter = comp::LightEmitter::default(); + let mut light_emitter = comp::LightEmitter::default(); - if let (Some(r), Some(g), Some(b)) = (opt_r, opt_g, opt_b) { - let r = r.max(0.0).min(1.0); - let g = g.max(0.0).min(1.0); - let b = b.max(0.0).min(1.0); - light_emitter.col = Rgb::new(r, g, b) - }; - if let (Some(x), Some(y), Some(z)) = (opt_x, opt_y, opt_z) { - light_emitter.offset = Vec3::new(x, y, z) - }; - if let Some(s) = opt_s { - light_emitter.strength = s.max(0.0) - }; - let pos = server - .state - .ecs() - .read_storage::() - .get(entity) - .copied(); - if let Some(pos) = pos { - server + if let (Some(r), Some(g), Some(b)) = (opt_r, opt_g, opt_b) { + let r = r.max(0.0).min(1.0); + let g = g.max(0.0).min(1.0); + let b = b.max(0.0).min(1.0); + light_emitter.col = Rgb::new(r, g, b) + }; + if let (Some(x), Some(y), Some(z)) = (opt_x, opt_y, opt_z) { + light_emitter.offset = Vec3::new(x, y, z) + }; + if let Some(s) = opt_s { + light_emitter.strength = s.max(0.0) + }; + let pos = server .state - .ecs_mut() - .create_entity_synced() - .with(pos) - .with(comp::ForceUpdate) - .with(light_emitter) - .build(); - server - .clients - .notify(entity, ServerMsg::private(format!("Spawned object."))); - } else { - server - .clients - .notify(entity, ServerMsg::private(format!("You have no position!"))); + .ecs() + .read_storage::() + .get(entity) + .copied(); + if let Some(pos) = pos { + server + .state + .ecs_mut() + .create_entity_synced() + .with(pos) + .with(comp::ForceUpdate) + .with(light_emitter) + .build(); + server + .clients + .notify(entity, ServerMsg::private(format!("Spawned object."))); + } else { + server + .clients + .notify(entity, ServerMsg::private(format!("You have no position!"))); + } } } fn handle_lantern(server: &mut Server, entity: 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 - .state - .read_storage::() - .get(entity) - .is_some() - { - if let Some(s) = opt_s { - if let Some(light) = server - .state - .ecs() - .write_storage::() - .get_mut(entity) - { - light.strength = s.max(0.1).min(20.0); + if server + .state + .read_storage::() + .get(entity) + .is_some() + { + if let Some(s) = opt_s { + if let Some(light) = server + .state + .ecs() + .write_storage::() + .get_mut(entity) + { + light.strength = s.max(0.1).min(20.0); + server.clients.notify( + entity, + ServerMsg::private(String::from("You played with flame strength.")), + ); + } + } else { + server + .state + .ecs() + .write_storage::() + .remove(entity); server.clients.notify( entity, - ServerMsg::private(String::from("You played with flame strength.")), + ServerMsg::private(String::from("You put out the lantern.")), ); } } else { - server + let _ = server .state .ecs() .write_storage::() - .remove(entity); + .insert( + entity, + comp::LightEmitter { + offset: Vec3::new(0.5, 0.2, 0.8), + col: Rgb::new(1.0, 0.75, 0.3), + strength: if let Some(s) = opt_s { + s.max(0.0).min(20.0) + } else { + 6.0 + }, + }, + ); + server.clients.notify( entity, - ServerMsg::private(String::from("You put out the lantern.")), + ServerMsg::private(String::from("You lighted your lantern.")), ); } - } else { - let _ = server - .state - .ecs() - .write_storage::() - .insert( - entity, - comp::LightEmitter { - offset: Vec3::new(0.5, 0.2, 0.8), - col: Rgb::new(1.0, 0.75, 0.3), - strength: if let Some(s) = opt_s { - s.max(0.0).min(20.0) - } else { - 6.0 - }, - }, - ); - - server.clients.notify( - entity, - ServerMsg::private(String::from("You lighted your lantern.")), - ); - } } fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { From 1f5817a80b6073c7ca541339ebc4185466a51072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Korg=C3=B3l?= Date: Mon, 12 Aug 2019 16:05:58 +0200 Subject: [PATCH 2/5] Add Admins field to the settings file --- common/src/comp/mod.rs | 4 +- server/src/cmd.rs | 90 +++++++++++++++++++++--------------------- server/src/lib.rs | 23 ++++++++++- server/src/settings.rs | 2 + 4 files changed, 70 insertions(+), 49 deletions(-) diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 7fd9c132fc..3e2874862a 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; @@ -10,10 +11,10 @@ mod phys; mod player; mod stats; mod visual; -mod admin; // Reexports pub use action_state::ActionState; +pub use admin::AdminPerms; pub use agent::Agent; pub use animation::{Animation, AnimationInfo}; pub use body::{humanoid, object, quadruped, quadruped_medium, Body}; @@ -27,4 +28,3 @@ pub use phys::{ForceUpdate, Ori, Pos, Scale, Vel}; pub use player::Player; pub use stats::{Dying, Exp, HealthSource, Level, Stats}; pub use visual::LightEmitter; -pub use admin::AdminPerms; diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 7d4d51e07d..5b26f76fda 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -668,61 +668,61 @@ fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &C } fn handle_lantern(server: &mut Server, entity: 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 - .state - .read_storage::() - .get(entity) - .is_some() - { - if let Some(s) = opt_s { - if let Some(light) = server - .state - .ecs() - .write_storage::() - .get_mut(entity) - { - light.strength = s.max(0.1).min(20.0); - server.clients.notify( - entity, - ServerMsg::private(String::from("You played with flame strength.")), - ); - } - } else { - server - .state - .ecs() - .write_storage::() - .remove(entity); - server.clients.notify( - entity, - ServerMsg::private(String::from("You put out the lantern.")), - ); - } - } else { - let _ = server + if server + .state + .read_storage::() + .get(entity) + .is_some() + { + if let Some(s) = opt_s { + if let Some(light) = server .state .ecs() .write_storage::() - .insert( + .get_mut(entity) + { + light.strength = s.max(0.1).min(20.0); + server.clients.notify( entity, - comp::LightEmitter { - offset: Vec3::new(0.5, 0.2, 0.8), - col: Rgb::new(1.0, 0.75, 0.3), - strength: if let Some(s) = opt_s { - s.max(0.0).min(20.0) - } else { - 6.0 - }, - }, + ServerMsg::private(String::from("You played with flame strength.")), ); - + } + } else { + server + .state + .ecs() + .write_storage::() + .remove(entity); server.clients.notify( entity, - ServerMsg::private(String::from("You lighted your lantern.")), + ServerMsg::private(String::from("You put out the lantern.")), ); } + } else { + let _ = server + .state + .ecs() + .write_storage::() + .insert( + entity, + comp::LightEmitter { + offset: Vec3::new(0.5, 0.2, 0.8), + col: Rgb::new(1.0, 0.75, 0.3), + strength: if let Some(s) = opt_s { + s.max(0.0).min(20.0) + } else { + 6.0 + }, + }, + ); + + server.clients.notify( + entity, + ServerMsg::private(String::from("You lighted your lantern.")), + ); + } } fn handle_explosion(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { diff --git a/server/src/lib.rs b/server/src/lib.rs index b1ab1ca702..896920f424 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -188,11 +188,12 @@ impl Server { client: &mut Client, name: String, body: comp::Body, + server_settings: &ServerSettings, ) { let spawn_point = state.ecs().read_resource::().0; state.write_component(entity, body); - state.write_component(entity, comp::Stats::new(name)); + state.write_component(entity, comp::Stats::new(name.to_string())); state.write_component(entity, comp::Controller::default()); state.write_component(entity, comp::Pos(spawn_point)); state.write_component(entity, comp::Vel(Vec3::zero())); @@ -203,6 +204,16 @@ impl Server { // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); + let settings = server_settings.clone(); + // Give the AdminPerms component to the player if their name exists in admin list + if settings.admins.contains(&name) { + state.write_component(entity, comp::AdminPerms); + dbg!("Given admin perms to an user"); + dbg!(settings.admins); + } else { + dbg!(settings.admins); + dbg!("The new user isn't an admin"); + } // Tell the client its request was successful. client.allow_state(ClientState::Character); } @@ -518,6 +529,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 +681,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) { diff --git a/server/src/settings.rs b/server/src/settings.rs index 2ffc475f00..5b2997e27a 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![], } } } From e07762d13417c4ca8c87e3c742d0e2c89cfa7d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Korg=C3=B3l?= Date: Mon, 12 Aug 2019 18:11:06 +0200 Subject: [PATCH 3/5] Fix spawn command, properly search for admins --- common/src/npc.rs | 3 +-- server/src/lib.rs | 29 ++++++++++++++++++++--------- 2 files changed, 21 insertions(+), 11 deletions(-) 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/server/src/lib.rs b/server/src/lib.rs index 896920f424..ea2f090418 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -193,7 +193,7 @@ impl Server { let spawn_point = state.ecs().read_resource::().0; state.write_component(entity, body); - state.write_component(entity, comp::Stats::new(name.to_string())); + state.write_component(entity, comp::Stats::new(name)); state.write_component(entity, comp::Controller::default()); state.write_component(entity, comp::Pos(spawn_point)); state.write_component(entity, comp::Vel(Vec3::zero())); @@ -204,15 +204,16 @@ impl Server { // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); - let settings = server_settings.clone(); // Give the AdminPerms component to the player if their name exists in admin list - if settings.admins.contains(&name) { + if server_settings.admins.contains( + &state + .ecs() + .read_storage::() + .get(entity) + .unwrap() + .alias, + ) { state.write_component(entity, comp::AdminPerms); - dbg!("Given admin perms to an user"); - dbg!(settings.admins); - } else { - dbg!(settings.admins); - dbg!("The new user isn't an admin"); } // Tell the client its request was successful. client.allow_state(ClientState::Character); @@ -821,7 +822,17 @@ impl Server { } else { let message = match self.state.ecs().read_storage::().get(entity) { - Some(player) => format!("[{}] {}", &player.alias, message), + Some(player) => match self + .state + .ecs() + .read_storage::() + .get(entity) + { + Some(_perms) => { + format!("[ADMIN][{}] {}", &player.alias, message) + } + None => format!("[{}] {}", &player.alias, message), + }, None => format!("[] {}", message), }; self.clients From 28c47663d0d99b6c9a55612cdd5309297f2274c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Korg=C3=B3l?= Date: Wed, 14 Aug 2019 17:51:59 +0200 Subject: [PATCH 4/5] Give singleplayer its own server settings --- common/src/comp/admin.rs | 9 ++-- common/src/comp/mod.rs | 2 +- common/src/state.rs | 2 +- server/src/cmd.rs | 94 +++++++++++++++++++++++++++---------- server/src/lib.rs | 25 +++++----- server/src/settings.rs | 12 +++++ voxygen/src/singleplayer.rs | 2 +- 7 files changed, 103 insertions(+), 43 deletions(-) diff --git a/common/src/comp/admin.rs b/common/src/comp/admin.rs index fd4c30c5fb..025fcc9bf2 100644 --- a/common/src/comp/admin.rs +++ b/common/src/comp/admin.rs @@ -1,7 +1,8 @@ -use specs::{Component, VecStorage}; +use specs::{Component, NullStorage}; -pub struct AdminPerms; +#[derive(Default)] +pub struct Admin; -impl Component for AdminPerms { - type Storage = VecStorage; +impl Component for Admin { + type Storage = NullStorage; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 3e2874862a..da82ee09af 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -14,7 +14,7 @@ mod visual; // Reexports pub use action_state::ActionState; -pub use admin::AdminPerms; +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/state.rs b/common/src/state.rs index d6b4dbdace..c73a3602b6 100644 --- a/common/src/state.rs +++ b/common/src/state.rs @@ -145,7 +145,7 @@ impl State { ecs.register::(); 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 5b26f76fda..2b75512f6c 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -161,25 +161,14 @@ lazy_static! { ]; } -fn is_admin(server: &mut Server, entity: EcsEntity) -> bool { - if server - .state - .read_storage::() - .get(entity) - .is_some() - { - true - } else { +fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { + if !server.entity_is_admin(entity) { server.clients.notify( entity, - ServerMsg::private(String::from("You have no permissions to do that")), + ServerMsg::private(String::from("You have no permission to do that")), ); - false - } -} - -fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + return; + } else { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { match server.state.read_component_cloned::(entity) { Some(current_pos) => { @@ -198,7 +187,13 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch } fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { if server .state @@ -233,7 +228,13 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: & } fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { let time = scan_fmt_some!(&args, action.arg_fmt, String); let new_time = match time.as_ref().map(|s| s.as_str()) { Some("night") => NaiveTime::from_hms(0, 0, 0), @@ -284,7 +285,13 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch } fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { if let Some(stats) = server .state @@ -324,7 +331,13 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C } fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { let ecs = server.state.ecs(); let opt_player = (&ecs.entities(), &ecs.read_storage::()) @@ -371,7 +384,13 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat } fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { match scan_fmt_some!(&args, action.arg_fmt, String, NpcKind, String) { (Some(opt_align), Some(id), opt_amount) => { if let Some(agent) = alignment_to_agent(&opt_align, entity) { @@ -445,7 +464,13 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action } fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { if server .state .read_storage::() @@ -475,6 +500,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 @@ -504,7 +530,13 @@ fn kind_to_body(kind: NpcKind) -> comp::Body { } fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { let ecs = server.state.ecs(); let mut stats = ecs.write_storage::(); let players = ecs.read_storage::(); @@ -523,7 +555,13 @@ fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _actio } fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { let obj_type = scan_fmt!(&args, _action.arg_fmt, String); let pos = server @@ -623,7 +661,13 @@ fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: } fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if is_admin(server, entity) { + if !server.entity_is_admin(entity) { + server.clients.notify( + entity, + ServerMsg::private(String::from("You have no permission to do that")), + ); + return; + } else { 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); diff --git a/server/src/lib.rs b/server/src/lib.rs index ea2f090418..e7a44c5930 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -204,7 +204,7 @@ impl Server { // Make sure physics are accepted. state.write_component(entity, comp::ForceUpdate); - // Give the AdminPerms component to the player if their name exists in admin list + // Give the Admin component to the player if their name exists in admin list if server_settings.admins.contains( &state .ecs() @@ -213,7 +213,7 @@ impl Server { .unwrap() .alias, ) { - state.write_component(entity, comp::AdminPerms); + state.write_component(entity, comp::Admin); } // Tell the client its request was successful. client.allow_state(ClientState::Character); @@ -822,17 +822,13 @@ impl Server { } else { let message = match self.state.ecs().read_storage::().get(entity) { - Some(player) => match self - .state - .ecs() - .read_storage::() - .get(entity) - { - Some(_perms) => { + Some(player) => { + if self.entity_is_admin(entity) { format!("[ADMIN][{}] {}", &player.alias, message) + } else { + format!("[{}] {}", &player.alias, message) } - None => format!("[{}] {}", &player.alias, message), - }, + } None => format!("[] {}", message), }; self.clients @@ -1182,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 5b2997e27a..4a83b6569e 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -61,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: "This is the best Veloren singleplayer server.".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 { From 892855a348abbaec8f17e2391da407ca90a47e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Korg=C3=B3l?= Date: Thu, 15 Aug 2019 16:33:00 +0200 Subject: [PATCH 5/5] Add needs_admin bool to check if the commands needs admin perms --- server/src/cmd.rs | 743 +++++++++++++++++++---------------------- server/src/settings.rs | 2 +- 2 files changed, 346 insertions(+), 399 deletions(-) diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 2b75512f6c..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,157 +80,157 @@ 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, ), ]; } fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { - match server.state.read_component_cloned::(entity) { - Some(current_pos) => { - server - .state - .write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); - server.state.write_component(entity, comp::ForceUpdate); - } - None => server.clients.notify( - entity, - ServerMsg::private(String::from("You have no position!")), - ), + if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { + match server.state.read_component_cloned::(entity) { + Some(current_pos) => { + server + .state + .write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z))); + server.state.write_component(entity, comp::ForceUpdate); } + None => server.clients.notify( + entity, + ServerMsg::private(String::from("You have no position!")), + ), } } } fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { - if server - .state - .read_component_cloned::(entity) - .is_some() - { - server - .state - .write_component(entity, comp::Pos(Vec3::new(x, y, z))); - server.state.write_component(entity, comp::ForceUpdate); - } else { - server.clients.notify( - entity, - ServerMsg::private(String::from("You don't have a position!")), - ); - } - } else { + if let Ok((x, y, z)) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32) { + if server + .state + .read_component_cloned::(entity) + .is_some() + { server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); + .state + .write_component(entity, comp::Pos(Vec3::new(x, y, z))); + server.state.write_component(entity, comp::ForceUpdate); + } else { + server.clients.notify( + entity, + ServerMsg::private(String::from("You don't have a position!")), + ); } + } else { + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); } } @@ -228,90 +244,74 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: & } fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - let time = scan_fmt_some!(&args, action.arg_fmt, String); - let new_time = match time.as_ref().map(|s| s.as_str()) { - Some("night") => NaiveTime::from_hms(0, 0, 0), - Some("dawn") => NaiveTime::from_hms(5, 0, 0), - Some("day") => NaiveTime::from_hms(12, 0, 0), - Some("dusk") => NaiveTime::from_hms(17, 0, 0), - Some(n) => match n.parse() { - Ok(n) => n, - Err(_) => match NaiveTime::parse_from_str(n, "%H:%M") { - Ok(time) => time, - Err(_) => { - server.clients.notify( - entity, - ServerMsg::private(format!("'{}' is not a valid time.", n)), - ); - return; - } - }, + let time = scan_fmt_some!(&args, action.arg_fmt, String); + let new_time = match time.as_ref().map(|s| s.as_str()) { + Some("night") => NaiveTime::from_hms(0, 0, 0), + Some("dawn") => NaiveTime::from_hms(5, 0, 0), + Some("day") => NaiveTime::from_hms(12, 0, 0), + Some("dusk") => NaiveTime::from_hms(17, 0, 0), + Some(n) => match n.parse() { + Ok(n) => n, + Err(_) => match NaiveTime::parse_from_str(n, "%H:%M") { + Ok(time) => time, + Err(_) => { + server.clients.notify( + entity, + ServerMsg::private(format!("'{}' is not a valid time.", n)), + ); + return; + } }, - None => { - let time_in_seconds = server.state.ecs_mut().read_resource::().0; + }, + None => { + let time_in_seconds = server.state.ecs_mut().read_resource::().0; - let current_time = NaiveTime::from_num_seconds_from_midnight_opt( - // Wraps around back to 0s if it exceeds 24 hours (24 hours = 86400s) - (time_in_seconds as u64 % 86400) as u32, - 0, - ); - let msg = match current_time { - Some(time) => format!("Current time is: {}", time.format("%H:%M").to_string()), - None => String::from("Unknown Time"), - }; - server.clients.notify(entity, ServerMsg::private(msg)); - return; - } - }; + let current_time = NaiveTime::from_num_seconds_from_midnight_opt( + // Wraps around back to 0s if it exceeds 24 hours (24 hours = 86400s) + (time_in_seconds as u64 % 86400) as u32, + 0, + ); + let msg = match current_time { + Some(time) => format!("Current time is: {}", time.format("%H:%M").to_string()), + None => String::from("Unknown Time"), + }; + server.clients.notify(entity, ServerMsg::private(msg)); + return; + } + }; - server.state.ecs_mut().write_resource::().0 = - new_time.num_seconds_from_midnight() as f64; + server.state.ecs_mut().write_resource::().0 = + new_time.num_seconds_from_midnight() as f64; - server.clients.notify( - entity, - ServerMsg::private(format!( - "Time changed to: {}", - new_time.format("%H:%M").to_string() - )), - ); - } + server.clients.notify( + entity, + ServerMsg::private(format!( + "Time changed to: {}", + new_time.format("%H:%M").to_string() + )), + ); } fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { - if let Some(stats) = server - .state - .ecs_mut() - .write_storage::() - .get_mut(entity) - { - stats.health.set_to(hp, comp::HealthSource::Command); - } else { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no health.")), - ); - } + if let Ok(hp) = scan_fmt!(&args, action.arg_fmt, u32) { + if let Some(stats) = server + .state + .ecs_mut() + .write_storage::() + .get_mut(entity) + { + stats.health.set_to(hp, comp::HealthSource::Command); } else { server.clients.notify( entity, - ServerMsg::private(String::from("You must specify health amount!")), + ServerMsg::private(String::from("You have no health.")), ); } + } else { + server.clients.notify( + entity, + ServerMsg::private(String::from("You must specify health amount!")), + ); } } @@ -331,110 +331,89 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C } fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { - let ecs = server.state.ecs(); - let opt_player = (&ecs.entities(), &ecs.read_storage::()) - .join() - .find(|(_, player)| player.alias == alias) - .map(|(entity, _)| entity); - match server.state.read_component_cloned::(entity) { - Some(_pos) => match opt_player { - Some(player) => match server.state.read_component_cloned::(player) { - Some(pos) => { - server.state.write_component(entity, pos); - server.state.write_component(entity, comp::ForceUpdate); - } - None => server.clients.notify( - entity, - ServerMsg::private(format!( - "Unable to teleport to player '{}'!", - alias - )), - ), - }, - None => { - server.clients.notify( - entity, - ServerMsg::private(format!("Player '{}' not found!", alias)), - ); - server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); + if let Ok(alias) = scan_fmt!(&args, action.arg_fmt, String) { + let ecs = server.state.ecs(); + let opt_player = (&ecs.entities(), &ecs.read_storage::()) + .join() + .find(|(_, player)| player.alias == alias) + .map(|(entity, _)| entity); + match server.state.read_component_cloned::(entity) { + Some(_pos) => match opt_player { + Some(player) => match server.state.read_component_cloned::(player) { + Some(pos) => { + server.state.write_component(entity, pos); + server.state.write_component(entity, comp::ForceUpdate); } + None => server.clients.notify( + entity, + ServerMsg::private(format!("Unable to teleport to player '{}'!", alias)), + ), }, None => { + server.clients.notify( + entity, + ServerMsg::private(format!("Player '{}' not found!", alias)), + ); server .clients - .notify(entity, ServerMsg::private(format!("You have no position!"))); + .notify(entity, ServerMsg::private(String::from(action.help_string))); } + }, + None => { + server + .clients + .notify(entity, ServerMsg::private(format!("You have no position!"))); } - } else { - server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); } + } else { + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); } } fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - match scan_fmt_some!(&args, action.arg_fmt, String, NpcKind, String) { - (Some(opt_align), Some(id), opt_amount) => { - if let Some(agent) = alignment_to_agent(&opt_align, entity) { - let amount = opt_amount - .and_then(|a| a.parse().ok()) - .filter(|x| *x > 0) - .unwrap_or(1) - .min(10); + match scan_fmt_some!(&args, action.arg_fmt, String, NpcKind, String) { + (Some(opt_align), Some(id), opt_amount) => { + if let Some(agent) = alignment_to_agent(&opt_align, entity) { + let amount = opt_amount + .and_then(|a| a.parse().ok()) + .filter(|x| *x > 0) + .unwrap_or(1) + .min(10); - match server.state.read_component_cloned::(entity) { - Some(pos) => { - for _ in 0..amount { - let vel = Vec3::new( - rand::thread_rng().gen_range(-2.0, 3.0), - rand::thread_rng().gen_range(-2.0, 3.0), - 10.0, - ); - - let body = kind_to_body(id); - server - .create_npc(pos, get_npc_name(id), body) - .with(comp::Vel(vel)) - .with(agent) - .build(); - } - server.clients.notify( - entity, - ServerMsg::private( - format!("Spawned {} entities", amount).to_owned(), - ), + match server.state.read_component_cloned::(entity) { + Some(pos) => { + for _ in 0..amount { + let vel = Vec3::new( + rand::thread_rng().gen_range(-2.0, 3.0), + rand::thread_rng().gen_range(-2.0, 3.0), + 10.0, ); + + let body = kind_to_body(id); + server + .create_npc(pos, get_npc_name(id), body) + .with(comp::Vel(vel)) + .with(agent) + .build(); } - None => server.clients.notify( + server.clients.notify( entity, - ServerMsg::private("You have no position!".to_owned()), - ), + ServerMsg::private(format!("Spawned {} entities", amount).to_owned()), + ); } + None => server.clients.notify( + entity, + ServerMsg::private("You have no position!".to_owned()), + ), } } - _ => { - server - .clients - .notify(entity, ServerMsg::private(String::from(action.help_string))); - } + } + _ => { + server + .clients + .notify(entity, ServerMsg::private(String::from(action.help_string))); } } } @@ -464,39 +443,31 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action } fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - if !server.entity_is_admin(entity) { + if server + .state + .read_storage::() + .get(entity) + .is_some() + { + server + .state + .ecs() + .write_storage::() + .remove(entity); server.clients.notify( entity, - ServerMsg::private(String::from("You have no permission to do that")), + ServerMsg::private(String::from("Toggled off build mode!")), ); - return; } else { - if server + let _ = server .state - .read_storage::() - .get(entity) - .is_some() - { - server - .state - .ecs() - .write_storage::() - .remove(entity); - server.clients.notify( - entity, - ServerMsg::private(String::from("Toggled off build mode!")), - ); - } else { - let _ = server - .state - .ecs() - .write_storage::() - .insert(entity, comp::CanBuild); - server.clients.notify( - entity, - ServerMsg::private(String::from("Toggled on build mode!")), - ); - } + .ecs() + .write_storage::() + .insert(entity, comp::CanBuild); + server.clients.notify( + entity, + ServerMsg::private(String::from("Toggled on build mode!")), + ); } } @@ -530,184 +501,160 @@ fn kind_to_body(kind: NpcKind) -> comp::Body { } fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - let ecs = server.state.ecs(); - let mut stats = ecs.write_storage::(); - let players = ecs.read_storage::(); - let mut count = 0; - for (stats, ()) in (&mut stats, !&players).join() { - count += 1; - stats.health.set_to(0, comp::HealthSource::Command); - } - let text = if count > 0 { - format!("Destroyed {} NPCs.", count) - } else { - "No NPCs on server.".to_string() - }; - server.clients.notify(entity, ServerMsg::private(text)); + let ecs = server.state.ecs(); + let mut stats = ecs.write_storage::(); + let players = ecs.read_storage::(); + let mut count = 0; + for (stats, ()) in (&mut stats, !&players).join() { + count += 1; + stats.health.set_to(0, comp::HealthSource::Command); } + let text = if count > 0 { + format!("Destroyed {} NPCs.", count) + } else { + "No NPCs on server.".to_string() + }; + server.clients.notify(entity, ServerMsg::private(text)); } fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - let obj_type = scan_fmt!(&args, _action.arg_fmt, String); + let obj_type = scan_fmt!(&args, _action.arg_fmt, String); - let pos = server - .state - .ecs() - .read_storage::() - .get(entity) - .copied(); - let ori = server - .state - .ecs() - .read_storage::() - .get(entity) - .copied(); - /*let builder = server - .create_object(pos, ori, obj_type) - .with(ori);*/ - if let (Some(pos), Some(ori)) = (pos, ori) { - let obj_type = match obj_type.as_ref().map(String::as_str) { - Ok("scarecrow") => comp::object::Body::Scarecrow, - Ok("cauldron") => comp::object::Body::Cauldron, - Ok("chest_vines") => comp::object::Body::ChestVines, - Ok("chest") => comp::object::Body::Chest, - Ok("chest_dark") => comp::object::Body::ChestDark, - Ok("chest_demon") => comp::object::Body::ChestDemon, - Ok("chest_gold") => comp::object::Body::ChestGold, - Ok("chest_light") => comp::object::Body::ChestLight, - Ok("chest_open") => comp::object::Body::ChestOpen, - Ok("chest_skull") => comp::object::Body::ChestSkull, - Ok("pumpkin") => comp::object::Body::Pumpkin, - Ok("pumpkin_2") => comp::object::Body::Pumpkin2, - Ok("pumpkin_3") => comp::object::Body::Pumpkin3, - Ok("pumpkin_4") => comp::object::Body::Pumpkin4, - Ok("pumpkin_5") => comp::object::Body::Pumpkin5, - Ok("campfire") => comp::object::Body::Campfire, - Ok("lantern_ground") => comp::object::Body::LanternGround, - Ok("lantern_ground_open") => comp::object::Body::LanternGroundOpen, - Ok("lantern_2") => comp::object::Body::LanternStanding2, - Ok("lantern") => comp::object::Body::LanternStanding, - Ok("potion_blue") => comp::object::Body::PotionBlue, - Ok("potion_green") => comp::object::Body::PotionGreen, - Ok("potion_red") => comp::object::Body::PotionRed, - Ok("crate") => comp::object::Body::Crate, - Ok("tent") => comp::object::Body::Tent, - Ok("bomb") => comp::object::Body::Bomb, - Ok("window_spooky") => comp::object::Body::WindowSpooky, - Ok("door_spooky") => comp::object::Body::DoorSpooky, - Ok("carpet") => comp::object::Body::Carpet, - Ok("table_human") => comp::object::Body::Table, - Ok("table_human_2") => comp::object::Body::Table2, - Ok("table_human_3") => comp::object::Body::Table3, - Ok("drawer") => comp::object::Body::Drawer, - Ok("bed_human_blue") => comp::object::Body::BedBlue, - Ok("anvil") => comp::object::Body::Anvil, - Ok("gravestone") => comp::object::Body::Gravestone, - Ok("gravestone_2") => comp::object::Body::Gravestone2, - Ok("chair") => comp::object::Body::Chair, - Ok("chair_2") => comp::object::Body::Chair2, - Ok("chair_3") => comp::object::Body::Chair3, - Ok("bench_human") => comp::object::Body::Bench, - Ok("bedroll") => comp::object::Body::Bedroll, - Ok("carpet_human_round") => comp::object::Body::CarpetHumanRound, - Ok("carpet_human_square") => comp::object::Body::CarpetHumanSquare, - Ok("carpet_human_square_2") => comp::object::Body::CarpetHumanSquare2, - Ok("carpet_human_squircle") => comp::object::Body::CarpetHumanSquircle, - _ => { - return server.clients.notify( - entity, - ServerMsg::private(String::from("Object not found!")), - ); - } - }; - server - .create_object(pos, obj_type) - .with(comp::Ori( - // converts player orientation into a 90° rotation for the object by using the axis with the highest value - ori.0 - .map(|e| { - if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() { - e - } else { - 0.0 - } - }) - .normalized(), - )) - .build(); - server - .clients - .notify(entity, ServerMsg::private(format!("Spawned object."))); - } else { - server - .clients - .notify(entity, ServerMsg::private(format!("You have no position!"))); - } + let pos = server + .state + .ecs() + .read_storage::() + .get(entity) + .copied(); + let ori = server + .state + .ecs() + .read_storage::() + .get(entity) + .copied(); + /*let builder = server + .create_object(pos, ori, obj_type) + .with(ori);*/ + if let (Some(pos), Some(ori)) = (pos, ori) { + let obj_type = match obj_type.as_ref().map(String::as_str) { + Ok("scarecrow") => comp::object::Body::Scarecrow, + Ok("cauldron") => comp::object::Body::Cauldron, + Ok("chest_vines") => comp::object::Body::ChestVines, + Ok("chest") => comp::object::Body::Chest, + Ok("chest_dark") => comp::object::Body::ChestDark, + Ok("chest_demon") => comp::object::Body::ChestDemon, + Ok("chest_gold") => comp::object::Body::ChestGold, + Ok("chest_light") => comp::object::Body::ChestLight, + Ok("chest_open") => comp::object::Body::ChestOpen, + Ok("chest_skull") => comp::object::Body::ChestSkull, + Ok("pumpkin") => comp::object::Body::Pumpkin, + Ok("pumpkin_2") => comp::object::Body::Pumpkin2, + Ok("pumpkin_3") => comp::object::Body::Pumpkin3, + Ok("pumpkin_4") => comp::object::Body::Pumpkin4, + Ok("pumpkin_5") => comp::object::Body::Pumpkin5, + Ok("campfire") => comp::object::Body::Campfire, + Ok("lantern_ground") => comp::object::Body::LanternGround, + Ok("lantern_ground_open") => comp::object::Body::LanternGroundOpen, + Ok("lantern_2") => comp::object::Body::LanternStanding2, + Ok("lantern") => comp::object::Body::LanternStanding, + Ok("potion_blue") => comp::object::Body::PotionBlue, + Ok("potion_green") => comp::object::Body::PotionGreen, + Ok("potion_red") => comp::object::Body::PotionRed, + Ok("crate") => comp::object::Body::Crate, + Ok("tent") => comp::object::Body::Tent, + Ok("bomb") => comp::object::Body::Bomb, + Ok("window_spooky") => comp::object::Body::WindowSpooky, + Ok("door_spooky") => comp::object::Body::DoorSpooky, + Ok("carpet") => comp::object::Body::Carpet, + Ok("table_human") => comp::object::Body::Table, + Ok("table_human_2") => comp::object::Body::Table2, + Ok("table_human_3") => comp::object::Body::Table3, + Ok("drawer") => comp::object::Body::Drawer, + Ok("bed_human_blue") => comp::object::Body::BedBlue, + Ok("anvil") => comp::object::Body::Anvil, + Ok("gravestone") => comp::object::Body::Gravestone, + Ok("gravestone_2") => comp::object::Body::Gravestone2, + Ok("chair") => comp::object::Body::Chair, + Ok("chair_2") => comp::object::Body::Chair2, + Ok("chair_3") => comp::object::Body::Chair3, + Ok("bench_human") => comp::object::Body::Bench, + Ok("bedroll") => comp::object::Body::Bedroll, + Ok("carpet_human_round") => comp::object::Body::CarpetHumanRound, + Ok("carpet_human_square") => comp::object::Body::CarpetHumanSquare, + Ok("carpet_human_square_2") => comp::object::Body::CarpetHumanSquare2, + Ok("carpet_human_squircle") => comp::object::Body::CarpetHumanSquircle, + _ => { + return server.clients.notify( + entity, + ServerMsg::private(String::from("Object not found!")), + ); + } + }; + server + .create_object(pos, obj_type) + .with(comp::Ori( + // converts player orientation into a 90° rotation for the object by using the axis with the highest value + ori.0 + .map(|e| { + if e.abs() == ori.0.map(|e| e.abs()).reduce_partial_max() { + e + } else { + 0.0 + } + }) + .normalized(), + )) + .build(); + server + .clients + .notify(entity, ServerMsg::private(format!("Spawned object."))); + } else { + server + .clients + .notify(entity, ServerMsg::private(format!("You have no position!"))); } } fn handle_light(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) { - if !server.entity_is_admin(entity) { - server.clients.notify( - entity, - ServerMsg::private(String::from("You have no permission to do that")), - ); - return; - } else { - 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); + 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); - let mut light_emitter = comp::LightEmitter::default(); + let mut light_emitter = comp::LightEmitter::default(); - if let (Some(r), Some(g), Some(b)) = (opt_r, opt_g, opt_b) { - let r = r.max(0.0).min(1.0); - let g = g.max(0.0).min(1.0); - let b = b.max(0.0).min(1.0); - light_emitter.col = Rgb::new(r, g, b) - }; - if let (Some(x), Some(y), Some(z)) = (opt_x, opt_y, opt_z) { - light_emitter.offset = Vec3::new(x, y, z) - }; - if let Some(s) = opt_s { - light_emitter.strength = s.max(0.0) - }; - let pos = server + if let (Some(r), Some(g), Some(b)) = (opt_r, opt_g, opt_b) { + let r = r.max(0.0).min(1.0); + let g = g.max(0.0).min(1.0); + let b = b.max(0.0).min(1.0); + light_emitter.col = Rgb::new(r, g, b) + }; + if let (Some(x), Some(y), Some(z)) = (opt_x, opt_y, opt_z) { + light_emitter.offset = Vec3::new(x, y, z) + }; + if let Some(s) = opt_s { + light_emitter.strength = s.max(0.0) + }; + let pos = server + .state + .ecs() + .read_storage::() + .get(entity) + .copied(); + if let Some(pos) = pos { + server .state - .ecs() - .read_storage::() - .get(entity) - .copied(); - if let Some(pos) = pos { - server - .state - .ecs_mut() - .create_entity_synced() - .with(pos) - .with(comp::ForceUpdate) - .with(light_emitter) - .build(); - server - .clients - .notify(entity, ServerMsg::private(format!("Spawned object."))); - } else { - server - .clients - .notify(entity, ServerMsg::private(format!("You have no position!"))); - } + .ecs_mut() + .create_entity_synced() + .with(pos) + .with(comp::ForceUpdate) + .with(light_emitter) + .build(); + server + .clients + .notify(entity, ServerMsg::private(format!("Spawned object."))); + } else { + server + .clients + .notify(entity, ServerMsg::private(format!("You have no position!"))); } } diff --git a/server/src/settings.rs b/server/src/settings.rs index 4a83b6569e..0897c5da0e 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -66,7 +66,7 @@ impl ServerSettings { address: SocketAddr::from(([0; 4], 14004)), world_seed: 1337, server_name: "Singleplayer".to_owned(), - server_description: "This is the best Veloren singleplayer server.".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