2019-01-23 20:01:58 +00:00
#![ feature(drain_filter) ]
2019-05-25 12:53:44 +00:00
#![ feature(type_alias_enum_variants) ]
2019-04-29 20:37:19 +00:00
#![ recursion_limit = " 2048 " ]
2019-01-23 20:01:58 +00:00
2019-04-26 03:30:46 +00:00
#[ macro_use ]
pub mod ui ;
2019-01-13 20:53:55 +00:00
pub mod anim ;
2019-05-11 20:39:46 +00:00
pub mod audio ;
2019-01-11 23:18:34 +00:00
pub mod error ;
2019-03-15 04:55:52 +00:00
pub mod hud ;
2019-03-02 03:48:30 +00:00
pub mod key_state ;
2019-01-11 23:18:34 +00:00
pub mod menu ;
2019-01-13 20:53:55 +00:00
pub mod mesh ;
2019-01-11 23:18:34 +00:00
pub mod render ;
pub mod scene ;
pub mod session ;
2019-04-15 17:51:26 +00:00
pub mod settings ;
2019-04-17 14:57:23 +00:00
pub mod singleplayer ;
2019-04-29 20:37:19 +00:00
pub mod window ;
2019-04-15 17:51:26 +00:00
2019-01-11 23:18:34 +00:00
// Reexports
pub use crate ::error ::Error ;
2019-01-02 21:25:01 +00:00
2019-07-02 18:35:40 +00:00
use crate ::{
audio ::{ base ::Genre , AudioFrontend } ,
menu ::main ::MainMenuState ,
settings ::Settings ,
window ::Window ,
} ;
2019-06-06 14:48:41 +00:00
use log ::{ debug , error , info , warn } ;
2019-04-29 20:37:19 +00:00
use simplelog ::{ CombinedLogger , Config , TermLogger , WriteLogger } ;
2019-06-06 14:48:41 +00:00
use std ::{ fs ::File , mem , panic , str ::FromStr } ;
2019-04-17 13:23:34 +00:00
2019-05-17 09:22:32 +00:00
/// A type used to store state that is shared between all play states.
2019-01-02 21:25:01 +00:00
pub struct GlobalState {
2019-04-15 17:51:26 +00:00
settings : Settings ,
2019-01-02 21:25:01 +00:00
window : Window ,
2019-05-11 20:39:46 +00:00
audio : AudioFrontend ,
2019-01-02 21:25:01 +00:00
}
2019-01-12 01:14:58 +00:00
impl GlobalState {
2019-05-17 09:22:32 +00:00
/// Called after a change in play state has occurred (usually used to reverse any temporary
2019-01-12 13:56:34 +00:00
/// effects a state may have made).
2019-01-12 01:14:58 +00:00
pub fn on_play_state_changed ( & mut self ) {
2019-01-23 22:21:47 +00:00
self . window . grab_cursor ( false ) ;
2019-03-28 04:42:04 +00:00
self . window . needs_refresh_resize ( ) ;
2019-01-12 01:14:58 +00:00
}
2019-05-18 19:28:12 +00:00
pub fn maintain ( & mut self ) {
2019-07-02 04:10:55 +00:00
// TODO: Maintain both `Bgm` and `Sfx` audio threads.
self . audio . play ( ) ;
2019-05-18 19:28:12 +00:00
}
2019-01-12 01:14:58 +00:00
}
2019-04-27 20:55:30 +00:00
pub enum Direction {
Forwards ,
Backwards ,
}
2019-05-17 09:22:32 +00:00
/// States can either close (and revert to a previous state), push a new state on top of themselves,
/// or switch to a totally different state.
2019-01-07 21:10:31 +00:00
pub enum PlayStateResult {
2019-05-17 09:22:32 +00:00
/// Pop all play states in reverse order and shut down the program.
2019-01-07 21:10:31 +00:00
Shutdown ,
2019-05-17 09:22:32 +00:00
/// Close the current play state and pop it from the play state stack.
2019-01-11 23:18:34 +00:00
Pop ,
2019-05-17 09:22:32 +00:00
/// Push a new play state onto the play state stack.
2019-01-02 21:25:01 +00:00
Push ( Box < dyn PlayState > ) ,
2019-05-17 09:22:32 +00:00
/// Switch the current play state with a new play state.
2019-01-02 21:25:01 +00:00
Switch ( Box < dyn PlayState > ) ,
}
2019-01-11 23:18:34 +00:00
/// A trait representing a playable game state. This may be a menu, a game session, the title
/// screen, etc.
2019-01-02 21:25:01 +00:00
pub trait PlayState {
2019-01-11 23:18:34 +00:00
/// Play the state until some change of state is required (i.e: a menu is opened or the game
/// is closed).
2019-04-27 20:55:30 +00:00
fn play ( & mut self , direction : Direction , global_state : & mut GlobalState ) -> PlayStateResult ;
2019-01-11 23:18:34 +00:00
2019-05-17 09:22:32 +00:00
/// Get a descriptive name for this state type.
2019-01-11 23:18:34 +00:00
fn name ( & self ) -> & 'static str ;
2019-01-02 21:25:01 +00:00
}
fn main ( ) {
2019-05-17 09:22:32 +00:00
// Set up the global state.
2019-06-06 14:48:41 +00:00
let settings = Settings ::load ( ) ;
2019-05-27 12:16:25 +00:00
let mut global_state = GlobalState {
2019-07-02 04:10:55 +00:00
audio : AudioFrontend ::new ( ) , // TODO: Provide `AudioFrontend::no_audio()` feature during initialisation, the config will be stored in `ron` object list.
2019-06-06 14:48:41 +00:00
window : Window ::new ( & settings ) . expect ( " Failed to create window! " ) ,
2019-05-27 12:16:25 +00:00
settings ,
} ;
2019-05-17 09:22:32 +00:00
// Initialize logging.
2019-04-29 16:56:38 +00:00
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-04-25 11:49:36 +00:00
CombinedLogger ::init ( vec! [
2019-04-29 16:56:38 +00:00
TermLogger ::new ( term_log_level , Config ::default ( ) ) . unwrap ( ) ,
2019-04-29 20:37:19 +00:00
WriteLogger ::new (
log ::LevelFilter ::Info ,
Config ::default ( ) ,
2019-05-27 12:16:25 +00:00
File ::create ( & global_state . settings . log . file ) . unwrap ( ) ,
2019-04-29 20:37:19 +00:00
) ,
] )
. unwrap ( ) ;
2019-04-25 11:49:36 +00:00
2019-05-11 20:39:46 +00:00
// Set up panic handler to relay swish panic messages to the user
2019-05-27 12:16:25 +00:00
let settings_clone = global_state . settings . clone ( ) ;
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
}
2019-05-09 17:27:07 +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 : { } " ,
settings_clone . log . file , reason , panic_info ,
) ;
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 \n Backtrace: \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
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-07-02 18:35:40 +00:00
match global_state . audio . model . get_genre ( ) {
Genre ::Bgm = > {
global_state . settings . audio . audio_device =
Some ( crate ::audio ::base ::get_default_device ( ) )
}
Genre ::Sfx = > unimplemented! ( ) ,
Genre ::None = > global_state . settings . audio . audio_device = None ,
2019-05-21 01:21:31 +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-06-06 14:48:41 +00:00
. map ( | current_state | info! ( " 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?
// ---------------------
// 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
// 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-06-06 14:48:41 +00:00
info! ( " 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
} ) ;
}
2019-04-29 20:37:19 +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
} ) ;
2019-04-29 20:37:19 +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 ( ) ;
2019-04-29 20:37:19 +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
} ) ;
2019-04-29 20:37:19 +00:00
}
2019-01-02 21:25:01 +00:00
}
}
2019-05-27 12:16:25 +00:00
// Save settings to add new fields or create the file if it is not already there
2019-06-06 14:48:41 +00:00
if let Err ( err ) = global_state . settings . save_to_file ( ) {
warn! ( " Failed to save settings: {:?} " , err ) ;
}
2019-01-02 21:25:01 +00:00
}