#![forbid(unsafe_code)]
#![deny(anonymous_parameters)]
#![deny(elided_lifetimes_in_paths)]
#![deny(ellipsis_inclusive_range_patterns)]
#![deny(nonstandard_style)]
#![deny(rust_2018_idioms)]
#![deny(trivial_numeric_casts)]
#![deny(broken_intra_doc_links)]
#![warn(missing_copy_implementations)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(variant_size_differences)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::default_trait_access)]
#![deny(clippy::empty_enum)]
#![deny(clippy::enum_glob_use)]
#![deny(clippy::expl_impl_clone_on_copy)]
#![deny(clippy::explicit_into_iter_loop)]
#![deny(clippy::explicit_iter_loop)]
#![deny(clippy::filter_map)]
#![deny(clippy::filter_map_next)]
#![deny(clippy::find_map)]
#![deny(clippy::if_not_else)]
#![deny(clippy::invalid_upcast_comparisons)]
#![deny(clippy::items_after_statements)]
#![deny(clippy::large_digit_groups)]
#![deny(clippy::map_flatten)]
#![deny(clippy::match_same_arms)]
#![deny(clippy::mut_mut)]
#![deny(clippy::needless_continue)]
#![deny(clippy::needless_pass_by_value)]
#![deny(clippy::map_unwrap_or)]
#![deny(clippy::redundant_closure_for_method_calls)]
#![deny(clippy::single_match_else)]
#![deny(clippy::string_add_assign)]
#![deny(clippy::type_repetition_in_bounds)]
#![deny(clippy::unseparated_literal_suffix)]
#![deny(clippy::unused_self)]
#![deny(clippy::use_self)]
#![deny(clippy::used_underscore_binding)]
#![warn(clippy::pub_enum_variant_names)]
#![warn(clippy::shadow_unrelated)]
#![warn(clippy::similar_names)]
#![warn(clippy::too_many_lines)]
mod input;
mod render_system;
mod root_console;
pub mod doryen {
pub use doryen_rs::*;
}
pub use input::{Input, Keys, MouseButton};
pub use render_system::RenderSystemExtensions;
pub use root_console::RootConsole;
use crate::doryen::{AppOptions, Console};
use crate::render_system::DoryenRenderSystems;
use bevy_app::{App as BevyApp, AppBuilder, AppExit, EventReader, Events, Plugin};
use bevy_ecs::Schedule;
use doryen_rs::{App as DoryenApp, DoryenApi, Engine, UpdateEvent};
use std::borrow::Cow;
#[derive(Default, Clone, Copy, Debug)]
pub struct DoryenPlugin;
pub struct DoryenPluginSettings {
pub app_options: AppOptions,
pub mouse_button_listeners: Vec<MouseButton>,
pub resize_mode: ResizeMode,
}
impl std::fmt::Debug for DoryenPluginSettings {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DoryenPluginSettings")
.field("app_options", &"<Not Debug>")
.field("mouse_button_listeners", &self.mouse_button_listeners)
.field("resize_mode", &self.resize_mode)
.finish()
}
}
impl Default for DoryenPluginSettings {
fn default() -> Self {
Self {
app_options: AppOptions::default(),
mouse_button_listeners: vec![
MouseButton::Left,
MouseButton::Middle,
MouseButton::Right,
],
resize_mode: ResizeMode::Nothing,
}
}
}
pub mod render_stage {
pub const FIRST: &str = "first";
pub const PRE_RENDER: &str = "pre_render";
pub const RENDER: &str = "render";
pub const POST_RENDER: &str = "post_render";
pub const LAST: &str = "last";
}
impl Plugin for DoryenPlugin {
fn build(&self, app: &mut AppBuilder) {
app.init_resource::<RootConsole>()
.init_resource::<Input>()
.init_resource::<FpsInfo>()
.add_event::<SetFontPath>()
.add_event::<Resized>()
.init_resource::<DoryenRenderSystems>()
.set_runner(doryen_runner);
}
}
struct DoryenPluginEngine {
bevy_app: BevyApp,
app_exit_event_reader: EventReader<AppExit>,
set_font_path_event_reader: EventReader<SetFontPath>,
swap_console: Option<Console>,
mouse_button_listeners: Vec<MouseButton>,
previous_screen_size: (u32, u32),
previous_console_size: (u32, u32),
resize_mode: ResizeMode,
}
impl DoryenPluginEngine {
#[inline]
fn take_root_console_ownership(&mut self, api: &mut dyn DoryenApi) {
use std::mem::swap;
swap(api.con(), &mut self.swap_console.as_mut().unwrap());
let mut doryen_root_console = self.bevy_app.resources.get_mut::<RootConsole>().unwrap();
doryen_root_console.0 = self.swap_console.take();
}
#[inline]
fn restore_root_console_ownership(&mut self, api: &mut dyn DoryenApi) {
use std::mem::swap;
let mut doryen_root_console = self.bevy_app.resources.get_mut::<RootConsole>().unwrap();
self.swap_console = doryen_root_console.0.take();
swap(api.con(), &mut self.swap_console.as_mut().unwrap());
}
#[inline]
fn take_doryen_render_schedule(&mut self) -> Schedule {
let mut doryen_render_systems = self
.bevy_app
.resources
.get_mut::<DoryenRenderSystems>()
.unwrap();
doryen_render_systems.0.take().unwrap()
}
#[inline]
fn restore_doryen_render_schedule(&mut self, doryen_render_schedule: Schedule) {
let mut doryen_render_systems = self
.bevy_app
.resources
.get_mut::<DoryenRenderSystems>()
.unwrap();
doryen_render_systems.0.replace(doryen_render_schedule);
}
#[inline]
fn handle_input(&mut self, api: &mut dyn DoryenApi) {
let mut doryen_input = self.bevy_app.resources.get_mut::<Input>().unwrap();
let input = api.input();
doryen_input.handle_input(&self.mouse_button_listeners, input);
}
}
impl Engine for DoryenPluginEngine {
fn update(&mut self, api: &mut dyn DoryenApi) -> Option<UpdateEvent> {
let mut doryen_fps_info = self.bevy_app.resources.get_mut::<FpsInfo>().unwrap();
doryen_fps_info.fps = api.fps();
doryen_fps_info.average_fps = api.average_fps();
drop(doryen_fps_info);
self.handle_input(api);
self.take_root_console_ownership(api);
self.bevy_app.update();
self.restore_root_console_ownership(api);
let doryen_set_font_path_events = self
.bevy_app
.resources
.get_mut::<Events<SetFontPath>>()
.unwrap();
if let Some(doryen_set_font_path) = self
.set_font_path_event_reader
.latest(&doryen_set_font_path_events)
{
api.set_font_path(doryen_set_font_path.0.as_ref());
}
if let Some(app_exit_events) = self.bevy_app.resources.get_mut::<Events<AppExit>>() {
if self
.app_exit_event_reader
.latest(&app_exit_events)
.is_some()
{
return Some(UpdateEvent::Exit);
}
}
None
}
fn render(&mut self, api: &mut dyn DoryenApi) {
self.take_root_console_ownership(api);
let mut doryen_render_schedule = self.take_doryen_render_schedule();
doryen_render_schedule
.initialize_and_run(&mut self.bevy_app.world, &mut self.bevy_app.resources);
self.restore_doryen_render_schedule(doryen_render_schedule);
self.restore_root_console_ownership(api);
}
fn resize(&mut self, api: &mut dyn DoryenApi) {
let (previous_width, previous_height) = self.previous_screen_size;
let (new_width, new_height) = api.get_screen_size();
let mut resized_events = self
.bevy_app
.resources
.get_mut::<Events<Resized>>()
.unwrap();
let resized = Resized {
previous_width,
previous_height,
new_width,
new_height,
};
resized_events.send(resized);
drop(resized_events);
match self.resize_mode {
ResizeMode::Nothing => (),
ResizeMode::Automatic => {
let (previous_console_width, previous_console_height) = self.previous_console_size;
let w_ratio = previous_width / previous_console_width;
let h_ratio = previous_height / previous_console_height;
let new_console_width = new_width / w_ratio;
let new_console_height = new_height / h_ratio;
api.con().resize(new_console_width, new_console_height);
}
ResizeMode::Callback(callback) => {
self.take_root_console_ownership(api);
callback(&mut *self.bevy_app.resources.get_mut().unwrap(), resized);
self.restore_root_console_ownership(api);
}
}
self.previous_screen_size = (new_width, new_height);
self.previous_console_size = api.con().get_size();
}
}
fn doryen_runner(mut app: BevyApp) {
let mut resource_settings = app
.resources
.get_or_insert_with(DoryenPluginSettings::default);
let DoryenPluginSettings {
app_options,
mouse_button_listeners,
resize_mode,
} = std::mem::take(&mut *resource_settings);
drop(resource_settings);
let AppOptions {
screen_height,
screen_width,
console_height,
console_width,
..
} = app_options;
let mut doryen_app = DoryenApp::new(app_options);
doryen_app.set_engine(Box::new(DoryenPluginEngine {
bevy_app: app,
app_exit_event_reader: EventReader::default(),
set_font_path_event_reader: EventReader::default(),
swap_console: Some(Console::new(1, 1)),
mouse_button_listeners,
previous_screen_size: (screen_width, screen_height),
previous_console_size: (console_width, console_height),
resize_mode,
}));
doryen_app.run();
}
#[derive(Default, Debug, Clone, Copy)]
pub struct FpsInfo {
pub fps: u32,
pub average_fps: u32,
}
#[derive(Debug, Clone)]
pub struct SetFontPath(pub Cow<'static, str>);
#[derive(Debug, Clone, Copy)]
pub struct Resized {
pub previous_width: u32,
pub previous_height: u32,
pub new_width: u32,
pub new_height: u32,
}
#[derive(Clone, Copy)]
pub enum ResizeMode {
Nothing,
Automatic,
Callback(fn(&mut RootConsole, Resized)),
}
impl std::fmt::Debug for ResizeMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Nothing => f.write_str("Nothing"),
Self::Automatic => f.write_str("Automatic"),
Self::Callback(_) => f.write_str("Callback"),
}
}
}