From fb83c836a1e7100de92ebdf9d4533e07c21a3e79 Mon Sep 17 00:00:00 2001 From: Imbris Date: Thu, 4 Apr 2019 09:49:31 -0400 Subject: [PATCH] make to_server() blocking, post fixes & cleanup, use server address Former-commit-id: 7debc790e158a996ed58271d3307214e42b850bd --- .gitignore | 4 +- assets/voxygen | 2 +- common/src/net/error.rs | 74 ------- common/src/net/postbox.rs | 270 -------------------------- common/src/net/postoffice.rs | 150 -------------- voxygen/src/hud/mod.rs | 41 ++-- voxygen/src/menu/char_selection/ui.rs | 29 ++- voxygen/src/menu/main/ui.rs | 28 +-- 8 files changed, 50 insertions(+), 548 deletions(-) delete mode 100644 common/src/net/error.rs delete mode 100644 common/src/net/postbox.rs delete mode 100644 common/src/net/postoffice.rs diff --git a/.gitignore b/.gitignore index 782816ee6f..4c5213199c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,5 @@ **/server_conf.toml **/keybinds.toml assets/voxygen -UI3.rar -assets.rar *.rar -assets/voxygen + diff --git a/assets/voxygen b/assets/voxygen index e3083ec8e8..6b64a65356 160000 --- a/assets/voxygen +++ b/assets/voxygen @@ -1 +1 @@ -Subproject commit e3083ec8e8e634af8c9daed00ea82435da195979 +Subproject commit 6b64a65356c1fb4c77ad0295908f4006f75a5448 diff --git a/common/src/net/error.rs b/common/src/net/error.rs deleted file mode 100644 index c3dd162fc9..0000000000 --- a/common/src/net/error.rs +++ /dev/null @@ -1,74 +0,0 @@ -#[derive(Debug)] -pub enum PostError { - InvalidMessage, - InternalError, - Disconnected, -} - -#[derive(Debug)] -pub enum PostErrorInternal { - Io(std::io::Error), - Serde(bincode::Error), - ChannelRecv(std::sync::mpsc::TryRecvError), - ChannelSend, // Empty because I couldn't figure out how to handle generic type in mpsc::TrySendError properly - MsgSizeLimitExceeded, - MioError, -} - -impl<'a, T: Into<&'a PostErrorInternal>> From for PostError { - fn from(err: T) -> Self { - match err.into() { - // TODO: Are I/O errors always disconnect errors? - PostErrorInternal::Io(_) => PostError::Disconnected, - PostErrorInternal::Serde(_) => PostError::InvalidMessage, - PostErrorInternal::MsgSizeLimitExceeded => PostError::InvalidMessage, - PostErrorInternal::MioError => PostError::InternalError, - PostErrorInternal::ChannelRecv(_) => PostError::InternalError, - PostErrorInternal::ChannelSend => PostError::InternalError, - } - } -} - -impl From for PostError { - fn from(err: PostErrorInternal) -> Self { - (&err).into() - } -} - -impl From for PostErrorInternal { - fn from(err: std::io::Error) -> Self { - PostErrorInternal::Io(err) - } -} - -impl From for PostErrorInternal { - fn from(err: bincode::Error) -> Self { - PostErrorInternal::Serde(err) - } -} - -impl From for PostErrorInternal { - fn from(err: std::sync::mpsc::TryRecvError) -> Self { - PostErrorInternal::ChannelRecv(err) - } -} - - - -impl From for PostError { - fn from(err: std::io::Error) -> Self { - (&PostErrorInternal::from(err)).into() - } -} - -impl From for PostError { - fn from(err: bincode::Error) -> Self { - (&PostErrorInternal::from(err)).into() - } -} - -impl From for PostError { - fn from(err: std::sync::mpsc::TryRecvError) -> Self { - (&PostErrorInternal::from(err)).into() - } -} diff --git a/common/src/net/postbox.rs b/common/src/net/postbox.rs deleted file mode 100644 index 21d9466637..0000000000 --- a/common/src/net/postbox.rs +++ /dev/null @@ -1,270 +0,0 @@ -// Standard -use std::{ - collections::VecDeque, - convert::TryFrom, - io::{ - ErrorKind, - Read, - }, - net::SocketAddr, - thread, - time::Duration, - sync::mpsc::TryRecvError, -}; - -// External -use bincode; -use mio::{net::TcpStream, Events, Poll, PollOpt, Ready, Token}; -use mio_extras::channel::{channel, Receiver, Sender}; - -// Crate -use super::{ - data::ControlMsg, - error::{ - PostError, - PostErrorInternal, - }, - PostRecv, - PostSend, -}; - -// Constants -const CTRL_TOKEN: Token = Token(0); // Token for thread control messages -const DATA_TOKEN: Token = Token(1); // Token for thread data exchange -const CONN_TOKEN: Token = Token(2); // Token for TcpStream for the PostBox child thread -const MESSAGE_SIZE_CAP: u64 = 1 << 20; // Maximum accepted length of a packet - -/// A high-level wrapper of [`TcpStream`](mio::net::TcpStream). -/// [`PostBox`] takes care of serializing sent packets and deserializing received packets in the background, providing a simple API for sending and receiving objects over network. -pub struct PostBox -where - S: PostSend, - R: PostRecv, -{ - handle: Option>, - ctrl: Sender, - recv: Receiver>, - send: Sender, - poll: Poll, - err: Option, -} - -impl PostBox -where - S: PostSend, - R: PostRecv, -{ - /// Creates a new [`PostBox`] connected to specified address, meant to be used by the client - pub fn to_server>(addr: A) -> Result, PostError> { - let connection = TcpStream::connect(&addr.into())?; - Self::from_tcpstream(connection) - } - - /// Creates a new [`PostBox`] from an existing connection, meant to be used by [`PostOffice`](super::PostOffice) on the server - pub fn from_tcpstream(connection: TcpStream) -> Result, PostError> { - let (ctrl_tx, ctrl_rx) = channel(); // Control messages - let (send_tx, send_rx) = channel(); // main thread -[data to be serialized and sent]> worker thread - let (recv_tx, recv_rx) = channel(); // main thread <[received and deserialized data]- worker thread - let thread_poll = Poll::new().unwrap(); - let postbox_poll = Poll::new().unwrap(); - thread_poll - .register(&connection, CONN_TOKEN, Ready::readable(), PollOpt::edge()) - .unwrap(); - thread_poll - .register(&ctrl_rx, CTRL_TOKEN, Ready::readable(), PollOpt::edge()) - .unwrap(); - thread_poll - .register(&send_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge()) - .unwrap(); - postbox_poll - .register(&recv_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge()) - .unwrap(); - let handle = thread::Builder::new() - .name("postbox_worker".into()) - .spawn(move || postbox_thread(connection, ctrl_rx, send_rx, recv_tx, thread_poll))?; - Ok(PostBox { - handle: Some(handle), - ctrl: ctrl_tx, - recv: recv_rx, - send: send_tx, - poll: postbox_poll, - err: None, - }) - } - - /// Return an `Option` indicating the current status of the `PostBox`. - pub fn status(&self) -> Option { - self.err.as_ref().map(|err| err.into()) - } - - /// Non-blocking sender method - pub fn send(&mut self, data: S) -> Result<(), PostError> { - match &mut self.err { - err @ None => if let Err(_) = self.send.send(data) { - *err = Some(PostErrorInternal::MioError); - Err(err.as_ref().unwrap().into()) - } else { - Ok(()) - }, - err => Err(err.as_ref().unwrap().into()), - } - } - - /// Non-blocking receiver method returning an iterator over already received and deserialized objects - /// # Errors - /// If the other side disconnects PostBox won't realize that until you try to send something - pub fn new_messages(&mut self) -> impl ExactSizeIterator { - let mut events = Events::with_capacity(4096); - let mut items = VecDeque::new(); - - // If an error occured, or previously occured, just give up - if let Some(_) = self.err { - return items.into_iter(); - } else if let Err(err) = self.poll.poll(&mut events, Some(Duration::new(0, 0))) { - self.err = Some(err.into()); - return items.into_iter(); - } - - for event in events { - match event.token() { - DATA_TOKEN => loop { - match self.recv.try_recv() { - Ok(Ok(item)) => items.push_back(item), - Err(TryRecvError::Empty) => break, - Err(err) => self.err = Some(err.into()), - Ok(Err(err)) => self.err = Some(err.into()), - } - }, - _ => (), - } - } - - items.into_iter() - } -} - -fn postbox_thread( - mut connection: TcpStream, - ctrl_rx: Receiver, - send_rx: Receiver, - recv_tx: Sender>, - poll: Poll, -) where - S: PostSend, - R: PostRecv, -{ - // Receiving related variables - let mut events = Events::with_capacity(64); - let mut recv_buff = Vec::new(); - let mut recv_nextlen: u64 = 0; - loop { - let mut disconnected = false; - poll.poll(&mut events, Some(Duration::from_millis(20))) - .expect("Failed to execute poll(), most likely fault of the OS"); - println!("FINISHED POLL!"); - for event in events.iter() { - println!("EVENT!"); - match event.token() { - CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() { - ControlMsg::Shutdown => return, - }, - CONN_TOKEN => match connection.read_to_end(&mut recv_buff) { - Ok(_) => {} - // Returned when all the data has been read - Err(ref e) if e.kind() == ErrorKind::WouldBlock => {} - Err(e) => recv_tx.send(Err(e.into())).unwrap(), - }, - DATA_TOKEN => { - let msg = send_rx.try_recv().unwrap(); - println!("Send: {:?}", msg); - let mut packet = bincode::serialize(&msg).unwrap(); - packet.splice(0..0, (packet.len() as u64).to_be_bytes().iter().cloned()); - match connection.write_bufs(&[packet.as_slice().into()]) { - Ok(_) => { println!("Sent!"); } - Err(e) => { - println!("Send error!"); - recv_tx.send(Err(e.into())).unwrap(); - } - }; - } - _ => {} - } - } - loop { - if recv_nextlen == 0 && recv_buff.len() >= 8 { - println!("Read nextlen"); - recv_nextlen = u64::from_be_bytes( - <[u8; 8]>::try_from(recv_buff.drain(0..8).collect::>().as_slice()).unwrap(), - ); - if recv_nextlen > MESSAGE_SIZE_CAP { - recv_tx.send(Err(PostErrorInternal::MsgSizeLimitExceeded)).unwrap(); - connection.shutdown(std::net::Shutdown::Both).unwrap(); - recv_buff.drain(..); - recv_nextlen = 0; - break; - } - } - if recv_buff.len() as u64 >= recv_nextlen && recv_nextlen != 0 { - match bincode::deserialize(recv_buff - .drain( - 0..usize::try_from(recv_nextlen) - .expect("Message size was larger than usize (insane message size and 32 bit OS)"), - ) - .collect::>() - .as_slice()) { - Ok(msg) => { - println!("Recv: {:?}", msg); - recv_tx - .send(Ok(msg)) - .unwrap(); - recv_nextlen = 0; - } - Err(e) => { - println!("Recv error: {:?}", e); - recv_tx.send(Err(e.into())).unwrap(); - recv_nextlen = 0; - } - } - } else { - break; - } - } - match connection.take_error().unwrap() { - Some(e) => { - if e.kind() == ErrorKind::BrokenPipe { - disconnected = true; - } - recv_tx.send(Err(e.into())).unwrap(); - } - None => {} - } - if disconnected == true { - break; - } - } - - // Loop after disconnected - loop { - poll.poll(&mut events, None) - .expect("Failed to execute poll(), most likely fault of the OS"); - for event in events.iter() { - match event.token() { - CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() { - ControlMsg::Shutdown => return, - }, - _ => {} - } - } - } -} - -impl Drop for PostBox -where - S: PostSend, - R: PostRecv, -{ - fn drop(&mut self) { - self.ctrl.send(ControlMsg::Shutdown).unwrap_or(()); - self.handle.take().map(|handle| handle.join()); - } -} diff --git a/common/src/net/postoffice.rs b/common/src/net/postoffice.rs deleted file mode 100644 index e8d70330ad..0000000000 --- a/common/src/net/postoffice.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Standard -use core::time::Duration; -use std::{ - collections::VecDeque, - net::SocketAddr, - thread, - sync::mpsc::TryRecvError, -}; - -// External -use mio::{net::TcpListener, Events, Poll, PollOpt, Ready, Token}; -use mio_extras::channel::{channel, Receiver, Sender}; - -// Crate -use super::{ - data::ControlMsg, - error::{ - PostError, - PostErrorInternal, - }, - postbox::PostBox, - PostRecv, - PostSend, -}; - -// Constants -const CTRL_TOKEN: Token = Token(0); // Token for thread control messages -const DATA_TOKEN: Token = Token(1); // Token for thread data exchange -const CONN_TOKEN: Token = Token(2); // Token for TcpStream for the PostBox child thread - -/// A high-level wrapper of [`TcpListener`](mio::net::TcpListener). -/// [`PostOffice`] listens for incoming connections in the background and wraps them into [`PostBox`]es, providing a simple non-blocking API for receiving them. -pub struct PostOffice -where - S: PostSend, - R: PostRecv, -{ - handle: Option>, - ctrl: Sender, - recv: Receiver, PostErrorInternal>>, - poll: Poll, - err: Option, -} - -impl PostOffice -where - S: PostSend, - R: PostRecv, -{ - /// Creates a new [`PostOffice`] listening on specified address - pub fn new>(addr: A) -> Result { - let listener = TcpListener::bind(&addr.into())?; - let (ctrl_tx, ctrl_rx) = channel(); - let (recv_tx, recv_rx) = channel(); - - let thread_poll = Poll::new()?; - let postbox_poll = Poll::new()?; - thread_poll.register(&listener, CONN_TOKEN, Ready::readable(), PollOpt::edge())?; - thread_poll.register(&ctrl_rx, CTRL_TOKEN, Ready::readable(), PollOpt::edge())?; - postbox_poll.register(&recv_rx, DATA_TOKEN, Ready::readable(), PollOpt::edge())?; - - let handle = thread::Builder::new() - .name("postoffice_worker".into()) - .spawn(move || postoffice_thread(listener, ctrl_rx, recv_tx, thread_poll))?; - - Ok(PostOffice { - handle: Some(handle), - ctrl: ctrl_tx, - recv: recv_rx, - poll: postbox_poll, - err: None, - }) - } - - /// Return an `Option` indicating the current status of the `PostOffice`. - pub fn status(&self) -> Option { - self.err.as_ref().map(|err| err.into()) - } - - /// Non-blocking method returning an iterator over new connections wrapped in [`PostBox`]es - pub fn new_connections( - &mut self, - ) -> impl ExactSizeIterator> { - let mut events = Events::with_capacity(256); - let mut conns = VecDeque::new(); - - // If an error occured, or previously occured, just give up - if let Some(_) = self.err { - return conns.into_iter(); - } else if let Err(err) = self.poll.poll(&mut events, Some(Duration::new(0, 0))) { - self.err = Some(err.into()); - return conns.into_iter(); - } - - for event in events { - match event.token() { - DATA_TOKEN => loop { - match self.recv.try_recv() { - Ok(Ok(conn)) => conns.push_back(conn), - Err(TryRecvError::Empty) => break, - Err(err) => self.err = Some(err.into()), - Ok(Err(err)) => self.err = Some(err.into()), - } - }, - _ => (), - } - } - conns.into_iter() - } -} - -fn postoffice_thread( - listener: TcpListener, - ctrl_rx: Receiver, - recv_tx: Sender, PostErrorInternal>>, - poll: Poll, -) where - S: PostSend, - R: PostRecv, -{ - let mut events = Events::with_capacity(256); - loop { - poll.poll(&mut events, None).expect("Failed to execute recv_poll.poll() in PostOffce receiver thread, most likely fault of the OS."); - for event in events.iter() { - match event.token() { - CTRL_TOKEN => match ctrl_rx.try_recv().unwrap() { - ControlMsg::Shutdown => return, - }, - CONN_TOKEN => { - let (conn, _addr) = listener.accept().unwrap(); - recv_tx.send(PostBox::from_tcpstream(conn) - // TODO: Is it okay to count a failure to create a postbox here as an 'internal error'? - .map_err(|_| PostErrorInternal::MioError)).unwrap(); - } - _ => (), - } - } - } -} - -impl Drop for PostOffice -where - S: PostSend, - R: PostRecv, -{ - fn drop(&mut self) { - self.ctrl.send(ControlMsg::Shutdown).unwrap_or(()); // If this fails the thread is dead already - self.handle.take().map(|handle| handle.join()); - } -} diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 3b4369019d..c6fe0a1aa3 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -7,11 +7,11 @@ use crate::{ }; use common::assets; use conrod_core::{ - color, Color, + color, image::Id as ImgId, text::font::Id as FontId, widget::{Button, Image, Rectangle, Scrollbar, Text}, - widget_ids, Colorable, Labelable, Positionable, Sizeable, Widget, + widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, }; widget_ids! { @@ -246,13 +246,13 @@ pub(self) struct Imgs { impl Imgs { fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs { let mut load = |filename| { - let fullpath: String = [ - "/voxygen/", - filename, - ].concat(); + let fullpath: String = ["/voxygen/", filename].concat(); let image = image::load_from_memory( - assets::load(fullpath.as_str()).expect("Error loading file").as_slice() - ).unwrap(); + assets::load(fullpath.as_str()) + .expect("Error loading file") + .as_slice(), + ) + .unwrap(); ui.new_image(renderer, &image).unwrap() }; Imgs { @@ -422,7 +422,7 @@ pub struct Hud { //#[inline] //pub fn rgba_bytes(r: u8, g: u8, b: u8, a: f32) -> Color { - //Color::Rgba(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a) +//Color::Rgba(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a) //} impl Hud { @@ -479,12 +479,11 @@ impl Hud { let mut events = Vec::new(); let ref mut ui_widgets = self.ui.set_widgets(); - const TEXT_COLOR: Color = Color::Rgba(0.86, 0.86, 0.86, 0.8); + const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0); const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0); const XP_COLOR: Color = Color::Rgba(0.59, 0.41, 0.67, 1.0); - if self.show_ui { // Add Bag-Space Button if self.inventorytest_button { @@ -753,7 +752,6 @@ impl Hud { .top_right_with_margins_on(self.ids.health_bar, 5.0, 0.0) .set(self.ids.health_bar_color, ui_widgets); - // Mana Bar Image::new(self.imgs.mana_bar) .w_h(1120.0 / 6.0, 96.0 / 6.0) @@ -766,7 +764,6 @@ impl Hud { .top_left_with_margins_on(self.ids.mana_bar, 5.0, 0.0) .set(self.ids.mana_bar_color, ui_widgets); - // Buffs/Debuffs // Buffs @@ -1561,39 +1558,39 @@ impl Hud { true } WinEvent::KeyDown(key) if !self.typing => match key { - Key::Map => { + Key::Map => { self.toggle_map(); true } - Key::Bag => { + Key::Bag => { self.toggle_bag(); true } - Key::QuestLog => { + Key::QuestLog => { self.toggle_questlog(); true } - Key::CharacterWindow => { + Key::CharacterWindow => { self.toggle_charwindow(); true } - Key::Social => { + Key::Social => { self.toggle_social(); true } - Key::Spellbook => { + Key::Spellbook => { self.toggle_spellbook(); true } - Key::Settings => { + Key::Settings => { self.toggle_settings(); true } - Key::Help => { + Key::Help => { self.toggle_help(); true } - Key::Interface => { + Key::Interface => { self.toggle_ui(); true } diff --git a/voxygen/src/menu/char_selection/ui.rs b/voxygen/src/menu/char_selection/ui.rs index ce89996f42..8becdd8b24 100644 --- a/voxygen/src/menu/char_selection/ui.rs +++ b/voxygen/src/menu/char_selection/ui.rs @@ -225,13 +225,13 @@ struct Imgs { impl Imgs { fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs { let mut load = |filename| { - let fullpath: String = [ - "/voxygen/", - filename, - ].concat(); + let fullpath: String = ["/voxygen/", filename].concat(); let image = image::load_from_memory( - assets::load(fullpath.as_str()).expect("Error loading file").as_slice() - ).unwrap(); + assets::load(fullpath.as_str()) + .expect("Error loading file") + .as_slice(), + ) + .unwrap(); ui.new_image(renderer, &image).unwrap() }; Imgs { @@ -345,7 +345,7 @@ pub enum Event { Play, } -const TEXT_COLOR: Color = Color::Rgba(0.86, 0.86, 0.86, 0.8); +const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); pub struct CharSelectionUi { ui: Ui, @@ -463,7 +463,7 @@ impl CharSelectionUi { .set(self.ids.test_char_l_button, ui_widgets) .was_clicked() { - self.selected_char_no = Some(1); + self.selected_char_no = Some(1); } // Veloren Logo and Alpha Version @@ -477,7 +477,7 @@ impl CharSelectionUi { .label_x(conrod_core::position::Relative::Scalar(-100.0)) .set(self.ids.v_logo, ui_widgets); - if let Some(no) = self.selected_char_no { + if let Some(no) = self.selected_char_no { // Selection_Window Image::new(self.imgs.selection_window) .w_h(522.0, 722.0) @@ -495,7 +495,6 @@ impl CharSelectionUi { .color(TEXT_COLOR) .set(self.ids.char_level, ui_widgets); - // Selected Character if no == 1 { Image::new(self.imgs.test_char_l_big) @@ -872,11 +871,11 @@ impl CharSelectionUi { Their greatest strengths are their adaptability and intelligence, which makes them allrounders in many fields."; const ORC_DESC: &str = "They are considered brutal, rude and combative. \n\ - But once you gained their trust they will be loyal friends \n\ - that follow a strict code of honor in all of their actions. \n\ - \n\ - Their warriors are masters of melee combat, but their true power \ - comes from the magical rituals of their powerful shamans."; + But once you gained their trust they will be loyal friends \n\ + that follow a strict code of honor in all of their actions. \n\ + \n\ + Their warriors are masters of melee combat, but their true power \ + comes from the magical rituals of their powerful shamans."; const DWARF_DESC: &str = "Smoking chimneys, the sound of countless hammers and hoes. \ Infinite tunnel systems to track down even the last chunk of metal in the ground. \n\ diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index 66126c6f0a..2f92fd4fa8 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -10,8 +10,7 @@ use conrod_core::{ position::Dimension, text::font::Id as FontId, widget::{text_box::Event as TextBoxEvent, Button, Image, Rectangle, Text, TextBox}, - widget_ids, Borderable, Color, - Colorable, Labelable, Positionable, Sizeable, Widget, + widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget, }; widget_ids! { @@ -58,13 +57,13 @@ impl Imgs { fn new(ui: &mut Ui, renderer: &mut Renderer) -> Imgs { // TODO: update paths let mut load = |filename| { - let fullpath: String = [ - "/voxygen/", - filename, - ].concat(); + let fullpath: String = ["/voxygen/", filename].concat(); let image = image::load_from_memory( - assets::load(fullpath.as_str()).expect("Error loading file").as_slice() - ).unwrap(); + assets::load(fullpath.as_str()) + .expect("Error loading file") + .as_slice(), + ) + .unwrap(); ui.new_image(renderer, &image).unwrap() }; Imgs { @@ -170,7 +169,7 @@ impl MainMenuUi { }); }; } - const TEXT_COLOR: Color = Color::Rgba(0.94, 0.94, 0.94, 0.8); + const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); // Username // TODO: get a lower resolution and cleaner input_bg.png Image::new(self.imgs.input_bg) @@ -193,7 +192,9 @@ impl MainMenuUi { // Note: TextBox limits the input string length to what fits in it self.username = username.to_string(); } - TextBoxEvent::Enter => { login!(); } + TextBoxEvent::Enter => { + login!(); + } } } // Login error @@ -211,8 +212,7 @@ impl MainMenuUi { .parent(ui_widgets.window) .up_from(self.ids.username_bg, 35.0) .set(self.ids.login_error_bg, ui_widgets); - text - .middle_of(self.ids.login_error_bg) + text.middle_of(self.ids.login_error_bg) .set(self.ids.login_error, ui_widgets); } // Server address @@ -235,7 +235,9 @@ impl MainMenuUi { TextBoxEvent::Update(server_address) => { self.server_address = server_address.to_string(); } - TextBoxEvent::Enter => { login!(); } + TextBoxEvent::Enter => { + login!(); + } } } // Login button