2019-08-19 12:39:23 +00:00
|
|
|
#![deny(unsafe_code)]
|
2019-04-29 20:37:19 +00:00
|
|
|
#![recursion_limit = "2048"]
|
2019-01-23 20:01:58 +00:00
|
|
|
|
2020-02-29 03:59:11 +00:00
|
|
|
use veloren_voxygen::{
|
|
|
|
audio::{self, AudioFrontend},
|
|
|
|
i18n::{self, i18n_asset_key, VoxygenLocalization},
|
|
|
|
logging,
|
2020-01-17 23:43:18 +00:00
|
|
|
menu::main::MainMenuState,
|
|
|
|
meta::Meta,
|
|
|
|
settings::Settings,
|
|
|
|
window::Window,
|
2020-02-29 03:59:11 +00:00
|
|
|
Direction, GlobalState, PlayState, PlayStateResult,
|
2020-01-20 13:37:29 +00:00
|
|
|
};
|
2020-02-29 03:59:11 +00:00
|
|
|
|
2020-01-22 21:15:19 +00:00
|
|
|
use common::assets::{load, load_expect};
|
2019-12-18 19:35:17 +00:00
|
|
|
use log::{debug, error};
|
2019-10-08 15:42:13 +00:00
|
|
|
use std::{mem, panic, str::FromStr};
|
2019-04-17 13:23:34 +00:00
|
|
|
|
2019-01-02 21:25:01 +00:00
|
|
|
fn main() {
|
2019-11-23 14:34:03 +00:00
|
|
|
// Initialize logging.
|
|
|
|
let term_log_level = std::env::var_os("VOXYGEN_LOG")
|
|
|
|
.and_then(|env| env.to_str().map(|s| s.to_owned()))
|
|
|
|
.and_then(|s| log::LevelFilter::from_str(&s).ok())
|
|
|
|
.unwrap_or(log::LevelFilter::Warn);
|
|
|
|
|
2019-11-24 16:22:15 +00:00
|
|
|
let file_log_level = std::env::var_os("VOXYGEN_FILE_LOG")
|
|
|
|
.and_then(|env| env.to_str().map(|s| s.to_owned()))
|
|
|
|
.and_then(|s| log::LevelFilter::from_str(&s).ok())
|
|
|
|
.unwrap_or(log::LevelFilter::Debug);
|
|
|
|
|
2019-08-18 19:21:16 +00:00
|
|
|
// Load the settings
|
2020-02-01 20:39:39 +00:00
|
|
|
// Note: This won't log anything due to it being called before
|
|
|
|
// ``logging::init``. The issue is we need to read a setting to decide
|
|
|
|
// whether we create a log file or not.
|
2019-06-06 14:48:41 +00:00
|
|
|
let settings = Settings::load();
|
2019-12-18 19:35:17 +00:00
|
|
|
|
|
|
|
logging::init(&settings, term_log_level, file_log_level);
|
|
|
|
|
2020-01-20 13:37:29 +00:00
|
|
|
// Load metadata
|
|
|
|
let meta = Meta::load();
|
|
|
|
|
2019-08-18 19:21:16 +00:00
|
|
|
// Save settings to add new fields or create the file if it is not already there
|
|
|
|
if let Err(err) = settings.save_to_file() {
|
|
|
|
panic!("Failed to save settings: {:?}", err);
|
|
|
|
}
|
2019-12-10 14:02:51 +00:00
|
|
|
|
2020-01-12 20:06:32 +00:00
|
|
|
let audio_device = || match &settings.audio.audio_device {
|
|
|
|
Some(d) => d.to_string(),
|
|
|
|
None => audio::get_default_device(),
|
|
|
|
};
|
2020-01-12 20:06:32 +00:00
|
|
|
|
2020-01-12 20:06:32 +00:00
|
|
|
let mut audio = if settings.audio.audio_on {
|
2020-02-15 21:30:44 +00:00
|
|
|
AudioFrontend::new(audio_device(), settings.audio.max_sfx_channels)
|
2019-08-31 06:37:09 +00:00
|
|
|
} else {
|
|
|
|
AudioFrontend::no_audio()
|
|
|
|
};
|
|
|
|
|
2019-12-10 14:02:51 +00:00
|
|
|
audio.set_music_volume(settings.audio.music_volume);
|
|
|
|
audio.set_sfx_volume(settings.audio.sfx_volume);
|
|
|
|
|
2019-08-31 06:37:09 +00:00
|
|
|
let mut global_state = GlobalState {
|
|
|
|
audio,
|
|
|
|
window: Window::new(&settings).expect("Failed to create window!"),
|
|
|
|
settings,
|
2020-01-20 13:37:29 +00:00
|
|
|
meta,
|
2019-10-18 20:05:37 +00:00
|
|
|
info_message: None,
|
2020-03-01 22:18:22 +00:00
|
|
|
singleplayer: None,
|
2019-08-31 06:37:09 +00:00
|
|
|
};
|
2019-12-10 14:02:51 +00:00
|
|
|
|
2020-01-17 23:43:18 +00:00
|
|
|
// Try to load the localization and log missing entries
|
2020-01-22 21:15:19 +00:00
|
|
|
let localized_strings = load::<VoxygenLocalization>(&i18n_asset_key(
|
2020-01-17 23:43:18 +00:00
|
|
|
&global_state.settings.language.selected_language,
|
2020-01-22 21:15:19 +00:00
|
|
|
))
|
|
|
|
.unwrap_or_else(|error| {
|
|
|
|
log::warn!(
|
2020-02-01 20:39:39 +00:00
|
|
|
"Impossible to load {} language: change to the default language (English) instead. \
|
|
|
|
Source error: {:?}",
|
2020-01-22 21:15:19 +00:00
|
|
|
&global_state.settings.language.selected_language,
|
|
|
|
error
|
|
|
|
);
|
|
|
|
global_state.settings.language.selected_language = i18n::REFERENCE_LANG.to_owned();
|
|
|
|
load_expect::<VoxygenLocalization>(&i18n_asset_key(
|
|
|
|
&global_state.settings.language.selected_language,
|
|
|
|
))
|
|
|
|
});
|
2020-01-17 23:43:18 +00:00
|
|
|
localized_strings.log_missing_entries();
|
|
|
|
|
2019-05-11 20:39:46 +00:00
|
|
|
// Set up panic handler to relay swish panic messages to the user
|
2019-05-05 18:42:44 +00:00
|
|
|
let default_hook = panic::take_hook();
|
2019-04-25 11:49:36 +00:00
|
|
|
panic::set_hook(Box::new(move |panic_info| {
|
2019-05-08 20:01:02 +00:00
|
|
|
let panic_info_payload = panic_info.payload();
|
|
|
|
let payload_string = panic_info_payload.downcast_ref::<String>();
|
|
|
|
let reason = match payload_string {
|
2019-05-09 17:27:07 +00:00
|
|
|
Some(s) => &s,
|
|
|
|
None => {
|
|
|
|
let payload_str = panic_info_payload.downcast_ref::<&str>();
|
|
|
|
match payload_str {
|
|
|
|
Some(st) => st,
|
|
|
|
None => "Payload is not a string",
|
2019-05-08 20:01:02 +00:00
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-05-08 20:01:02 +00:00
|
|
|
};
|
2019-05-17 07:55:51 +00:00
|
|
|
let msg = format!(
|
2019-05-31 03:01:52 +00:00
|
|
|
"A critical error has occurred and Voxygen has been forced to \
|
2019-05-17 07:55:51 +00:00
|
|
|
terminate in an unusual manner. Details about the error can be \
|
|
|
|
found below.\n\
|
|
|
|
\n\
|
|
|
|
> What should I do?\n\
|
|
|
|
\n\
|
|
|
|
We need your help to fix this! You can help by contacting us and \
|
|
|
|
reporting this problem. To do this, open an issue on the Veloren \
|
|
|
|
issue tracker:\n\
|
|
|
|
\n\
|
|
|
|
https://www.gitlab.com/veloren/veloren/issues/new\n\
|
|
|
|
\n\
|
|
|
|
If you're on the Veloren community Discord server, we'd be \
|
|
|
|
grateful if you could also post a message in the #support channel.
|
|
|
|
\n\
|
|
|
|
> What should I include?\n\
|
|
|
|
\n\
|
|
|
|
The error information below will be useful in finding and fixing \
|
|
|
|
the problem. Please include as much information about your setup \
|
|
|
|
and the events that led up to the panic as possible.
|
|
|
|
\n\
|
|
|
|
Voxygen has logged information about the problem (including this \
|
|
|
|
message) to the file {:#?}. Please include the contents of this \
|
|
|
|
file in your bug report.
|
|
|
|
\n\
|
|
|
|
> Error information\n\
|
|
|
|
\n\
|
|
|
|
The information below is intended for developers and testers.\n\
|
|
|
|
\n\
|
|
|
|
Panic Payload: {:?}\n\
|
|
|
|
PanicInfo: {}",
|
2019-12-18 19:35:17 +00:00
|
|
|
// TODO: Verify that this works
|
|
|
|
Settings::get_settings_path()
|
|
|
|
.join("voxygen-<date>.log")
|
|
|
|
.display(),
|
|
|
|
reason,
|
|
|
|
panic_info,
|
2019-05-17 07:55:51 +00:00
|
|
|
);
|
2019-04-25 11:49:36 +00:00
|
|
|
|
2019-06-06 14:48:41 +00:00
|
|
|
error!(
|
2019-05-14 07:41:27 +00:00
|
|
|
"VOXYGEN HAS PANICKED\n\n{}\n\nBacktrace:\n{:?}",
|
|
|
|
msg,
|
2019-05-17 07:55:51 +00:00
|
|
|
backtrace::Backtrace::new(),
|
2019-05-14 07:41:27 +00:00
|
|
|
);
|
2019-04-25 11:49:36 +00:00
|
|
|
|
2019-09-21 17:06:08 +00:00
|
|
|
#[cfg(feature = "msgbox")]
|
2020-01-07 05:45:43 +00:00
|
|
|
{
|
|
|
|
#[cfg(target_os = "macos")]
|
|
|
|
dispatch::Queue::main()
|
|
|
|
.sync(|| msgbox::create("Voxygen has panicked", &msg, msgbox::IconType::Error));
|
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
msgbox::create("Voxygen has panicked", &msg, msgbox::IconType::Error);
|
|
|
|
}
|
2019-05-05 18:42:44 +00:00
|
|
|
|
|
|
|
default_hook(panic_info);
|
2019-04-25 11:49:36 +00:00
|
|
|
}));
|
|
|
|
|
2019-05-17 09:22:32 +00:00
|
|
|
// Set up the initial play state.
|
2019-04-29 20:37:19 +00:00
|
|
|
let mut states: Vec<Box<dyn PlayState>> = vec![Box::new(MainMenuState::new(&mut global_state))];
|
|
|
|
states
|
|
|
|
.last()
|
2019-11-23 14:34:03 +00:00
|
|
|
.map(|current_state| debug!("Started game with state '{}'", current_state.name()));
|
2019-01-30 12:11:34 +00:00
|
|
|
|
2019-01-11 23:18:34 +00:00
|
|
|
// What's going on here?
|
|
|
|
// ---------------------
|
2020-02-01 20:39:39 +00:00
|
|
|
// The state system used by Voxygen allows for the easy development of
|
|
|
|
// stack-based menus. For example, you may want a "title" state that can
|
|
|
|
// push a "main menu" state on top of it, which can in turn push a
|
|
|
|
// "settings" state or a "game session" state on top of it. The code below
|
|
|
|
// manages the state transfer logic automatically so that we don't have to
|
2019-01-11 23:18:34 +00:00
|
|
|
// re-engineer it for each menu we decide to add to the game.
|
2019-04-27 20:55:30 +00:00
|
|
|
let mut direction = Direction::Forwards;
|
2019-04-29 20:37:19 +00:00
|
|
|
while let Some(state_result) = states
|
|
|
|
.last_mut()
|
|
|
|
.map(|last| last.play(direction, &mut global_state))
|
|
|
|
{
|
2019-05-17 09:22:32 +00:00
|
|
|
// Implement state transfer logic.
|
2019-01-02 22:08:13 +00:00
|
|
|
match state_result {
|
2019-01-11 23:18:34 +00:00
|
|
|
PlayStateResult::Shutdown => {
|
2019-04-27 20:55:30 +00:00
|
|
|
direction = Direction::Backwards;
|
2019-11-23 14:34:03 +00:00
|
|
|
debug!("Shutting down all states...");
|
2019-01-11 23:18:34 +00:00
|
|
|
while states.last().is_some() {
|
|
|
|
states.pop().map(|old_state| {
|
2019-06-06 14:48:41 +00:00
|
|
|
debug!("Popped state '{}'.", old_state.name());
|
2019-01-12 01:14:58 +00:00
|
|
|
global_state.on_play_state_changed();
|
2019-01-11 23:18:34 +00:00
|
|
|
});
|
|
|
|
}
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-01-11 23:18:34 +00:00
|
|
|
PlayStateResult::Pop => {
|
2019-04-27 20:55:30 +00:00
|
|
|
direction = Direction::Backwards;
|
2019-01-11 23:18:34 +00:00
|
|
|
states.pop().map(|old_state| {
|
2019-06-06 14:48:41 +00:00
|
|
|
debug!("Popped state '{}'.", old_state.name());
|
2019-01-12 01:14:58 +00:00
|
|
|
global_state.on_play_state_changed();
|
2019-01-11 23:18:34 +00:00
|
|
|
});
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-01-07 21:10:31 +00:00
|
|
|
PlayStateResult::Push(new_state) => {
|
2019-04-27 20:55:30 +00:00
|
|
|
direction = Direction::Forwards;
|
2019-06-06 14:48:41 +00:00
|
|
|
debug!("Pushed state '{}'.", new_state.name());
|
2019-01-02 22:08:13 +00:00
|
|
|
states.push(new_state);
|
2019-01-12 01:14:58 +00:00
|
|
|
global_state.on_play_state_changed();
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-01-07 21:10:31 +00:00
|
|
|
PlayStateResult::Switch(mut new_state) => {
|
2019-04-27 20:55:30 +00:00
|
|
|
direction = Direction::Forwards;
|
2019-01-11 23:18:34 +00:00
|
|
|
states.last_mut().map(|old_state| {
|
2019-06-06 14:48:41 +00:00
|
|
|
debug!(
|
2019-05-17 09:22:32 +00:00
|
|
|
"Switching to state '{}' from state '{}'.",
|
2019-04-29 20:37:19 +00:00
|
|
|
new_state.name(),
|
|
|
|
old_state.name()
|
|
|
|
);
|
2019-01-11 23:18:34 +00:00
|
|
|
mem::swap(old_state, &mut new_state);
|
2019-01-12 01:14:58 +00:00
|
|
|
global_state.on_play_state_changed();
|
2019-01-11 23:18:34 +00:00
|
|
|
});
|
2020-02-01 20:39:39 +00:00
|
|
|
},
|
2019-01-02 21:25:01 +00:00
|
|
|
}
|
|
|
|
}
|
2019-06-19 19:25:08 +00:00
|
|
|
|
2020-01-20 13:37:29 +00:00
|
|
|
// Save any unsaved changes to settings and meta
|
2019-08-18 19:21:16 +00:00
|
|
|
global_state.settings.save_to_file_warn();
|
2020-01-20 13:37:29 +00:00
|
|
|
global_state.meta.save_to_file_warn();
|
2019-01-02 21:25:01 +00:00
|
|
|
}
|