From 8d90952074834bf9dbebdaba90dbbb46f2c92d48 Mon Sep 17 00:00:00 2001 From: Joey Maher Date: Fri, 5 Jun 2020 14:14:07 -0500 Subject: [PATCH 1/3] Add channel to send messages up from methods like fetch_events to hud --- CHANGELOG.md | 1 + voxygen/src/menu/char_selection/mod.rs | 8 ++++--- voxygen/src/menu/main/mod.rs | 6 +++++- voxygen/src/session.rs | 14 +++++++++--- voxygen/src/window.rs | 30 +++++++++++++++++++++++--- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f9a333ec0..bb3f61ff9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Eyebrows and shapes can now be selected - Character name and level information to chat, social tab and `/players` command. - Added inventory, armour and weapon saving +- Show where screenshots are saved to in the chat ### Changed diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 983e244d86..9feb900171 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -7,11 +7,11 @@ use crate::{ window::Event as WinEvent, Direction, GlobalState, PlayState, PlayStateResult, }; -use client::{self, Client}; +use client::{self, Client, Event as ClientEvent}; use common::{assets, clock::Clock, comp, msg::ClientState, state::DeltaTime}; use log::error; use specs::WorldExt; -use std::{cell::RefCell, rc::Rc, time::Duration}; +use std::{cell::RefCell, rc::Rc, time::Duration, sync::mpsc}; use ui::CharSelectionUi; pub struct CharSelectionState { @@ -42,10 +42,12 @@ impl PlayState for CharSelectionState { // Load the player's character list self.client.borrow_mut().load_character_list(); + let (message_sender, _message_receiver): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); + let mut current_client_state = self.client.borrow().get_client_state(); while let ClientState::Pending | ClientState::Registered = current_client_state { // Handle window events - for event in global_state.window.fetch_events(&mut global_state.settings) { + for event in global_state.window.fetch_events(&mut global_state.settings, &message_sender) { if self.char_selection_ui.handle_event(event.clone()) { continue; } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 27382c26ea..e46d492a45 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -11,6 +11,8 @@ use log::{error, warn}; #[cfg(feature = "singleplayer")] use std::time::Duration; use ui::{Event as MainMenuEvent, MainMenuUi}; +use std::sync::mpsc; +use client::{self, Event as ClientEvent}; pub struct MainMenuState { main_menu_ui: MainMenuUi, @@ -47,9 +49,11 @@ impl PlayState for MainMenuState { &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), ); + let (message_sender, _message_receiver): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); + loop { // Handle window events. - for event in global_state.window.fetch_events(&mut global_state.settings) { + for event in global_state.window.fetch_events(&mut global_state.settings, &message_sender) { match event { Event::Close => return PlayStateResult::Shutdown, // Pass events to ui. diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index e785de0d9c..2722547c98 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -9,7 +9,7 @@ use crate::{ window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; -use client::{self, Client, Event::Chat}; +use client::{self, Client, Event::Chat, Event as ClientEvent}; use common::{ assets::{load_watched, watch}, clock::Clock, @@ -23,7 +23,7 @@ use common::{ }; use log::error; use specs::{Join, WorldExt}; -use std::{cell::RefCell, rc::Rc, time::Duration}; +use std::{cell::RefCell, rc::Rc, time::Duration, sync::mpsc}; use vek::*; /// The action to perform after a tick @@ -143,6 +143,8 @@ impl PlayState for SessionState { let mut ori = self.scene.camera().get_orientation(); let mut free_look = false; + let (message_sender, message_receiver): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); + // Game loop let mut current_client_state = self.client.borrow().get_client_state(); while let ClientState::Pending | ClientState::Character = current_client_state { @@ -224,8 +226,14 @@ impl PlayState for SessionState { .unwrap_or(false) })); + // Receive any ClientEvents sent through the message channel + match message_receiver.try_recv() { + Ok(message_event) => self.hud.new_message(message_event), + Err(_x) => {}, + }; + // Handle window events. - for event in global_state.window.fetch_events(&mut global_state.settings) { + for event in global_state.window.fetch_events(&mut global_state.settings, &message_sender) { // Pass all events to the ui first. if self.hud.handle_event(event.clone(), global_state) { continue; diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 667b352d5b..1ef15d6895 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -10,6 +10,11 @@ use hashbrown::HashMap; use log::{error, warn}; use serde_derive::{Deserialize, Serialize}; use std::fmt; +use std::sync::mpsc::Sender; +use client::{self, Event as ClientEvent}; +use common::{ + ChatType, +}; use vek::*; /// Represents a key that the game recognises after input mapping. @@ -483,7 +488,7 @@ impl Window { pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer } - pub fn fetch_events(&mut self, settings: &mut Settings) -> Vec { + pub fn fetch_events(&mut self, settings: &mut Settings, message_sender: &Sender) -> Vec { let mut events = vec![]; events.append(&mut self.supplement_events); // Refresh ui size (used when changing playstates) @@ -651,7 +656,7 @@ impl Window { } if take_screenshot { - self.take_screenshot(&settings); + self.take_screenshot(&settings, &message_sender); } if toggle_fullscreen { @@ -924,10 +929,11 @@ impl Window { pub fn send_supplement_event(&mut self, event: Event) { self.supplement_events.push(event) } - pub fn take_screenshot(&mut self, settings: &Settings) { + pub fn take_screenshot(&mut self, settings: &Settings, message_sender: &Sender) { match self.renderer.create_screenshot() { Ok(img) => { let mut path = settings.screenshots_path.clone(); + let sender = message_sender.clone(); std::thread::spawn(move || { use std::time::SystemTime; @@ -935,6 +941,10 @@ impl Window { if !path.exists() { if let Err(err) = std::fs::create_dir_all(&path) { warn!("Couldn't create folder for screenshot: {:?}", err); + let _result = sender.send(ClientEvent::Chat { + chat_type: ChatType::Meta, + message: String::from("Couldn't create folder for screenshot"), + }); } } path.push(format!( @@ -946,6 +956,20 @@ impl Window { )); if let Err(err) = img.save(&path) { warn!("Couldn't save screenshot: {:?}", err); + let _result = sender.send(ClientEvent::Chat { + chat_type: ChatType::Meta, + message: String::from("Couldn't save screenshot"), + }); + } else { + match path.to_str() { + Some(x) => { + let _result = sender.send(ClientEvent::Chat { + chat_type: ChatType::Meta, + message: format!("Screenshot saved to {}", x), + }); + }, + None => {} + } } }); }, From 09541af551bab4dd2fcf7f204112c2a3cd35fe85 Mon Sep 17 00:00:00 2001 From: Joey Maher Date: Fri, 5 Jun 2020 15:53:15 -0500 Subject: [PATCH 2/3] Moving to a single crossbeam channel as a detail of Window, using strings instead of ClientEvent, and formatting --- voxygen/src/menu/char_selection/mod.rs | 8 ++-- voxygen/src/menu/main/mod.rs | 6 +-- voxygen/src/session.rs | 18 ++++----- voxygen/src/window.rs | 53 +++++++++++++------------- 4 files changed, 38 insertions(+), 47 deletions(-) diff --git a/voxygen/src/menu/char_selection/mod.rs b/voxygen/src/menu/char_selection/mod.rs index 9feb900171..983e244d86 100644 --- a/voxygen/src/menu/char_selection/mod.rs +++ b/voxygen/src/menu/char_selection/mod.rs @@ -7,11 +7,11 @@ use crate::{ window::Event as WinEvent, Direction, GlobalState, PlayState, PlayStateResult, }; -use client::{self, Client, Event as ClientEvent}; +use client::{self, Client}; use common::{assets, clock::Clock, comp, msg::ClientState, state::DeltaTime}; use log::error; use specs::WorldExt; -use std::{cell::RefCell, rc::Rc, time::Duration, sync::mpsc}; +use std::{cell::RefCell, rc::Rc, time::Duration}; use ui::CharSelectionUi; pub struct CharSelectionState { @@ -42,12 +42,10 @@ impl PlayState for CharSelectionState { // Load the player's character list self.client.borrow_mut().load_character_list(); - let (message_sender, _message_receiver): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); - let mut current_client_state = self.client.borrow().get_client_state(); while let ClientState::Pending | ClientState::Registered = current_client_state { // Handle window events - for event in global_state.window.fetch_events(&mut global_state.settings, &message_sender) { + for event in global_state.window.fetch_events(&mut global_state.settings) { if self.char_selection_ui.handle_event(event.clone()) { continue; } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index e46d492a45..27382c26ea 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -11,8 +11,6 @@ use log::{error, warn}; #[cfg(feature = "singleplayer")] use std::time::Duration; use ui::{Event as MainMenuEvent, MainMenuUi}; -use std::sync::mpsc; -use client::{self, Event as ClientEvent}; pub struct MainMenuState { main_menu_ui: MainMenuUi, @@ -49,11 +47,9 @@ impl PlayState for MainMenuState { &crate::i18n::i18n_asset_key(&global_state.settings.language.selected_language), ); - let (message_sender, _message_receiver): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); - loop { // Handle window events. - for event in global_state.window.fetch_events(&mut global_state.settings, &message_sender) { + for event in global_state.window.fetch_events(&mut global_state.settings) { match event { Event::Close => return PlayStateResult::Shutdown, // Pass events to ui. diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 2722547c98..e66fd4b83a 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -9,7 +9,7 @@ use crate::{ window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; -use client::{self, Client, Event::Chat, Event as ClientEvent}; +use client::{self, Client, Event::Chat}; use common::{ assets::{load_watched, watch}, clock::Clock, @@ -23,7 +23,7 @@ use common::{ }; use log::error; use specs::{Join, WorldExt}; -use std::{cell::RefCell, rc::Rc, time::Duration, sync::mpsc}; +use std::{cell::RefCell, rc::Rc, time::Duration}; use vek::*; /// The action to perform after a tick @@ -143,8 +143,6 @@ impl PlayState for SessionState { let mut ori = self.scene.camera().get_orientation(); let mut free_look = false; - let (message_sender, message_receiver): (mpsc::Sender, mpsc::Receiver) = mpsc::channel(); - // Game loop let mut current_client_state = self.client.borrow().get_client_state(); while let ClientState::Pending | ClientState::Character = current_client_state { @@ -226,14 +224,8 @@ impl PlayState for SessionState { .unwrap_or(false) })); - // Receive any ClientEvents sent through the message channel - match message_receiver.try_recv() { - Ok(message_event) => self.hud.new_message(message_event), - Err(_x) => {}, - }; - // Handle window events. - for event in global_state.window.fetch_events(&mut global_state.settings, &message_sender) { + for event in global_state.window.fetch_events(&mut global_state.settings) { // Pass all events to the ui first. if self.hud.handle_event(event.clone(), global_state) { continue; @@ -461,6 +453,10 @@ impl PlayState for SessionState { self.scene.handle_input_event(Event::AnalogGameInput(other)); }, }, + Event::ScreenshotSaved(screenshot_message) => self.hud.new_message(Chat { + chat_type: ChatType::Meta, + message: screenshot_message, + }), // Pass all other events to the scene event => { diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index 1ef15d6895..b224f11f78 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -7,14 +7,10 @@ use crate::{ use gilrs::{EventType, Gilrs}; use hashbrown::HashMap; +use crossbeam::channel; use log::{error, warn}; use serde_derive::{Deserialize, Serialize}; use std::fmt; -use std::sync::mpsc::Sender; -use client::{self, Event as ClientEvent}; -use common::{ - ChatType, -}; use vek::*; /// Represents a key that the game recognises after input mapping. @@ -191,6 +187,8 @@ pub enum Event { AnalogMenuInput(AnalogMenuInput), /// Update of the analog inputs recognized by the game AnalogGameInput(AnalogGameInput), + /// We tried to save a screenshot + ScreenshotSaved(String), } pub type MouseButton = winit::MouseButton; @@ -396,6 +394,8 @@ pub struct Window { controller_settings: ControllerSettings, cursor_position: winit::dpi::LogicalPosition, mouse_emulation_vec: Vec2, + message_channel_sender: channel::Sender, + message_channel_receiver: channel::Receiver, } impl Window { @@ -450,6 +450,11 @@ impl Window { let controller_settings = ControllerSettings::from(&settings.controller); + let (message_sender, message_receiver): ( + channel::Sender, + channel::Receiver, + ) = channel::unbounded::(); + let mut this = Self { events_loop, renderer: Renderer::new( @@ -477,6 +482,8 @@ impl Window { controller_settings, cursor_position: winit::dpi::LogicalPosition::new(0.0, 0.0), mouse_emulation_vec: Vec2::zero(), + message_channel_sender: message_sender, + message_channel_receiver: message_receiver, }; this.fullscreen(settings.graphics.fullscreen); @@ -488,7 +495,7 @@ impl Window { pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer } - pub fn fetch_events(&mut self, settings: &mut Settings, message_sender: &Sender) -> Vec { + pub fn fetch_events(&mut self, settings: &mut Settings) -> Vec { let mut events = vec![]; events.append(&mut self.supplement_events); // Refresh ui size (used when changing playstates) @@ -497,6 +504,12 @@ impl Window { self.needs_refresh_resize = false; } + // Receive any messages sent through the message channel + match self.message_channel_receiver.try_recv() { + Ok(message_string) => events.push(Event::ScreenshotSaved(message_string)), + Err(_x) => {}, + } + // Copy data that is needed by the events closure to avoid lifetime errors. // TODO: Remove this if/when the compiler permits it. let cursor_grabbed = self.cursor_grabbed; @@ -656,7 +669,7 @@ impl Window { } if take_screenshot { - self.take_screenshot(&settings, &message_sender); + self.take_screenshot(&settings); } if toggle_fullscreen { @@ -929,11 +942,11 @@ impl Window { pub fn send_supplement_event(&mut self, event: Event) { self.supplement_events.push(event) } - pub fn take_screenshot(&mut self, settings: &Settings, message_sender: &Sender) { + pub fn take_screenshot(&mut self, settings: &Settings) { match self.renderer.create_screenshot() { Ok(img) => { let mut path = settings.screenshots_path.clone(); - let sender = message_sender.clone(); + let sender = self.message_channel_sender.clone(); std::thread::spawn(move || { use std::time::SystemTime; @@ -941,10 +954,8 @@ impl Window { if !path.exists() { if let Err(err) = std::fs::create_dir_all(&path) { warn!("Couldn't create folder for screenshot: {:?}", err); - let _result = sender.send(ClientEvent::Chat { - chat_type: ChatType::Meta, - message: String::from("Couldn't create folder for screenshot"), - }); + let _result = + sender.send(String::from("Couldn't create folder for screenshot")); } } path.push(format!( @@ -956,20 +967,10 @@ impl Window { )); if let Err(err) = img.save(&path) { warn!("Couldn't save screenshot: {:?}", err); - let _result = sender.send(ClientEvent::Chat { - chat_type: ChatType::Meta, - message: String::from("Couldn't save screenshot"), - }); + let _result = sender.send(String::from("Couldn't save screenshot")); } else { - match path.to_str() { - Some(x) => { - let _result = sender.send(ClientEvent::Chat { - chat_type: ChatType::Meta, - message: format!("Screenshot saved to {}", x), - }); - }, - None => {} - } + let _result = + sender.send(format!("Screenshot saved to {}", path.to_string_lossy())); } }); }, From 76f7f4fd989a6ba17f42b07d7c4a88f4c135e4e1 Mon Sep 17 00:00:00 2001 From: Joey Maher Date: Fri, 5 Jun 2020 16:33:39 -0500 Subject: [PATCH 3/3] Renaming and adding comments --- voxygen/src/session.rs | 2 +- voxygen/src/window.rs | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index e66fd4b83a..4593e94c22 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -453,7 +453,7 @@ impl PlayState for SessionState { self.scene.handle_input_event(Event::AnalogGameInput(other)); }, }, - Event::ScreenshotSaved(screenshot_message) => self.hud.new_message(Chat { + Event::ScreenshotMessage(screenshot_message) => self.hud.new_message(Chat { chat_type: ChatType::Meta, message: screenshot_message, }), diff --git a/voxygen/src/window.rs b/voxygen/src/window.rs index b224f11f78..78f60f3db2 100644 --- a/voxygen/src/window.rs +++ b/voxygen/src/window.rs @@ -188,7 +188,7 @@ pub enum Event { /// Update of the analog inputs recognized by the game AnalogGameInput(AnalogGameInput), /// We tried to save a screenshot - ScreenshotSaved(String), + ScreenshotMessage(String), } pub type MouseButton = winit::MouseButton; @@ -394,8 +394,9 @@ pub struct Window { controller_settings: ControllerSettings, cursor_position: winit::dpi::LogicalPosition, mouse_emulation_vec: Vec2, - message_channel_sender: channel::Sender, - message_channel_receiver: channel::Receiver, + // Currently used to send and receive screenshot result messages + message_sender: channel::Sender, + message_receiver: channel::Receiver, } impl Window { @@ -482,8 +483,9 @@ impl Window { controller_settings, cursor_position: winit::dpi::LogicalPosition::new(0.0, 0.0), mouse_emulation_vec: Vec2::zero(), - message_channel_sender: message_sender, - message_channel_receiver: message_receiver, + // Currently used to send and receive screenshot result messages + message_sender, + message_receiver, }; this.fullscreen(settings.graphics.fullscreen); @@ -505,10 +507,9 @@ impl Window { } // Receive any messages sent through the message channel - match self.message_channel_receiver.try_recv() { - Ok(message_string) => events.push(Event::ScreenshotSaved(message_string)), - Err(_x) => {}, - } + self.message_receiver + .try_iter() + .for_each(|message| events.push(Event::ScreenshotMessage(message))); // Copy data that is needed by the events closure to avoid lifetime errors. // TODO: Remove this if/when the compiler permits it. @@ -946,7 +947,7 @@ impl Window { match self.renderer.create_screenshot() { Ok(img) => { let mut path = settings.screenshots_path.clone(); - let sender = self.message_channel_sender.clone(); + let sender = self.message_sender.clone(); std::thread::spawn(move || { use std::time::SystemTime;