/*
* This file is part of laurelin/client
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
app::AppExit,
prelude::*,
window::{
CompositeAlphaMode, Cursor, PresentMode, WindowLevel, WindowMode, WindowResizeConstraints,
WindowResolution,
},
};
use bevy_egui::EguiPlugin;
use naia_bevy_client::{
Client, ClientConfig as NaiaClientConfig, Plugin as NaiaClientPlugin, ReceiveEvents,
};
use laurelin_shared::{server::protocol::protocol, types::user::UserPub};
mod cfg;
mod constants;
mod plugins;
mod runtime;
mod util;
#[cfg(feature = "dev")]
mod dev;
// NOTE: lua things, currently not in use
//use bevy_mod_scripting::prelude::*;
//mod lua;
/// Used to control the state of the game
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default, States)]
pub enum GameState {
#[default]
Splash,
Loading,
MainMenu,
Game,
}
#[derive(Resource)]
pub struct Global {
pub users_cache: Vec<UserPub>,
}
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
struct MainLoop;
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
struct AfterMainLoop;
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
cursor: Cursor::default(),
present_mode: PresentMode::Fifo,
mode: WindowMode::Windowed,
position: WindowPosition::Centered(MonitorSelection::Primary),
resolution: WindowResolution::new(1280., 720.),
title: "Laurelin".to_string(),
composite_alpha_mode: CompositeAlphaMode::Auto,
resize_constraints: WindowResizeConstraints {
min_width: 1280.,
min_height: 720.,
max_width: 3840.,
max_height: 2160.,
},
resizable: false,
decorations: true,
transparent: false,
focused: true,
window_level: WindowLevel::Normal,
canvas: None,
fit_canvas_to_parent: false,
prevent_default_event_handling: false,
ime_enabled: false,
..Default::default()
}),
..Default::default()
}));
app.configure_set(MainLoop.after(ReceiveEvents))
.configure_set(AfterMainLoop.after(MainLoop));
#[cfg(feature = "dev")]
app.add_plugin(dev::DevPlugin);
if !app.is_plugin_added::<EguiPlugin>() {
app.add_plugin(EguiPlugin);
}
//app.add_plugin(ScriptingPlugin).add_plugin(lua::LuaPlugin);
app.insert_resource(Global {
users_cache: vec![],
})
.insert_resource(cfg::CfgDirs(
directories::ProjectDirs::from("com", "liljamo", "deckbuilder")
.expect("failed to get project directories"),
));
app.insert_resource(cfg::CfgSettings::default())
.register_type::<cfg::CfgSettings>()
.insert_resource(cfg::CfgUser::default())
.register_type::<cfg::CfgUser>()
.insert_resource(cfg::CfgDev::default())
.register_type::<cfg::CfgDev>();
app.add_startup_system(setup).add_state::<GameState>();
app.add_plugin(plugins::config::ConfigPlugin)
.add_plugin(plugins::phases::splash::SplashPlugin)
.add_plugin(plugins::phases::loading::LoadingPlugin)
.add_plugin(plugins::menu::MenuPlugin);
// Lastly, add in runtime data structs
//app.insert_resource(runtime::menu::RTDMenu::default())
// .insert_resource(runtime::game::RTDGame::default());
// Networking
app.add_plugin(NaiaClientPlugin::new(
NaiaClientConfig::default(),
protocol(),
))
.add_plugin(plugins::networking::NetworkingPlugin);
// Graceful exit
app.add_event::<GracefulExit>()
.add_system(handle_graceful_exit.in_set(AfterMainLoop));
app.run();
}
fn setup(mut commands: Commands) {
// Spawn a camera
commands.spawn(Camera3dBundle::default());
}
/// Utility function do despawn an entity and all its children
pub fn despawn_screen<T: Component>(to_despawn: Query<Entity, With<T>>, mut commands: Commands) {
for entity in &to_despawn {
commands.entity(entity).despawn_recursive();
}
}
pub struct GracefulExit;
/// Graceful exit, disconnects client cleanly before closing
pub fn handle_graceful_exit(
mut ev: EventReader<GracefulExit>,
mut app_exit_events: EventWriter<AppExit>,
mut client: Client,
) {
for _ in ev.iter() {
if client.is_connected() {
client.disconnect();
}
app_exit_events.send(AppExit);
}
}