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-database_error = Server database error: { $raw_error }
|
||||||
main-servers-persistence_error = Server persistence error (Probably Asset/Character Data related): { $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-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 = Credits
|
||||||
main-credits-created_by = created by
|
main-credits-created_by = created by
|
||||||
main-credits-music = Music
|
main-credits-music = Music
|
||||||
|
@ -59,6 +59,8 @@ impl CharSelectionState {
|
|||||||
})
|
})
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn client(&self) -> &RefCell<Client> { &self.client }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayState for CharSelectionState {
|
impl PlayState for CharSelectionState {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
mod client_init;
|
mod client_init;
|
||||||
mod scene;
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use super::char_selection::CharSelectionState;
|
use super::{char_selection::CharSelectionState, dummy_scene::Scene, server_info::ServerInfoState};
|
||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
use crate::singleplayer::SingleplayerState;
|
use crate::singleplayer::SingleplayerState;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -20,7 +19,6 @@ use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
|||||||
use common::comp;
|
use common::comp;
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
use i18n::LocalizationHandle;
|
use i18n::LocalizationHandle;
|
||||||
use scene::Scene;
|
|
||||||
#[cfg(feature = "singleplayer")]
|
#[cfg(feature = "singleplayer")]
|
||||||
use server::ServerInitStage;
|
use server::ServerInitStage;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -294,10 +292,24 @@ impl PlayState for MainMenuState {
|
|||||||
core::mem::replace(&mut self.init, InitState::None)
|
core::mem::replace(&mut self.init, InitState::None)
|
||||||
{
|
{
|
||||||
self.main_menu_ui.connected();
|
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,
|
global_state,
|
||||||
std::rc::Rc::new(std::cell::RefCell::new(*client)),
|
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,
|
fonts: Fonts,
|
||||||
imgs: Imgs,
|
imgs: Imgs,
|
||||||
bg_img: widget::image::Handle,
|
bg_img: widget::image::Handle,
|
||||||
@ -677,6 +677,7 @@ pub struct MainMenuUi {
|
|||||||
// TODO: re add this
|
// TODO: re add this
|
||||||
// tip_no: u16,
|
// tip_no: u16,
|
||||||
controls: Controls,
|
controls: Controls,
|
||||||
|
bg_img_spec: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MainMenuUi {
|
impl MainMenuUi {
|
||||||
@ -707,9 +708,15 @@ impl MainMenuUi {
|
|||||||
server,
|
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) {
|
pub fn update_language(&mut self, i18n: LocalizationHandle, settings: &Settings) {
|
||||||
self.controls.i18n = i18n;
|
self.controls.i18n = i18n;
|
||||||
let i18n = &i18n.read();
|
let i18n = &i18n.read();
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
pub mod char_selection;
|
pub mod char_selection;
|
||||||
|
pub mod dummy_scene;
|
||||||
pub mod main;
|
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>,
|
pub selected_character: Option<CharacterId>,
|
||||||
/// Last spectate position
|
/// Last spectate position
|
||||||
pub spectate_position: Option<vek::Vec3<f32>>,
|
pub spectate_position: Option<vek::Vec3<f32>>,
|
||||||
|
/// Hash of left-accepted server rules
|
||||||
|
pub accepted_rules: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ServerProfile {
|
impl Default for ServerProfile {
|
||||||
@ -47,6 +49,7 @@ impl Default for ServerProfile {
|
|||||||
characters: HashMap::new(),
|
characters: HashMap::new(),
|
||||||
selected_character: None,
|
selected_character: None,
|
||||||
spectate_position: None,
|
spectate_position: None,
|
||||||
|
accepted_rules: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user