mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added client-side character saving
This commit is contained in:
parent
96e0cad281
commit
2c42aaf5f5
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -3244,6 +3244,7 @@ name = "veloren-voxygen"
|
|||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
"backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"conrod_core 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
|
"conrod_core 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
|
||||||
"conrod_winit 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
|
"conrod_winit 0.63.0 (git+https://gitlab.com/veloren/conrod.git)",
|
||||||
|
@ -62,9 +62,10 @@ crossbeam = "=0.7.2"
|
|||||||
hashbrown = { version = "0.6.2", features = ["serde", "nightly"] }
|
hashbrown = { version = "0.6.2", features = ["serde", "nightly"] }
|
||||||
chrono = "0.4.9"
|
chrono = "0.4.9"
|
||||||
rust-argon2 = "0.5"
|
rust-argon2 = "0.5"
|
||||||
|
bincode = "1.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
dispatch = "0.1.4"
|
dispatch = "0.1.4"
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
@ -13,6 +13,7 @@ pub mod key_state;
|
|||||||
mod logging;
|
mod logging;
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
|
pub mod meta;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod session;
|
pub mod session;
|
||||||
@ -24,13 +25,16 @@ pub mod window;
|
|||||||
// Reexports
|
// Reexports
|
||||||
pub use crate::error::Error;
|
pub use crate::error::Error;
|
||||||
|
|
||||||
use crate::{audio::AudioFrontend, menu::main::MainMenuState, settings::Settings, window::Window};
|
use crate::{
|
||||||
|
audio::AudioFrontend, menu::main::MainMenuState, meta::Meta, settings::Settings, window::Window,
|
||||||
|
};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use std::{mem, panic, str::FromStr};
|
use std::{mem, panic, str::FromStr};
|
||||||
|
|
||||||
/// A type used to store state that is shared between all play states.
|
/// A type used to store state that is shared between all play states.
|
||||||
pub struct GlobalState {
|
pub struct GlobalState {
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
|
meta: Meta,
|
||||||
window: Window,
|
window: Window,
|
||||||
audio: AudioFrontend,
|
audio: AudioFrontend,
|
||||||
info_message: Option<String>,
|
info_message: Option<String>,
|
||||||
@ -97,6 +101,9 @@ fn main() {
|
|||||||
|
|
||||||
logging::init(&settings, term_log_level, file_log_level);
|
logging::init(&settings, term_log_level, file_log_level);
|
||||||
|
|
||||||
|
// Load metadata
|
||||||
|
let meta = Meta::load();
|
||||||
|
|
||||||
// Save settings to add new fields or create the file if it is not already there
|
// Save settings to add new fields or create the file if it is not already there
|
||||||
if let Err(err) = settings.save_to_file() {
|
if let Err(err) = settings.save_to_file() {
|
||||||
panic!("Failed to save settings: {:?}", err);
|
panic!("Failed to save settings: {:?}", err);
|
||||||
@ -120,6 +127,7 @@ fn main() {
|
|||||||
audio,
|
audio,
|
||||||
window: Window::new(&settings).expect("Failed to create window!"),
|
window: Window::new(&settings).expect("Failed to create window!"),
|
||||||
settings,
|
settings,
|
||||||
|
meta,
|
||||||
info_message: None,
|
info_message: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -254,6 +262,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save any unsaved changes to settings
|
// Save any unsaved changes to settings and meta
|
||||||
global_state.settings.save_to_file_warn();
|
global_state.settings.save_to_file_warn();
|
||||||
|
global_state.meta.save_to_file_warn();
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ impl PlayState for CharSelectionState {
|
|||||||
// Maintain the UI.
|
// Maintain the UI.
|
||||||
let events = self
|
let events = self
|
||||||
.char_selection_ui
|
.char_selection_ui
|
||||||
.maintain(global_state.window.renderer_mut(), &self.client.borrow());
|
.maintain(global_state, &self.client.borrow());
|
||||||
for event in events {
|
for event in events {
|
||||||
match event {
|
match event {
|
||||||
ui::Event::Logout => {
|
ui::Event::Logout => {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::window::{Event as WinEvent, PressState};
|
use crate::window::{Event as WinEvent, PressState};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
meta::CharacterData,
|
||||||
render::{Consts, Globals, Renderer},
|
render::{Consts, Globals, Renderer},
|
||||||
ui::{
|
ui::{
|
||||||
img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic},
|
img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic, VoxelMs9Graphic},
|
||||||
@ -8,7 +9,7 @@ use crate::{
|
|||||||
GlobalState,
|
GlobalState,
|
||||||
};
|
};
|
||||||
use client::Client;
|
use client::Client;
|
||||||
use common::comp::humanoid;
|
use common::comp::{self, humanoid};
|
||||||
use conrod_core::{
|
use conrod_core::{
|
||||||
color,
|
color,
|
||||||
color::TRANSPARENT,
|
color::TRANSPARENT,
|
||||||
@ -43,7 +44,6 @@ widget_ids! {
|
|||||||
divider,
|
divider,
|
||||||
bodyrace_text,
|
bodyrace_text,
|
||||||
facialfeatures_text,
|
facialfeatures_text,
|
||||||
char_delete,
|
|
||||||
info_bg,
|
info_bg,
|
||||||
info_frame,
|
info_frame,
|
||||||
info_button_align,
|
info_button_align,
|
||||||
@ -61,10 +61,11 @@ widget_ids! {
|
|||||||
|
|
||||||
|
|
||||||
// Characters
|
// Characters
|
||||||
character_box_1,
|
character_boxes[],
|
||||||
character_name_1,
|
character_deletes[],
|
||||||
character_location_1,
|
character_names[],
|
||||||
character_level_1,
|
character_locations[],
|
||||||
|
character_levels[],
|
||||||
|
|
||||||
character_box_2,
|
character_box_2,
|
||||||
character_name_2,
|
character_name_2,
|
||||||
@ -242,7 +243,8 @@ const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0);
|
|||||||
const TEXT_COLOR_2: Color = Color::Rgba(1.0, 1.0, 1.0, 0.2);
|
const TEXT_COLOR_2: Color = Color::Rgba(1.0, 1.0, 1.0, 0.2);
|
||||||
|
|
||||||
enum InfoContent {
|
enum InfoContent {
|
||||||
Deletion,
|
None,
|
||||||
|
Deletion(usize),
|
||||||
//Name,
|
//Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -254,7 +256,6 @@ pub struct CharSelectionUi {
|
|||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
character_creation: bool,
|
character_creation: bool,
|
||||||
info_content: InfoContent,
|
info_content: InfoContent,
|
||||||
info_window: bool,
|
|
||||||
//deletion_confirmation: bool,
|
//deletion_confirmation: bool,
|
||||||
pub character_name: String,
|
pub character_name: String,
|
||||||
pub character_body: humanoid::Body,
|
pub character_body: humanoid::Body,
|
||||||
@ -283,18 +284,29 @@ impl CharSelectionUi {
|
|||||||
imgs,
|
imgs,
|
||||||
rot_imgs,
|
rot_imgs,
|
||||||
fonts,
|
fonts,
|
||||||
info_window: false,
|
info_content: InfoContent::None,
|
||||||
info_content: InfoContent::Deletion,
|
|
||||||
//deletion_confirmation: false,
|
//deletion_confirmation: false,
|
||||||
character_creation: false,
|
character_creation: false,
|
||||||
character_name: "Character Name".to_string(),
|
character_name: "Character Name".to_string(),
|
||||||
character_body: humanoid::Body::random(),
|
character_body: if let Some(character) = global_state
|
||||||
|
.meta
|
||||||
|
.characters
|
||||||
|
.get(global_state.meta.selected_character)
|
||||||
|
{
|
||||||
|
match character.body {
|
||||||
|
comp::Body::Humanoid(body) => Some(body.clone()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.unwrap_or_else(|| humanoid::Body::random()),
|
||||||
character_tool: Some(STARTER_SWORD),
|
character_tool: Some(STARTER_SWORD),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Split this into multiple modules or functions.
|
// TODO: Split this into multiple modules or functions.
|
||||||
fn update_layout(&mut self, client: &Client) -> Vec<Event> {
|
fn update_layout(&mut self, global_state: &mut GlobalState, client: &Client) -> Vec<Event> {
|
||||||
let mut events = Vec::new();
|
let mut events = Vec::new();
|
||||||
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
|
let (ref mut ui_widgets, ref mut tooltip_manager) = self.ui.set_widgets();
|
||||||
let version = format!(
|
let version = format!(
|
||||||
@ -322,7 +334,8 @@ impl CharSelectionUi {
|
|||||||
.desc_text_color(TEXT_COLOR_2);
|
.desc_text_color(TEXT_COLOR_2);
|
||||||
|
|
||||||
// Information Window
|
// Information Window
|
||||||
if self.info_window {
|
if let InfoContent::None = self.info_content {
|
||||||
|
} else {
|
||||||
Rectangle::fill_with([520.0, 150.0], color::rgba(0.0, 0.0, 0.0, 0.9))
|
Rectangle::fill_with([520.0, 150.0], color::rgba(0.0, 0.0, 0.0, 0.9))
|
||||||
.mid_top_with_margin_on(ui_widgets.window, 300.0)
|
.mid_top_with_margin_on(ui_widgets.window, 300.0)
|
||||||
.set(self.ids.info_bg, ui_widgets);
|
.set(self.ids.info_bg, ui_widgets);
|
||||||
@ -334,7 +347,8 @@ impl CharSelectionUi {
|
|||||||
.bottom_left_with_margins_on(self.ids.info_frame, 0.0, 0.0)
|
.bottom_left_with_margins_on(self.ids.info_frame, 0.0, 0.0)
|
||||||
.set(self.ids.info_button_align, ui_widgets);
|
.set(self.ids.info_button_align, ui_widgets);
|
||||||
match self.info_content {
|
match self.info_content {
|
||||||
InfoContent::Deletion => {
|
InfoContent::None => unreachable!(),
|
||||||
|
InfoContent::Deletion(character_index) => {
|
||||||
Text::new("Permanently delete this Character?")
|
Text::new("Permanently delete this Character?")
|
||||||
.mid_top_with_margin_on(self.ids.info_frame, 40.0)
|
.mid_top_with_margin_on(self.ids.info_frame, 40.0)
|
||||||
.font_size(24)
|
.font_size(24)
|
||||||
@ -354,23 +368,23 @@ impl CharSelectionUi {
|
|||||||
.set(self.ids.info_no, ui_widgets)
|
.set(self.ids.info_no, ui_widgets)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
self.info_window = false
|
self.info_content = InfoContent::None;
|
||||||
};
|
};
|
||||||
if Button::image(self.imgs.button)
|
if Button::image(self.imgs.button)
|
||||||
.w_h(150.0, 40.0)
|
.w_h(150.0, 40.0)
|
||||||
.right_from(self.ids.info_no, 100.0)
|
.right_from(self.ids.info_no, 100.0)
|
||||||
//.hover_image(self.imgs.button_hover)
|
.hover_image(self.imgs.button_hover)
|
||||||
//.press_image(self.imgs.button_press)
|
.press_image(self.imgs.button_press)
|
||||||
.label_y(Relative::Scalar(2.0))
|
.label_y(Relative::Scalar(2.0))
|
||||||
.label("Yes")
|
.label("Yes")
|
||||||
.label_font_size(18)
|
|
||||||
.label_font_id(self.fonts.cyri)
|
.label_font_id(self.fonts.cyri)
|
||||||
.label_color(Color::Rgba(1.0, 1.0, 1.0, 0.1))
|
.label_font_size(18)
|
||||||
|
.label_color(TEXT_COLOR)
|
||||||
.set(self.ids.info_ok, ui_widgets)
|
.set(self.ids.info_ok, ui_widgets)
|
||||||
.was_clicked()
|
.was_clicked()
|
||||||
{
|
{
|
||||||
//self.info_window = false
|
self.info_content = InfoContent::None;
|
||||||
// TODO -> Char Deletion Event
|
global_state.meta.delete_character(character_index);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -489,54 +503,96 @@ impl CharSelectionUi {
|
|||||||
.color(TEXT_COLOR)
|
.color(TEXT_COLOR)
|
||||||
.set(self.ids.version, ui_widgets);
|
.set(self.ids.version, ui_widgets);
|
||||||
|
|
||||||
// 1st Character in Selection List
|
// Resize character selection widgets
|
||||||
if Button::image(self.imgs.selection)
|
let character_count = global_state.meta.characters.len();
|
||||||
.top_left_with_margins_on(self.ids.charlist_alignment, 0.0, 2.0)
|
self.ids
|
||||||
.w_h(386.0, 80.0)
|
.character_boxes
|
||||||
.image_color(Color::Rgba(1.0, 1.0, 1.0, 0.8))
|
.resize(character_count, &mut ui_widgets.widget_id_generator());
|
||||||
.hover_image(self.imgs.selection)
|
self.ids
|
||||||
.press_image(self.imgs.selection)
|
.character_deletes
|
||||||
.label_font_id(self.fonts.cyri)
|
.resize(character_count, &mut ui_widgets.widget_id_generator());
|
||||||
.label_y(conrod_core::position::Relative::Scalar(20.0))
|
self.ids
|
||||||
.set(self.ids.character_box_1, ui_widgets)
|
.character_names
|
||||||
.was_clicked()
|
.resize(character_count, &mut ui_widgets.widget_id_generator());
|
||||||
{}
|
self.ids
|
||||||
if Button::image(self.imgs.delete_button)
|
.character_levels
|
||||||
.w_h(30.0 * 0.5, 30.0 * 0.5)
|
.resize(character_count, &mut ui_widgets.widget_id_generator());
|
||||||
.top_right_with_margins_on(self.ids.character_box_1, 15.0, 15.0)
|
self.ids
|
||||||
.hover_image(self.imgs.delete_button_hover)
|
.character_locations
|
||||||
.press_image(self.imgs.delete_button_press)
|
.resize(character_count, &mut ui_widgets.widget_id_generator());
|
||||||
.with_tooltip(tooltip_manager, "Delete Character", "", &tooltip_human)
|
|
||||||
.set(self.ids.char_delete, ui_widgets)
|
// Character selection
|
||||||
.was_clicked()
|
for (i, character) in global_state.meta.characters.iter().enumerate() {
|
||||||
{
|
let character_box = Button::image(if global_state.meta.selected_character == i {
|
||||||
self.info_content = InfoContent::Deletion;
|
self.imgs.selection_hover
|
||||||
self.info_window = true;
|
} else {
|
||||||
|
self.imgs.selection
|
||||||
|
});
|
||||||
|
let character_box = if i == 0 {
|
||||||
|
character_box.top_left_with_margins_on(self.ids.charlist_alignment, 0.0, 2.0)
|
||||||
|
} else {
|
||||||
|
character_box.down_from(self.ids.character_boxes[i - 1], 5.0)
|
||||||
|
};
|
||||||
|
if character_box
|
||||||
|
.w_h(386.0, 80.0)
|
||||||
|
.image_color(Color::Rgba(1.0, 1.0, 1.0, 0.8))
|
||||||
|
.hover_image(self.imgs.selection_hover)
|
||||||
|
.press_image(self.imgs.selection_press)
|
||||||
|
.label_font_id(self.fonts.cyri)
|
||||||
|
.label_y(conrod_core::position::Relative::Scalar(20.0))
|
||||||
|
.set(self.ids.character_boxes[i], ui_widgets)
|
||||||
|
.was_clicked()
|
||||||
|
{
|
||||||
|
self.character_name = character.name.clone();
|
||||||
|
self.character_body = match &character.body {
|
||||||
|
comp::Body::Humanoid(body) => body.clone(),
|
||||||
|
_ => panic!("Unsupported body type!"),
|
||||||
|
};
|
||||||
|
global_state.meta.selected_character = i;
|
||||||
|
}
|
||||||
|
if Button::image(self.imgs.delete_button)
|
||||||
|
.w_h(30.0 * 0.5, 30.0 * 0.5)
|
||||||
|
.top_right_with_margins_on(self.ids.character_boxes[i], 15.0, 15.0)
|
||||||
|
.hover_image(self.imgs.delete_button_hover)
|
||||||
|
.press_image(self.imgs.delete_button_press)
|
||||||
|
.with_tooltip(tooltip_manager, "Delete Character", "", &tooltip_human)
|
||||||
|
.set(self.ids.character_deletes[i], ui_widgets)
|
||||||
|
.was_clicked()
|
||||||
|
{
|
||||||
|
self.info_content = InfoContent::Deletion(i);
|
||||||
|
}
|
||||||
|
Text::new(&character.name)
|
||||||
|
.top_left_with_margins_on(self.ids.character_boxes[i], 6.0, 9.0)
|
||||||
|
.font_size(19)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(self.ids.character_names[i], ui_widgets);
|
||||||
|
|
||||||
|
Text::new("Level <n/a>")
|
||||||
|
.down_from(self.ids.character_names[i], 4.0)
|
||||||
|
.font_size(17)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(self.ids.character_levels[i], ui_widgets);
|
||||||
|
|
||||||
|
Text::new("Uncanny Valley")
|
||||||
|
.down_from(self.ids.character_levels[i], 4.0)
|
||||||
|
.font_size(17)
|
||||||
|
.font_id(self.fonts.cyri)
|
||||||
|
.color(TEXT_COLOR)
|
||||||
|
.set(self.ids.character_locations[i], ui_widgets);
|
||||||
}
|
}
|
||||||
Text::new("Test Character")
|
|
||||||
.top_left_with_margins_on(self.ids.character_box_1, 6.0, 9.0)
|
|
||||||
.font_size(19)
|
|
||||||
.font_id(self.fonts.cyri)
|
|
||||||
.color(TEXT_COLOR)
|
|
||||||
.set(self.ids.character_name_1, ui_widgets);
|
|
||||||
|
|
||||||
Text::new("Level 1")
|
|
||||||
.down_from(self.ids.character_name_1, 4.0)
|
|
||||||
.font_size(17)
|
|
||||||
.font_id(self.fonts.cyri)
|
|
||||||
.color(TEXT_COLOR)
|
|
||||||
.set(self.ids.character_level_1, ui_widgets);
|
|
||||||
|
|
||||||
Text::new("Uncanny Valley")
|
|
||||||
.down_from(self.ids.character_level_1, 4.0)
|
|
||||||
.font_size(17)
|
|
||||||
.font_id(self.fonts.cyri)
|
|
||||||
.color(TEXT_COLOR)
|
|
||||||
.set(self.ids.character_location_1, ui_widgets);
|
|
||||||
|
|
||||||
// Create Character Button
|
// Create Character Button
|
||||||
if Button::image(self.imgs.selection)
|
let create_char_button = Button::image(self.imgs.selection);
|
||||||
.down_from(self.ids.character_box_1, 5.0)
|
|
||||||
|
let create_char_button = if character_count > 0 {
|
||||||
|
create_char_button.down_from(self.ids.character_boxes[character_count - 1], 5.0)
|
||||||
|
} else {
|
||||||
|
create_char_button.top_left_with_margins_on(self.ids.charlist_alignment, 0.0, 2.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
if create_char_button
|
||||||
.w_h(386.0, 80.0)
|
.w_h(386.0, 80.0)
|
||||||
.hover_image(self.imgs.selection_hover)
|
.hover_image(self.imgs.selection_hover)
|
||||||
.press_image(self.imgs.selection_press)
|
.press_image(self.imgs.selection_press)
|
||||||
@ -549,6 +605,7 @@ impl CharSelectionUi {
|
|||||||
{
|
{
|
||||||
self.character_creation = true;
|
self.character_creation = true;
|
||||||
self.character_tool = Some(STARTER_SWORD);
|
self.character_tool = Some(STARTER_SWORD);
|
||||||
|
self.character_body = humanoid::Body::random();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Character_Creation //////////////////////////////////////////////////////////////////////
|
// Character_Creation //////////////////////////////////////////////////////////////////////
|
||||||
@ -585,6 +642,10 @@ impl CharSelectionUi {
|
|||||||
{
|
{
|
||||||
// TODO: Save character.
|
// TODO: Save character.
|
||||||
self.character_creation = false;
|
self.character_creation = false;
|
||||||
|
global_state.meta.add_character(CharacterData {
|
||||||
|
name: self.character_name.clone(),
|
||||||
|
body: comp::Body::Humanoid(self.character_body.clone()),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// Character Name Input
|
// Character Name Input
|
||||||
Rectangle::fill_with([320.0, 50.0], color::rgba(0.0, 0.0, 0.0, 0.97))
|
Rectangle::fill_with([320.0, 50.0], color::rgba(0.0, 0.0, 0.0, 0.97))
|
||||||
@ -1186,9 +1247,9 @@ impl CharSelectionUi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn maintain(&mut self, renderer: &mut Renderer, client: &Client) -> Vec<Event> {
|
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &Client) -> Vec<Event> {
|
||||||
let events = self.update_layout(client);
|
let events = self.update_layout(global_state, client);
|
||||||
self.ui.maintain(renderer, None);
|
self.ui.maintain(global_state.window.renderer_mut(), None);
|
||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
87
voxygen/src/meta.rs
Normal file
87
voxygen/src/meta.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
use common::comp;
|
||||||
|
use directories::ProjectDirs;
|
||||||
|
use log::warn;
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use std::{fs, io, path::PathBuf};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct CharacterData {
|
||||||
|
pub name: String,
|
||||||
|
pub body: comp::Body,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
pub struct Meta {
|
||||||
|
pub characters: Vec<CharacterData>,
|
||||||
|
pub selected_character: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Meta {
|
||||||
|
pub fn delete_character(&mut self, index: usize) {
|
||||||
|
self.characters.remove(index);
|
||||||
|
if index < self.selected_character {
|
||||||
|
self.selected_character -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_character(&mut self, data: CharacterData) {
|
||||||
|
self.characters.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Self {
|
||||||
|
let path = Self::get_meta_path();
|
||||||
|
|
||||||
|
if let Ok(file) = fs::File::open(&path) {
|
||||||
|
match bincode::deserialize_from(file) {
|
||||||
|
Ok(s) => return s,
|
||||||
|
Err(e) => {
|
||||||
|
log::warn!("Failed to parse meta file! Fallback to default. {}", e);
|
||||||
|
// Rename the corrupted settings file
|
||||||
|
let mut new_path = path.to_owned();
|
||||||
|
new_path.pop();
|
||||||
|
new_path.push("meta.invalid.dat");
|
||||||
|
if let Err(err) = std::fs::rename(path, new_path) {
|
||||||
|
log::warn!("Failed to rename meta file. {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is reached if either:
|
||||||
|
// - The file can't be opened (presumably it doesn't exist)
|
||||||
|
// - Or there was an error parsing the file
|
||||||
|
let default = Self::default();
|
||||||
|
default.save_to_file_warn();
|
||||||
|
default
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_to_file_warn(&self) {
|
||||||
|
if let Err(err) = self.save_to_file() {
|
||||||
|
warn!("Failed to save settings: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save_to_file(&self) -> std::io::Result<()> {
|
||||||
|
let path = Self::get_meta_path();
|
||||||
|
if let Some(dir) = path.parent() {
|
||||||
|
fs::create_dir_all(dir)?;
|
||||||
|
}
|
||||||
|
bincode::serialize_into(fs::File::create(path)?, self)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_meta_path() -> PathBuf {
|
||||||
|
if let Some(val) = std::env::var_os("VOXYGEN_CONFIG") {
|
||||||
|
let meta = PathBuf::from(val).join("meta.dat");
|
||||||
|
if meta.exists() || meta.parent().map(|x| x.exists()).unwrap_or(false) {
|
||||||
|
return meta;
|
||||||
|
}
|
||||||
|
log::warn!("VOXYGEN_CONFIG points to invalid path.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let proj_dirs = ProjectDirs::from("net", "veloren", "voxygen")
|
||||||
|
.expect("System's $HOME directory path not found!");
|
||||||
|
proj_dirs.config_dir().join("meta").with_extension("dat")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user