diff --git a/Cargo.lock b/Cargo.lock index e21d2fbd2e..4e841742f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -687,16 +687,16 @@ dependencies = [ [[package]] name = "clap" -version = "3.1.10" +version = "3.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3124f3f75ce09e22d1410043e1e24f2ecc44fad3afe4f08408f1f7663d68da2b" +checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd" dependencies = [ "atty", "bitflags", "clap_derive", "clap_lex", "indexmap", - "lazy_static", + "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.15.0", @@ -704,9 +704,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "3.1.7" +version = "3.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3aab4734e083b809aaf5794e14e756d1c798d2c69c7f7de7a09a2f5214993c1" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" dependencies = [ "heck 0.4.0", "proc-macro-error", @@ -717,9 +717,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.1.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "189ddd3b5d32a70b35e7686054371742a937b0d99128e76dde6340210e966669" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] @@ -4016,9 +4016,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "oorandom" @@ -6501,7 +6501,7 @@ dependencies = [ "async-channel", "authc", "byteorder", - "clap 3.1.10", + "clap 3.2.20", "hashbrown 0.12.0", "image", "num 0.4.0", @@ -6688,7 +6688,7 @@ dependencies = [ "bincode", "bitflags", "bytes", - "clap 3.1.10", + "clap 3.2.20", "criterion", "crossbeam-channel", "futures-core", @@ -6811,7 +6811,7 @@ name = "veloren-server-cli" version = "0.13.0" dependencies = [ "ansi-parser", - "clap 3.1.10", + "clap 3.2.20", "crossterm 0.23.2", "lazy_static", "mimalloc", @@ -6840,6 +6840,7 @@ dependencies = [ "bytemuck", "chrono", "chumsky", + "clap 3.2.20", "cmake", "conrod_core", "conrod_winit", @@ -6962,7 +6963,7 @@ dependencies = [ name = "veloren-voxygen-i18n" version = "0.13.0" dependencies = [ - "clap 3.1.10", + "clap 3.2.20", "deunicode", "fluent", "fluent-bundle", @@ -6983,7 +6984,7 @@ dependencies = [ "arr_macro", "bincode", "bitvec", - "clap 3.1.10", + "clap 3.2.20", "criterion", "csv", "deflate", diff --git a/assets/voxygen/element/ui/generic/buttons/unlock.png b/assets/voxygen/element/ui/generic/buttons/unlock.png new file mode 100644 index 0000000000..cb4c4fef50 --- /dev/null +++ b/assets/voxygen/element/ui/generic/buttons/unlock.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:100af979da48f36d3e27abd4feae5a1cc50d7aa8c8ca07527fcab5a03289ad87 +size 354 diff --git a/assets/voxygen/element/ui/generic/buttons/unlock_hover.png b/assets/voxygen/element/ui/generic/buttons/unlock_hover.png new file mode 100644 index 0000000000..6ec528cfb0 --- /dev/null +++ b/assets/voxygen/element/ui/generic/buttons/unlock_hover.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ceaf4c86825b72c22da051bc280ff229fc5eb0921011003321772e8abcde4c7e +size 392 diff --git a/assets/voxygen/element/ui/generic/buttons/unlock_press.png b/assets/voxygen/element/ui/generic/buttons/unlock_press.png new file mode 100644 index 0000000000..3137577145 --- /dev/null +++ b/assets/voxygen/element/ui/generic/buttons/unlock_press.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48a857665864c0eed8372aa84a49ac9585008acd9d05ee989897bb45e9945eb6 +size 389 diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 48d4026f18..e8b08ba855 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -91,6 +91,9 @@ gilrs = {version = "0.8.0", features = ["serde-serialize"]} # Singleplayer server = { package = "veloren-server", path = "../server", optional = true, default-features = false, features = ["worldgen"] } +# CLI +clap = { version = "3.2.20", features = ["derive"] } + # Utility assets_manager = {version = "0.8", features = ["ab_glyph"]} backtrace = "0.3.40" diff --git a/voxygen/src/cli.rs b/voxygen/src/cli.rs new file mode 100644 index 0000000000..2aefc2abe9 --- /dev/null +++ b/voxygen/src/cli.rs @@ -0,0 +1,19 @@ +//! NOTE: Some of these arguments are used by airshipper, so those needs to be +//! kept fairly stable (probably with some sort of migration period if we need +//! to modify the name or semantics). +//! +//! The arguments used by airshipper are: +//! * `server` +//! +//! Airshipper should only use arguments listed above! Since we will not try to +//! be careful about their stability otherwise. +use clap::Parser; + +#[derive(Parser)] +pub struct Args { + /// Value to auto-fill into the server field. + /// + /// This allows passing in server selection performed in airshipper. + #[clap(short, long)] + pub server: Option, +} diff --git a/voxygen/src/main.rs b/voxygen/src/main.rs index 4e3f8ae100..8bde511768 100644 --- a/voxygen/src/main.rs +++ b/voxygen/src/main.rs @@ -2,6 +2,8 @@ #![feature(bool_to_option)] #![recursion_limit = "2048"] +mod cli; + #[cfg(all( target_os = "windows", not(feature = "tracy-memory"), @@ -182,6 +184,10 @@ fn main() { default_hook(panic_info); })); + // Process CLI arguments + use clap::Parser; + let args = cli::Args::parse(); + // Setup tokio runtime use common::consts::MIN_RECOMMENDED_TOKIO_THREADS; use std::sync::{ @@ -317,5 +323,5 @@ fn main() { discord, }; - run::run(global_state, event_loop); + run::run(global_state, event_loop, args.server); } diff --git a/voxygen/src/menu/main/mod.rs b/voxygen/src/menu/main/mod.rs index 7b7d3cfa1e..8be7c547da 100644 --- a/voxygen/src/menu/main/mod.rs +++ b/voxygen/src/menu/main/mod.rs @@ -54,9 +54,9 @@ pub struct MainMenuState { impl MainMenuState { /// Create a new `MainMenuState`. - pub fn new(global_state: &mut GlobalState) -> Self { + pub fn new(global_state: &mut GlobalState, server: Option) -> Self { Self { - main_menu_ui: MainMenuUi::new(global_state), + main_menu_ui: MainMenuUi::new(global_state, server), init: InitState::None, scene: Scene::new(global_state.window.renderer_mut()), } diff --git a/voxygen/src/menu/main/ui/login.rs b/voxygen/src/menu/main/ui/login.rs index 56ccc16ac8..b924f4889a 100644 --- a/voxygen/src/menu/main/ui/login.rs +++ b/voxygen/src/menu/main/ui/login.rs @@ -6,7 +6,7 @@ use crate::ui::{ style, widget::{ compound_graphic::{CompoundGraphic, Graphic}, - BackgroundContainer, Image, Padding, + AspectRatioContainer, BackgroundContainer, Image, Padding, }, Element, }, @@ -55,6 +55,7 @@ impl Screen { &mut self, fonts: &Fonts, imgs: &Imgs, + server_field_locked: bool, login_info: &LoginInfo, error: Option<&str>, i18n: &Localization, @@ -64,14 +65,19 @@ impl Screen { button_style: style::button::Style, version: &str, ) -> Element { - let buttons = Column::with_children(vec![ - neat_button( + let mut buttons = Vec::new(); + // If the server field is locked, we don't want to show the server selection + // list! + if !server_field_locked { + buttons.push(neat_button( &mut self.servers_button, i18n.get_msg("common-servers"), FILL_FRAC_ONE, button_style, Some(Message::ShowServers), - ), + )) + } + buttons.extend([ // neat_button( // &mut self.settings_button, // i18n.get_msg("common-settings"), @@ -100,15 +106,17 @@ impl Screen { button_style, Some(Message::Quit), ), - ]) - .width(Length::Fill) - .max_width(100) - .spacing(5); + ]); - let buttons = Container::new(buttons) - .width(Length::Fill) - .height(Length::Fill) - .align_y(Align::End); + let buttons = Container::new( + Column::with_children(buttons) + .width(Length::Fill) + .max_width(100) + .spacing(5), + ) + .width(Length::Fill) + .height(Length::Fill) + .align_y(Align::End); let intro_text = i18n.get_msg("main-login_process"); @@ -173,8 +181,14 @@ impl Screen { button_style, ) } else { - self.banner - .view(fonts, imgs, login_info, i18n, button_style) + self.banner.view( + fonts, + imgs, + server_field_locked, + login_info, + i18n, + button_style, + ) }; let central_column = Container::new(central_content) @@ -330,6 +344,8 @@ pub struct LoginBanner { multiplayer_button: button::State, #[cfg(feature = "singleplayer")] singleplayer_button: button::State, + + unlock_server_field_button: button::State, } impl LoginBanner { @@ -342,6 +358,8 @@ impl LoginBanner { multiplayer_button: Default::default(), #[cfg(feature = "singleplayer")] singleplayer_button: Default::default(), + + unlock_server_field_button: Default::default(), } } @@ -349,12 +367,56 @@ impl LoginBanner { &mut self, fonts: &Fonts, imgs: &Imgs, + server_field_locked: bool, login_info: &LoginInfo, i18n: &Localization, button_style: style::button::Style, ) -> Element { let input_text_size = fonts.cyri.scale(INPUT_TEXT_SIZE); + let server_field: Element = if server_field_locked { + let unlock_style = style::button::Style::new(imgs.unlock) + .hover_image(imgs.unlock_hover) + .press_image(imgs.unlock_press); + + let unlock_button = Button::new( + &mut self.unlock_server_field_button, + Space::new(Length::Fill, Length::Fill), + ) + .style(unlock_style) + .width(Length::Fill) + .height(Length::Fill) + .on_press(Message::UnlockServerField); + + let container = AspectRatioContainer::new(unlock_button); + let container = match unlock_style.active().0 { + Some((img, _)) => container.ratio_of_image(img), + None => container, + }; + + Row::with_children(vec![ + Text::new(&login_info.server) + .size(input_text_size) + .width(Length::Fill) + .height(Length::Shrink) + .into(), + container.into(), + ]) + .align_items(Align::Center) + .height(Length::Fill) + .into() + } else { + TextInput::new( + &mut self.server, + &i18n.get_msg("main-server"), + &login_info.server, + Message::Server, + ) + .size(input_text_size) + .on_submit(Message::Multiplayer) + .into() + }; + let banner_content = Column::with_children(vec![ Column::with_children(vec![ BackgroundContainer::new( @@ -392,16 +454,9 @@ impl LoginBanner { Image::new(imgs.input_bg) .width(Length::Units(INPUT_WIDTH)) .fix_aspect_ratio(), - TextInput::new( - &mut self.server, - &i18n.get_msg("main-server"), - &login_info.server, - Message::Server, - ) - .size(input_text_size) - .on_submit(Message::Multiplayer), + server_field, ) - .padding(Padding::new().horizontal(7).top(5)) + .padding(Padding::new().horizontal(7).vertical(5)) .into(), ]) .spacing(5) diff --git a/voxygen/src/menu/main/ui/mod.rs b/voxygen/src/menu/main/ui/mod.rs index f68ffabafc..eb514c3c22 100644 --- a/voxygen/src/menu/main/ui/mod.rs +++ b/voxygen/src/menu/main/ui/mod.rs @@ -53,6 +53,9 @@ image_ids_ice! { selection: "voxygen.element.ui.generic.frames.selection", selection_hover: "voxygen.element.ui.generic.frames.selection_hover", selection_press: "voxygen.element.ui.generic.frames.selection_press", + unlock: "voxygen.element.ui.generic.buttons.unlock", + unlock_hover: "voxygen.element.ui.generic.buttons.unlock_hover", + unlock_press: "voxygen.element.ui.generic.buttons.unlock_press", } } @@ -137,6 +140,10 @@ struct Controls { alpha: String, credits: Credits, + // If a server address was provided via cli argument we hide the server list button and replace + // the server field with a plain label (with a button to exit this mode and freely edit the + // field). + server_field_locked: bool, selected_server_index: Option, login_info: LoginInfo, @@ -157,6 +164,7 @@ enum Message { #[cfg(feature = "singleplayer")] Singleplayer, Multiplayer, + UnlockServerField, LanguageChanged(usize), OpenLanguageMenu, Username(String), @@ -180,6 +188,7 @@ impl Controls { bg_img: widget::image::Handle, i18n: LocalizationHandle, settings: &Settings, + server: Option, ) -> Self { let version = common::util::DISPLAY_VERSION_LONG.clone(); let alpha = format!("Veloren {}", common::util::DISPLAY_VERSION.as_str()); @@ -198,10 +207,11 @@ impl Controls { }; //}; + let server_field_locked = server.is_some(); let login_info = LoginInfo { username: settings.networking.username.clone(), password: String::new(), - server: settings.networking.default_server.clone(), + server: server.unwrap_or_else(|| settings.networking.default_server.clone()), }; let selected_server_index = settings .networking @@ -223,6 +233,7 @@ impl Controls { alpha, credits, + server_field_locked, selected_server_index, login_info, @@ -291,6 +302,7 @@ impl Controls { Screen::Login { screen, error } => screen.view( &self.fonts, &self.imgs, + self.server_field_locked, &self.login_info, error.as_deref(), &self.i18n.read(), @@ -386,6 +398,7 @@ impl Controls { server_address: self.login_info.server.clone(), }); }, + Message::UnlockServerField => self.server_field_locked = false, Message::Username(new_value) => self.login_info.username = new_value, Message::LanguageChanged(new_value) => { events.push(Event::ChangeLanguage(language_metadatas.remove(new_value))); @@ -495,7 +508,12 @@ impl Controls { screen.banner.password.move_cursor_to_end(); } else if screen.banner.password.is_focused() { screen.banner.password = text_input::State::new(); - screen.banner.server = text_input::State::focused(); + // Skip focusing server field if it isn't editable! + if self.server_field_locked { + screen.banner.username = text_input::State::focused(); + } else { + screen.banner.server = text_input::State::focused(); + } screen.banner.server.move_cursor_to_end(); } else if screen.banner.server.is_focused() { screen.banner.server = text_input::State::new(); @@ -517,7 +535,7 @@ pub struct MainMenuUi { } impl MainMenuUi { - pub fn new(global_state: &mut GlobalState) -> Self { + pub fn new(global_state: &mut GlobalState, server: Option) -> Self { // Load language let i18n = &global_state.i18n.read(); // TODO: don't add default font twice @@ -541,6 +559,7 @@ impl MainMenuUi { ui.add_graphic(Graphic::Image(bg_img, None)), global_state.i18n, &global_state.settings, + server, ); Self { ui, controls } diff --git a/voxygen/src/run.rs b/voxygen/src/run.rs index 64a09ecd09..8e2da638f8 100644 --- a/voxygen/src/run.rs +++ b/voxygen/src/run.rs @@ -9,9 +9,10 @@ use common_base::{prof_span, span}; use std::{mem, time::Duration}; use tracing::debug; -pub fn run(mut global_state: GlobalState, event_loop: EventLoop) { +pub fn run(mut global_state: GlobalState, event_loop: EventLoop, server: Option) { // Set up the initial play state. - let mut states: Vec> = vec![Box::new(MainMenuState::new(&mut global_state))]; + let mut states: Vec> = + vec![Box::new(MainMenuState::new(&mut global_state, server))]; states.last_mut().map(|current_state| { current_state.enter(&mut global_state, Direction::Forwards); let current_state = current_state.name();