diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 41d7a70818..68da230d76 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -1,12 +1,12 @@ use crate::{assets, comp, npc}; use lazy_static::lazy_static; -use tracing::warn; use std::{ collections::HashMap, fmt::{self, Display}, path::Path, str::FromStr, }; +use tracing::warn; /// Struct representing a command that a user can run from server chat. pub struct ChatCommandData { diff --git a/common/src/comp/admin.rs b/common/src/comp/admin.rs index 5ce3c59327..f411c3f07d 100644 --- a/common/src/comp/admin.rs +++ b/common/src/comp/admin.rs @@ -1,4 +1,5 @@ use specs::{Component, NullStorage}; +use std::ops::Deref; #[derive(Clone, Copy, Default)] pub struct Admin; @@ -6,3 +7,12 @@ pub struct Admin; impl Component for Admin { type Storage = NullStorage; } + +/// List of admin usernames. This is stored as a specs resource so that the list +/// can be read by specs systems. +pub struct AdminList(pub Vec); +impl Deref for AdminList { + type Target = Vec; + + fn deref(&self) -> &Vec { &self.0 } +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 2cb45d8d84..24dc6b3a8f 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -18,7 +18,7 @@ mod visual; // Reexports pub use ability::{CharacterAbility, CharacterAbilityType, ItemConfig, Loadout}; -pub use admin::Admin; +pub use admin::{Admin, AdminList}; pub use agent::{Agent, Alignment}; pub use body::{ biped_large, bird_medium, bird_small, critter, dragon, fish_medium, fish_small, golem, diff --git a/server/src/lib.rs b/server/src/lib.rs index dc5e1b8744..5ba9a71044 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -105,6 +105,17 @@ impl Server { state .ecs_mut() .insert(CharacterLoader::new(settings.persistence_db_dir.clone())); + state + .ecs_mut() + .insert(persistence::character::CharacterUpdater::new( + settings.persistence_db_dir.clone(), + )); + state.ecs_mut().insert(crate::settings::PersistenceDBDir( + settings.persistence_db_dir.clone(), + )); + state + .ecs_mut() + .insert(comp::AdminList(settings.admins.clone())); // System timers for performance monitoring state.ecs_mut().insert(sys::EntitySyncTimer::default()); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index d069eff2e6..785941c89a 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -10,11 +10,11 @@ use common::{ sync::{Uid, UidAllocator, WorldSyncExt}, util::Dir, }; -use tracing::warn; use specs::{ saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder, Join, WorldExt, }; +use tracing::warn; use vek::*; pub trait StateExt { @@ -212,6 +212,8 @@ impl StateExt for State { fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents) { let (body, stats, inventory, loadout) = components; + // Make sure physics are accepted. + self.write_component(entity, comp::ForceUpdate); // Notify clients of a player list update let client_uid = self diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index 9524b03f83..fb1d0a9134 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -5,8 +5,8 @@ use crate::{ }; use common::{ comp::{ - Admin, CanBuild, ChatMode, ChatMsg, ChatType, ControlEvent, Controller, ForceUpdate, Ori, - Player, Pos, Stats, Vel, + Admin, AdminList, CanBuild, ChatMode, ChatMsg, ChatType, ControlEvent, Controller, + ForceUpdate, Ori, Player, Pos, Stats, Vel, }, event::{EventBus, ServerEvent}, msg::{ @@ -34,7 +34,6 @@ impl<'a> System<'a> for Sys { ReadExpect<'a, CharacterLoader>, ReadExpect<'a, TerrainGrid>, Write<'a, SysTimer>, - ReadStorage<'a, Admin>, ReadStorage<'a, Uid>, ReadStorage<'a, CanBuild>, ReadStorage<'a, ForceUpdate>, @@ -42,6 +41,8 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, ChatMode>, WriteExpect<'a, AuthProvider>, Write<'a, BlockChange>, + ReadExpect<'a, AdminList>, + WriteStorage<'a, Admin>, WriteStorage<'a, Pos>, WriteStorage<'a, Vel>, WriteStorage<'a, Ori>, @@ -64,7 +65,6 @@ impl<'a> System<'a> for Sys { character_loader, terrain, mut timer, - admins, uids, can_build, force_updates, @@ -72,6 +72,8 @@ impl<'a> System<'a> for Sys { chat_modes, mut accounts, mut block_changes, + admin_list, + mut admins, mut positions, mut velocities, mut orientations, @@ -163,6 +165,7 @@ impl<'a> System<'a> for Sys { let vd = view_distance .map(|vd| vd.min(settings.max_view_distance.unwrap_or(vd))); let player = Player::new(username, None, vd, uuid); + let is_admin = admin_list.contains(&username); if !player.is_valid() { // Invalid player @@ -175,6 +178,12 @@ impl<'a> System<'a> for Sys { // Add Player component to this client let _ = players.insert(entity, player); + // Give the Admin component to the player if their name exists in + // admin list + if is_admin { + let _ = admins.insert(entity, Admin); + } + // Tell the client its request was successful. client.allow_state(ClientState::Registered); @@ -348,7 +357,11 @@ impl<'a> System<'a> for Sys { Err(ChatMsgValidationError::TooLong) => { let max = MAX_BYTES_CHAT_MSG; let len = message.len(); - tracing::warn!(?len, ?max, "Recieved a chat message that's too long") + tracing::warn!( + ?len, + ?max, + "Recieved a chat message that's too long" + ) }, }, ClientState::Pending => {}, diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index 2863dfb103..32204fb527 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -284,7 +284,11 @@ impl<'a> Widget for Overhead<'a> { } } text_shadow.set(state.ids.speech_bubble_shadow, ui); - let icon = if self.settings.speech_bubble_icon { bubble_icon(&bubble, &self.imgs) } else { self.imgs.nothing }; + let icon = if self.settings.speech_bubble_icon { + bubble_icon(&bubble, &self.imgs) + } else { + self.imgs.nothing + }; Image::new(icon) .w_h(16.0, 16.0) .top_left_with_margin_on(state.ids.speech_bubble_text, -16.0) diff --git a/voxygen/src/hud/settings_window.rs b/voxygen/src/hud/settings_window.rs index bd7fb37e19..7f06b2f39c 100644 --- a/voxygen/src/hud/settings_window.rs +++ b/voxygen/src/hud/settings_window.rs @@ -950,11 +950,11 @@ impl<'a> Widget for SettingsWindow<'a> { self.imgs.checkbox, self.imgs.checkbox_checked, ) - .down_from(state.ids.speech_bubble_text, 10.0) - .w_h(18.0, 18.0) - .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) - .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) - .set(state.ids.speech_bubble_dark_mode_button, ui); + .down_from(state.ids.speech_bubble_text, 10.0) + .w_h(18.0, 18.0) + .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) + .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) + .set(state.ids.speech_bubble_dark_mode_button, ui); if self.global_state.settings.gameplay.speech_bubble_dark_mode != speech_bubble_dark_mode { @@ -962,14 +962,14 @@ impl<'a> Widget for SettingsWindow<'a> { } Text::new( &self - .localized_strings - .get("hud.settings.speech_bubble_dark_mode"), + .localized_strings + .get("hud.settings.speech_bubble_dark_mode"), ) - .right_from(state.ids.speech_bubble_dark_mode_button, 10.0) - .font_size(self.fonts.cyri.scale(15)) - .font_id(self.fonts.cyri.conrod_id) - .color(TEXT_COLOR) - .set(state.ids.speech_bubble_dark_mode_text, ui); + .right_from(state.ids.speech_bubble_dark_mode_button, 10.0) + .font_size(self.fonts.cyri.scale(15)) + .font_id(self.fonts.cyri.conrod_id) + .color(TEXT_COLOR) + .set(state.ids.speech_bubble_dark_mode_text, ui); // Speech bubble icon let speech_bubble_icon = ToggleButton::new( self.global_state.settings.gameplay.speech_bubble_icon, @@ -981,8 +981,7 @@ impl<'a> Widget for SettingsWindow<'a> { .hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo) .press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked) .set(state.ids.speech_bubble_icon_button, ui); - if self.global_state.settings.gameplay.speech_bubble_icon != speech_bubble_icon - { + if self.global_state.settings.gameplay.speech_bubble_icon != speech_bubble_icon { events.push(Event::SpeechBubbleIcon(speech_bubble_icon)); } Text::new(