mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added rules menu when connecting to server
This commit is contained in:
parent
3d9ab445d9
commit
f99bf91617
@ -82,6 +82,8 @@ main-servers-stream_error = Client connection/compression/(de)serialization erro
|
||||
main-servers-database_error = Server database error: { $raw_error }
|
||||
main-servers-persistence_error = Server persistence error (Probably Asset/Character Data related): { $raw_error }
|
||||
main-servers-other_error = Server general error: { $raw_error }
|
||||
main-server-rules = This server has rules that must be accepted
|
||||
main-server-rules-seen-before = These rules have changed since the last time you accepted them
|
||||
main-credits = Credits
|
||||
main-credits-created_by = created by
|
||||
main-credits-music = Music
|
||||
|
@ -59,6 +59,8 @@ impl CharSelectionState {
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn client(&self) -> &RefCell<Client> { &self.client }
|
||||
}
|
||||
|
||||
impl PlayState for CharSelectionState {
|
||||
|
@ -1,8 +1,7 @@
|
||||
mod client_init;
|
||||
mod scene;
|
||||
mod ui;
|
||||
|
||||
use super::char_selection::CharSelectionState;
|
||||
use super::{char_selection::CharSelectionState, dummy_scene::Scene, server_info::ServerInfoState};
|
||||
#[cfg(feature = "singleplayer")]
|
||||
use crate::singleplayer::SingleplayerState;
|
||||
use crate::{
|
||||
@ -20,7 +19,6 @@ use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
||||
use common::comp;
|
||||
use common_base::span;
|
||||
use i18n::LocalizationHandle;
|
||||
use scene::Scene;
|
||||
#[cfg(feature = "singleplayer")]
|
||||
use server::ServerInitStage;
|
||||
use std::sync::Arc;
|
||||
@ -294,10 +292,24 @@ impl PlayState for MainMenuState {
|
||||
core::mem::replace(&mut self.init, InitState::None)
|
||||
{
|
||||
self.main_menu_ui.connected();
|
||||
return PlayStateResult::Push(Box::new(CharSelectionState::new(
|
||||
|
||||
let server_info = client.server_info().clone();
|
||||
|
||||
let char_select = CharSelectionState::new(
|
||||
global_state,
|
||||
std::rc::Rc::new(std::cell::RefCell::new(*client)),
|
||||
)));
|
||||
);
|
||||
|
||||
let new_state = ServerInfoState::try_from_server_info(
|
||||
global_state,
|
||||
self.main_menu_ui.bg_img_spec(),
|
||||
char_select,
|
||||
server_info,
|
||||
)
|
||||
.map(|s| Box::new(s) as _)
|
||||
.unwrap_or_else(|s| Box::new(s) as _);
|
||||
|
||||
return PlayStateResult::Push(new_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ impl Showing {
|
||||
}
|
||||
}
|
||||
|
||||
struct Controls {
|
||||
pub struct Controls {
|
||||
fonts: Fonts,
|
||||
imgs: Imgs,
|
||||
bg_img: widget::image::Handle,
|
||||
@ -677,6 +677,7 @@ pub struct MainMenuUi {
|
||||
// TODO: re add this
|
||||
// tip_no: u16,
|
||||
controls: Controls,
|
||||
bg_img_spec: &'static str,
|
||||
}
|
||||
|
||||
impl MainMenuUi {
|
||||
@ -707,8 +708,14 @@ impl MainMenuUi {
|
||||
server,
|
||||
);
|
||||
|
||||
Self { ui, controls }
|
||||
Self {
|
||||
ui,
|
||||
controls,
|
||||
bg_img_spec,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bg_img_spec(&self) -> &'static str { self.bg_img_spec }
|
||||
|
||||
pub fn update_language(&mut self, i18n: LocalizationHandle, settings: &Settings) {
|
||||
self.controls.i18n = i18n;
|
||||
|
@ -1,2 +1,4 @@
|
||||
pub mod char_selection;
|
||||
pub mod dummy_scene;
|
||||
pub mod main;
|
||||
pub mod server_info;
|
||||
|
336
voxygen/src/menu/server_info/mod.rs
Normal file
336
voxygen/src/menu/server_info/mod.rs
Normal file
@ -0,0 +1,336 @@
|
||||
use super::{char_selection::CharSelectionState, dummy_scene::Scene};
|
||||
use crate::{
|
||||
render::{Drawer, GlobalsBindGroup},
|
||||
settings::Settings,
|
||||
ui::{
|
||||
fonts::IcedFonts as Fonts,
|
||||
ice::{component::neat_button, load_font, style, widget, Element, IcedUi as Ui},
|
||||
img_ids::ImageGraphic,
|
||||
Graphic,
|
||||
},
|
||||
window::{self, Event},
|
||||
Direction, GlobalState, PlayState, PlayStateResult,
|
||||
};
|
||||
use client::ServerInfo;
|
||||
use common::assets::{self, AssetExt};
|
||||
use common_base::span;
|
||||
use i18n::LocalizationHandle;
|
||||
use iced::{
|
||||
button, scrollable, Align, Column, Container, HorizontalAlignment, Length, Row, Scrollable,
|
||||
VerticalAlignment,
|
||||
};
|
||||
use std::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
image_ids_ice! {
|
||||
struct Imgs {
|
||||
<ImageGraphic>
|
||||
button: "voxygen.element.ui.generic.buttons.button",
|
||||
button_hover: "voxygen.element.ui.generic.buttons.button_hover",
|
||||
button_press: "voxygen.element.ui.generic.buttons.button_press",
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Controls {
|
||||
fonts: Fonts,
|
||||
imgs: Imgs,
|
||||
i18n: LocalizationHandle,
|
||||
bg_img: widget::image::Handle,
|
||||
|
||||
accept_button: button::State,
|
||||
decline_button: button::State,
|
||||
scrollable: scrollable::State,
|
||||
server_info: ServerInfo,
|
||||
seen_before: bool,
|
||||
}
|
||||
|
||||
pub struct ServerInfoState {
|
||||
ui: Ui,
|
||||
scene: Scene,
|
||||
controls: Controls,
|
||||
char_select: Option<CharSelectionState>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Message {
|
||||
Accept,
|
||||
Decline,
|
||||
}
|
||||
|
||||
fn rules_hash(rules: &Option<String>) -> u64 {
|
||||
let mut hasher = DefaultHasher::default();
|
||||
rules.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
impl ServerInfoState {
|
||||
/// Create a new `MainMenuState`.
|
||||
pub fn try_from_server_info(
|
||||
global_state: &mut GlobalState,
|
||||
bg_img_spec: &'static str,
|
||||
char_select: CharSelectionState,
|
||||
server_info: ServerInfo,
|
||||
) -> Result<Self, CharSelectionState> {
|
||||
let server = global_state.profile.servers.get(&server_info.name);
|
||||
|
||||
// If there are no rules, or we've already accepted these rules, we don't need
|
||||
// this state
|
||||
if server_info.rules.is_none()
|
||||
|| server.map_or(false, |s| {
|
||||
s.accepted_rules == Some(rules_hash(&server_info.rules))
|
||||
})
|
||||
{
|
||||
return Err(char_select);
|
||||
}
|
||||
|
||||
// Load language
|
||||
let i18n = &global_state.i18n.read();
|
||||
// TODO: don't add default font twice
|
||||
let font = load_font(&i18n.fonts().get("cyri").unwrap().asset_key);
|
||||
|
||||
let mut ui = Ui::new(
|
||||
&mut global_state.window,
|
||||
font,
|
||||
global_state.settings.interface.ui_scale,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Ok(Self {
|
||||
scene: Scene::new(global_state.window.renderer_mut()),
|
||||
controls: Controls {
|
||||
bg_img: ui.add_graphic(Graphic::Image(
|
||||
assets::Image::load_expect(bg_img_spec).read().to_image(),
|
||||
None,
|
||||
)),
|
||||
imgs: Imgs::load(&mut ui).expect("Failed to load images"),
|
||||
fonts: Fonts::load(i18n.fonts(), &mut ui).expect("Impossible to load fonts"),
|
||||
i18n: global_state.i18n.clone(),
|
||||
accept_button: Default::default(),
|
||||
decline_button: Default::default(),
|
||||
scrollable: Default::default(),
|
||||
server_info,
|
||||
seen_before: server.map_or(false, |s| s.accepted_rules.is_some()),
|
||||
},
|
||||
ui,
|
||||
char_select: Some(char_select),
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_event(&mut self, event: window::Event) -> bool {
|
||||
match event {
|
||||
// Pass events to ui.
|
||||
window::Event::IcedUi(event) => {
|
||||
self.ui.handle_event(event);
|
||||
true
|
||||
},
|
||||
window::Event::ScaleFactorChanged(s) => {
|
||||
self.ui.scale_factor_changed(s);
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayState for ServerInfoState {
|
||||
fn enter(&mut self, _global_state: &mut GlobalState, _: Direction) {
|
||||
/*
|
||||
// Updated localization in case the selected language was changed
|
||||
self.main_menu_ui
|
||||
.update_language(global_state.i18n, &global_state.settings);
|
||||
// Set scale mode in case it was change
|
||||
self.main_menu_ui
|
||||
.set_scale_mode(global_state.settings.interface.ui_scale);
|
||||
*/
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match)] // TODO: remove when event match has multiple arms
|
||||
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<Event>) -> PlayStateResult {
|
||||
span!(_guard, "tick", "<ServerInfoState as PlayState>::tick");
|
||||
|
||||
// Handle window events.
|
||||
for event in events {
|
||||
// Pass all events to the ui first.
|
||||
if self.handle_event(event.clone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::Close => return PlayStateResult::Shutdown,
|
||||
// Ignore all other events.
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
// Maintain the UI.
|
||||
let view = self.controls.view();
|
||||
let (messages, _) = self.ui.maintain(
|
||||
view,
|
||||
global_state.window.renderer_mut(),
|
||||
None,
|
||||
&mut global_state.clipboard,
|
||||
);
|
||||
|
||||
for message in messages {
|
||||
match message {
|
||||
Message::Accept => {
|
||||
// Update last-accepted rules hash so we don't see the message again
|
||||
if let Some(server) = global_state
|
||||
.profile
|
||||
.servers
|
||||
.get_mut(&self.controls.server_info.name)
|
||||
{
|
||||
server.accepted_rules = Some(rules_hash(&self.controls.server_info.rules));
|
||||
}
|
||||
|
||||
return PlayStateResult::Switch(Box::new(self.char_select.take().unwrap()));
|
||||
},
|
||||
Message::Decline => return PlayStateResult::Pop,
|
||||
}
|
||||
}
|
||||
|
||||
PlayStateResult::Continue
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str { "Server Info" }
|
||||
|
||||
fn capped_fps(&self) -> bool { true }
|
||||
|
||||
fn globals_bind_group(&self) -> &GlobalsBindGroup { self.scene.global_bind_group() }
|
||||
|
||||
fn render(&self, drawer: &mut Drawer<'_>, _: &Settings) {
|
||||
// Draw the UI to the screen.
|
||||
let mut third_pass = drawer.third_pass();
|
||||
if let Some(mut ui_drawer) = third_pass.draw_ui() {
|
||||
self.ui.render(&mut ui_drawer);
|
||||
};
|
||||
}
|
||||
|
||||
fn egui_enabled(&self) -> bool { false }
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
||||
pub const IMPORTANT_TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 0.85, 0.5);
|
||||
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
|
||||
|
||||
pub const FILL_FRAC_ONE: f32 = 0.67;
|
||||
|
||||
let i18n = self.i18n.read();
|
||||
|
||||
// TODO: consider setting this as the default in the renderer
|
||||
let button_style = style::button::Style::new(self.imgs.button)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.text_color(TEXT_COLOR)
|
||||
.disabled_text_color(DISABLED_TEXT_COLOR);
|
||||
|
||||
let accept_button = Container::new(
|
||||
Container::new(neat_button(
|
||||
&mut self.accept_button,
|
||||
i18n.get_msg("common-accept"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::Accept),
|
||||
))
|
||||
.max_width(200),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.align_x(Align::Center);
|
||||
|
||||
let decline_button = Container::new(
|
||||
Container::new(neat_button(
|
||||
&mut self.decline_button,
|
||||
i18n.get_msg("common-decline"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::Decline),
|
||||
))
|
||||
.max_width(200),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.align_x(Align::Center);
|
||||
|
||||
let mut elements = Vec::new();
|
||||
|
||||
elements.push(
|
||||
Container::new(
|
||||
iced::Text::new(i18n.get_msg("main-server-rules"))
|
||||
.size(self.fonts.cyri.scale(30))
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
|
||||
if self.seen_before {
|
||||
elements.push(
|
||||
Container::new(
|
||||
iced::Text::new(i18n.get_msg("main-server-rules-seen-before"))
|
||||
.size(self.fonts.cyri.scale(20))
|
||||
.color(IMPORTANT_TEXT_COLOR)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
|
||||
// elements.push(iced::Text::new(format!("{}: {}", self.server_info.name,
|
||||
// self.server_info.description)) .size(self.fonts.cyri.scale(20))
|
||||
// .width(Length::Shrink)
|
||||
// .horizontal_alignment(HorizontalAlignment::Center)
|
||||
// .into());
|
||||
|
||||
elements.push(
|
||||
Scrollable::new(&mut self.scrollable)
|
||||
.push(
|
||||
iced::Text::new(self.server_info.rules.as_deref().unwrap_or("<rules>"))
|
||||
.size(self.fonts.cyri.scale(16))
|
||||
.width(Length::Shrink)
|
||||
.horizontal_alignment(HorizontalAlignment::Left)
|
||||
.vertical_alignment(VerticalAlignment::Top),
|
||||
)
|
||||
.height(Length::Fill)
|
||||
.width(Length::Fill)
|
||||
.into(),
|
||||
);
|
||||
|
||||
elements.push(
|
||||
Row::with_children(vec![decline_button.into(), accept_button.into()])
|
||||
.width(Length::Shrink)
|
||||
.height(Length::Shrink)
|
||||
.padding(25)
|
||||
.into(),
|
||||
);
|
||||
|
||||
Container::new(
|
||||
Container::new(
|
||||
Column::with_children(elements)
|
||||
.spacing(10)
|
||||
.padding(20),
|
||||
)
|
||||
.style(
|
||||
style::container::Style::color_with_double_cornerless_border(
|
||||
(22, 18, 16, 255).into(),
|
||||
(11, 11, 11, 255).into(),
|
||||
(54, 46, 38, 255).into(),
|
||||
),
|
||||
)
|
||||
.max_width(1000)
|
||||
.align_x(Align::Center)
|
||||
// .width(Length::Shrink)
|
||||
// .height(Length::Shrink)
|
||||
.padding(15),
|
||||
)
|
||||
.style(style::container::Style::image(self.bg_img))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_x(Align::Center)
|
||||
.padding(50)
|
||||
.into()
|
||||
}
|
||||
}
|
@ -39,6 +39,8 @@ pub struct ServerProfile {
|
||||
pub selected_character: Option<CharacterId>,
|
||||
/// Last spectate position
|
||||
pub spectate_position: Option<vek::Vec3<f32>>,
|
||||
/// Hash of left-accepted server rules
|
||||
pub accepted_rules: Option<u64>,
|
||||
}
|
||||
|
||||
impl Default for ServerProfile {
|
||||
@ -47,6 +49,7 @@ impl Default for ServerProfile {
|
||||
characters: HashMap::new(),
|
||||
selected_character: None,
|
||||
spectate_position: None,
|
||||
accepted_rules: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user