Reorganize project to workspace
This commit is contained in:
parent
92c0278ef0
commit
960e2f8a37
39 changed files with 4420 additions and 1189 deletions
18
khors-core/Cargo.toml
Normal file
18
khors-core/Cargo.toml
Normal file
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
name = "khors-core"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
egui-vulkano = { path = "../vendor/egui-vulkano", version = "0.1.0" }
|
||||
|
||||
flax = { version = "0.6.2", features = ["derive", "serde", "tokio", "tracing"] }
|
||||
flume = "0.11.0"
|
||||
anyhow = "1.0.80"
|
||||
vulkano = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" }
|
||||
vulkano-util = { git = "https://github.com/vulkano-rs/vulkano.git", branch = "master" }
|
||||
winit = { version = "0.29.15",features = ["rwh_05"] }
|
||||
parking_lot = "0.12.1"
|
||||
downcast-rs = "1.2.0"
|
315
khors-core/src/app.rs
Normal file
315
khors-core/src/app.rs
Normal file
|
@ -0,0 +1,315 @@
|
|||
#![warn(dead_code)]
|
||||
|
||||
use crate::{
|
||||
debug_gui::DebugGuiStack,
|
||||
events::Events,
|
||||
module::{Module, ModulesStack, RenderModule as ThreadLocalModule, RenderModulesStack},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use flax::{component, Schedule, World};
|
||||
use vulkano::device::DeviceFeatures;
|
||||
use vulkano_util::{
|
||||
context::{VulkanoConfig, VulkanoContext},
|
||||
window::VulkanoWindows,
|
||||
};
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
component! {
|
||||
window_id: WindowId,
|
||||
|
||||
resources,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct App {
|
||||
name: String,
|
||||
modules: ModulesStack,
|
||||
thread_local_modules: RenderModulesStack,
|
||||
world: World,
|
||||
schedule: Schedule,
|
||||
events: Events,
|
||||
rx: flume::Receiver<AppEvent>,
|
||||
running: bool,
|
||||
event_cleanup_time: std::time::Duration,
|
||||
vk_context: VulkanoContext,
|
||||
vk_windows: VulkanoWindows,
|
||||
debug_gui_stack: DebugGuiStack,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new() -> Self {
|
||||
let mut events = Events::new();
|
||||
let (tx, rx) = flume::unbounded();
|
||||
events.subscribe_custom(tx);
|
||||
|
||||
let schedule = Schedule::builder().build();
|
||||
|
||||
let vk_config = VulkanoConfig {
|
||||
device_features: DeviceFeatures {
|
||||
dynamic_rendering: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let vk_context = VulkanoContext::new(vk_config);
|
||||
let vk_windows = VulkanoWindows::default();
|
||||
|
||||
Self {
|
||||
name: "Khors".into(),
|
||||
modules: ModulesStack::new(),
|
||||
thread_local_modules: RenderModulesStack::new(),
|
||||
world: World::new(),
|
||||
schedule,
|
||||
events,
|
||||
rx,
|
||||
running: false,
|
||||
event_cleanup_time: std::time::Duration::from_secs(60),
|
||||
vk_context,
|
||||
vk_windows,
|
||||
debug_gui_stack: DebugGuiStack::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<()> {
|
||||
self.running = true;
|
||||
|
||||
self.schedule.execute_par(&mut self.world).unwrap();
|
||||
|
||||
let vk_context = &mut self.vk_context;
|
||||
let vk_windows = &mut self.vk_windows;
|
||||
let world = &mut self.world;
|
||||
let events = &mut self.events;
|
||||
let frame_time = std::time::Duration::from_millis(16);
|
||||
let gui_stack = &mut self.debug_gui_stack;
|
||||
|
||||
for module in self.modules.iter_mut() {
|
||||
module.on_update(world, events, frame_time)?;
|
||||
}
|
||||
|
||||
for module in self.thread_local_modules.iter_mut() {
|
||||
module.on_update(gui_stack, vk_context, vk_windows, world, events, frame_time)?;
|
||||
}
|
||||
|
||||
self.handle_events();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_window<T>(&mut self, event_loop: &winit::event_loop::EventLoopWindowTarget<T>)
|
||||
where
|
||||
T: Clone + Send + Sync,
|
||||
{
|
||||
let vk_window_id = self.vk_windows.create_window(
|
||||
event_loop,
|
||||
&self.vk_context,
|
||||
&vulkano_util::window::WindowDescriptor {
|
||||
title: self.name.clone(),
|
||||
present_mode: vulkano::swapchain::PresentMode::Mailbox,
|
||||
..Default::default()
|
||||
},
|
||||
|_| {},
|
||||
);
|
||||
|
||||
let renderer = self.vk_windows.get_renderer(vk_window_id).unwrap();
|
||||
|
||||
self.world
|
||||
.set(resources(), window_id(), vk_window_id)
|
||||
.unwrap();
|
||||
|
||||
self.debug_gui_stack
|
||||
.add_gui(vk_window_id, event_loop, renderer, true, false);
|
||||
}
|
||||
|
||||
pub fn process_event_loop<T>(
|
||||
&mut self,
|
||||
event: winit::event::Event<T>,
|
||||
_elwt: &winit::event_loop::EventLoopWindowTarget<T>,
|
||||
) -> Result<bool>
|
||||
where
|
||||
T: Clone + Send + Sync,
|
||||
{
|
||||
match &event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
return Ok(true);
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Focused(_),
|
||||
..
|
||||
} => self.events().send(event.clone()),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(..) | WindowEvent::ScaleFactorChanged { .. },
|
||||
window_id,
|
||||
} => self
|
||||
.vk_windows
|
||||
.get_renderer_mut(*window_id)
|
||||
.unwrap()
|
||||
.resize(),
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
window_id,
|
||||
} => 'redraw: {
|
||||
// Tasks for redrawing:
|
||||
// 1. Update state based on events
|
||||
// 2. Compute & Render
|
||||
// 3. Reset input state
|
||||
// 4. Update time & title
|
||||
|
||||
// The rendering part goes here:
|
||||
match self
|
||||
.vk_windows
|
||||
.get_renderer(*window_id)
|
||||
.unwrap()
|
||||
.window_size()
|
||||
{
|
||||
[w, h] => {
|
||||
// Skip this frame when minimized.
|
||||
if w == 0.0 || h == 0.0 {
|
||||
break 'redraw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.run()?;
|
||||
}
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
let window = self.vk_windows.get_window(*window_id).unwrap();
|
||||
let gui = self.debug_gui_stack.get_mut(*window_id).unwrap();
|
||||
gui.update(window, event);
|
||||
}
|
||||
Event::AboutToWait => {
|
||||
self.vk_windows.iter().for_each(|(window_id, _)| {
|
||||
self.vk_windows
|
||||
.get_window(*window_id)
|
||||
.unwrap()
|
||||
.request_redraw()
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
pub fn handle_events(&mut self) {
|
||||
for event in self.rx.try_iter() {
|
||||
match event {
|
||||
AppEvent::Exit => self.running = false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn set_schedule(&mut self, schedule: Schedule) {
|
||||
self.schedule = schedule;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn world(&self) -> &World {
|
||||
&self.world
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn world_mut(&mut self) -> &mut World {
|
||||
&mut self.world
|
||||
}
|
||||
|
||||
pub fn events(&self) -> &Events {
|
||||
&self.events
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn events_mut(&mut self) -> &mut Events {
|
||||
&mut self.events
|
||||
}
|
||||
|
||||
/// Pushes a module from the provided init closure to to the top of the layer stack. The provided
|
||||
/// closure to construct the layer takes in the world and events.
|
||||
pub fn push_render_module<F, T>(&mut self, func: F)
|
||||
where
|
||||
F: FnOnce(
|
||||
&mut VulkanoContext,
|
||||
&mut VulkanoWindows,
|
||||
&mut Schedule,
|
||||
&mut World,
|
||||
&mut Events,
|
||||
) -> T,
|
||||
T: 'static + ThreadLocalModule,
|
||||
{
|
||||
let module = func(
|
||||
&mut self.vk_context,
|
||||
&mut self.vk_windows,
|
||||
&mut self.schedule,
|
||||
&mut self.world,
|
||||
&mut self.events,
|
||||
);
|
||||
self.thread_local_modules.push(module);
|
||||
}
|
||||
|
||||
/// Pushes a layer from the provided init closure to to the top of the layer stack. The provided
|
||||
/// closure to construct the layer takes in the world and events.
|
||||
pub fn push_module<F, T>(&mut self, func: F)
|
||||
where
|
||||
F: FnOnce(&mut Schedule, &mut World, &mut Events) -> T,
|
||||
T: 'static + Module,
|
||||
{
|
||||
let module = func(&mut self.schedule, &mut self.world, &mut self.events);
|
||||
self.modules.push(module);
|
||||
}
|
||||
|
||||
/// Pushes a module from the provided init closure to to the top of the module stack. The provided
|
||||
/// closure to construct the module takes in the world and events, and may return an error which
|
||||
/// is propagated to the callee.
|
||||
#[allow(dead_code)]
|
||||
pub fn try_push_module<F, T, E>(&mut self, func: F) -> Result<(), E>
|
||||
where
|
||||
F: FnOnce(&mut World, &mut Events) -> Result<T, E>,
|
||||
T: 'static + Module,
|
||||
{
|
||||
let module = func(&mut self.world, &mut self.events)?;
|
||||
self.modules.push(module);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Inserts a module from the provided init closure to to the top of the module stack. The provided
|
||||
/// closure to construct the module takes in the world and events.
|
||||
#[allow(dead_code)]
|
||||
pub fn insert_module<F, T>(&mut self, index: usize, func: F)
|
||||
where
|
||||
F: FnOnce(&mut World, &mut Events) -> T,
|
||||
T: 'static + Module,
|
||||
{
|
||||
let module = func(&mut self.world, &mut self.events);
|
||||
self.modules.insert(index, module);
|
||||
}
|
||||
|
||||
/// Pushes a module from the provided init closure to to the top of the module stack. The provided
|
||||
/// closure to construct the module takes in the world and events, and may return an error which
|
||||
/// is propagated to the callee.
|
||||
#[allow(dead_code)]
|
||||
pub fn try_insert_module<F, T, E>(&mut self, index: usize, func: F) -> Result<(), E>
|
||||
where
|
||||
F: FnOnce(&mut World, &mut Events) -> Result<T, E>,
|
||||
T: 'static + Module,
|
||||
{
|
||||
let module = func(&mut self.world, &mut self.events)?;
|
||||
self.modules.insert(index, module);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
#[allow(dead_code)]
|
||||
pub enum AppEvent {
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
49
khors-core/src/debug_gui.rs
Normal file
49
khors-core/src/debug_gui.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use egui_vulkano::{Gui, GuiConfig};
|
||||
use vulkano_util::renderer::VulkanoWindowRenderer;
|
||||
use winit::{event_loop::EventLoopWindowTarget, window::WindowId};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DebugGuiStack {
|
||||
guis: HashMap<WindowId, Gui>,
|
||||
}
|
||||
|
||||
impl DebugGuiStack {
|
||||
pub fn add_gui<T>(
|
||||
&mut self,
|
||||
window_id: WindowId,
|
||||
event_loop: &EventLoopWindowTarget<T>,
|
||||
renderer: &VulkanoWindowRenderer,
|
||||
is_overlay: bool,
|
||||
allow_srgb_render_target: bool,
|
||||
) where
|
||||
T: Clone + Send + Sync,
|
||||
{
|
||||
let gui = Gui::new(
|
||||
event_loop,
|
||||
renderer.surface().clone(),
|
||||
renderer.graphics_queue().clone(),
|
||||
renderer.swapchain_format(),
|
||||
GuiConfig {
|
||||
is_overlay,
|
||||
allow_srgb_render_target,
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
self.guis.insert(window_id, gui);
|
||||
}
|
||||
|
||||
pub fn remove_gui(&mut self, window_id: WindowId) {
|
||||
self.guis.remove(&window_id).unwrap();
|
||||
}
|
||||
|
||||
pub fn get(&mut self, window_id: WindowId) -> Option<&Gui> {
|
||||
self.guis.get(&window_id)
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, window_id: WindowId) -> Option<&mut Gui> {
|
||||
self.guis.get_mut(&window_id)
|
||||
}
|
||||
}
|
183
khors-core/src/events/dispatcher.rs
Normal file
183
khors-core/src/events/dispatcher.rs
Normal file
|
@ -0,0 +1,183 @@
|
|||
use std::sync::mpsc;
|
||||
|
||||
use downcast_rs::{impl_downcast, Downcast};
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use super::Event;
|
||||
|
||||
pub trait AnyEventDispatcher: 'static + Send + Sync + Downcast {
|
||||
fn cleanup(&mut self);
|
||||
}
|
||||
|
||||
impl_downcast!(AnyEventDispatcher);
|
||||
|
||||
pub trait AnyEventSender: 'static + Send + Sync + Downcast {}
|
||||
impl_downcast!(AnyEventSender);
|
||||
|
||||
/// Handles event dispatching for a single type of event
|
||||
pub struct EventDispatcher<T: Event> {
|
||||
subscribers: Vec<Subscriber<T>>,
|
||||
pub blocked: bool,
|
||||
}
|
||||
|
||||
impl<T> Default for EventDispatcher<T>
|
||||
where
|
||||
T: Event + Clone,
|
||||
{
|
||||
fn default() -> Self {
|
||||
EventDispatcher::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventDispatcher<T>
|
||||
where
|
||||
T: Event + Clone,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
subscribers: Vec::new(),
|
||||
blocked: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends an event to all subscribed subscriber. Event is cloned for each registered subscriber. Requires mutable access to cleanup no longer active subscribers.
|
||||
pub fn send(&self, event: T) {
|
||||
if self.blocked {
|
||||
return;
|
||||
}
|
||||
|
||||
for subscriber in &self.subscribers {
|
||||
if (subscriber.filter)(&event) {
|
||||
subscriber.send(event.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subscribes to events using sender to send events. The subscriber is automatically cleaned
|
||||
/// up when the receiving end is dropped.
|
||||
pub fn subscribe<S>(&mut self, sender: S, filter: fn(&T) -> bool)
|
||||
where
|
||||
S: 'static + EventSender<T> + Send,
|
||||
{
|
||||
self.subscribers.push(Subscriber::new(sender, filter));
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Event> AnyEventDispatcher for EventDispatcher<T> {
|
||||
fn cleanup(&mut self) {
|
||||
self.subscribers.retain(|val| !val.sender.is_disconnected())
|
||||
}
|
||||
}
|
||||
|
||||
struct Subscriber<T> {
|
||||
sender: Box<dyn EventSender<T> + Send>,
|
||||
filter: fn(&T) -> bool,
|
||||
}
|
||||
|
||||
impl<T: Event> Subscriber<T> {
|
||||
pub fn new<S>(sender: S, filter: fn(&T) -> bool) -> Self
|
||||
where
|
||||
S: 'static + EventSender<T> + Send,
|
||||
{
|
||||
Self {
|
||||
sender: Box::new(sender),
|
||||
filter,
|
||||
}
|
||||
}
|
||||
pub fn send(&self, event: T) {
|
||||
self.sender.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a type which can send events. Implemented for mpsc::channel and crossbeam channel.
|
||||
pub trait EventSender<T>: 'static + Send + Sync {
|
||||
/// Send an event
|
||||
fn send(&self, event: T);
|
||||
/// Returns true if the sender has been disconnected
|
||||
fn is_disconnected(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Wrapper for thread safe sender
|
||||
pub struct MpscSender<T> {
|
||||
inner: Mutex<(bool, mpsc::Sender<T>)>,
|
||||
}
|
||||
|
||||
impl<T> From<mpsc::Sender<T>> for MpscSender<T> {
|
||||
fn from(val: mpsc::Sender<T>) -> Self {
|
||||
Self::new(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MpscSender<T> {
|
||||
pub fn new(inner: mpsc::Sender<T>) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new((false, inner)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Event> EventSender<T> for MpscSender<T> {
|
||||
fn send(&self, event: T) {
|
||||
let mut inner = self.inner.lock();
|
||||
match inner.1.send(event) {
|
||||
Ok(_) => {}
|
||||
Err(_) => inner.0 = true,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_disconnected(&self) -> bool {
|
||||
// TODO
|
||||
self.inner.lock().0
|
||||
// self.inner.is_disconnected()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "crossbeam-channel")]
|
||||
impl<T: Event> EventSender<T> for crossbeam_channel::Sender<T> {
|
||||
fn send(&self, event: T) -> bool {
|
||||
let _ = self.send(event);
|
||||
}
|
||||
|
||||
fn is_disconnected(&self) -> bool {
|
||||
self.is_disconnected
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Event> EventSender<T> for flume::Sender<T> {
|
||||
fn send(&self, event: T) {
|
||||
let _ = self.send(event);
|
||||
}
|
||||
|
||||
fn is_disconnected(&self) -> bool {
|
||||
self.is_disconnected()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_event_dispatcher<T: Event + Clone>() -> Box<dyn AnyEventDispatcher> {
|
||||
let dispatcher: EventDispatcher<T> = EventDispatcher::new();
|
||||
Box::new(dispatcher)
|
||||
}
|
||||
|
||||
pub struct ConcreteSender<T> {
|
||||
inner: Box<dyn EventSender<T>>,
|
||||
}
|
||||
|
||||
impl<T> ConcreteSender<T> {
|
||||
pub fn new<S: EventSender<T>>(sender: S) -> Self {
|
||||
Self {
|
||||
inner: Box::new(sender),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Event> EventSender<T> for ConcreteSender<T> {
|
||||
fn send(&self, event: T) {
|
||||
self.inner.send(event)
|
||||
}
|
||||
|
||||
fn is_disconnected(&self) -> bool {
|
||||
self.inner.is_disconnected()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Event> AnyEventSender for ConcreteSender<T> {}
|
208
khors-core/src/events/mod.rs
Normal file
208
khors-core/src/events/mod.rs
Normal file
|
@ -0,0 +1,208 @@
|
|||
mod dispatcher;
|
||||
pub use dispatcher::EventSender;
|
||||
|
||||
use std::{
|
||||
any::{type_name, TypeId},
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
fmt::Display,
|
||||
};
|
||||
|
||||
use self::dispatcher::{
|
||||
new_event_dispatcher, AnyEventDispatcher, AnyEventSender, ConcreteSender, EventDispatcher,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct AlreadyIntercepted {
|
||||
ty: &'static str,
|
||||
}
|
||||
|
||||
impl Display for AlreadyIntercepted {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Events of type {:?} have already been intercepted",
|
||||
self.ty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for AlreadyIntercepted {}
|
||||
|
||||
/// Manages event broadcasting for different types of events.
|
||||
/// Sending an event will send a clone of the event to all subscribed listeners.
|
||||
///
|
||||
/// The event listeners can be anything implementing `EventSender`. Implemented by `std::sync::mpsc::Sender`,
|
||||
/// `flume::Sender`, `crossbeam_channel::Sender`.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use ivy_base::Events;
|
||||
/// use std::sync::mpsc;
|
||||
/// let mut events = Events::new();
|
||||
///
|
||||
/// let (tx1, rx1) = mpsc::channel::<&'static str>();
|
||||
/// events.subscribe(tx1);
|
||||
///
|
||||
/// let (tx2, rx2) = mpsc::channel::<&'static str>();
|
||||
/// events.subscribe(tx2);
|
||||
///
|
||||
/// events.send("Hello");
|
||||
///
|
||||
/// if let Ok(e) = rx1.try_recv() {
|
||||
/// println!("1 Received: {}", e);
|
||||
/// }
|
||||
///
|
||||
/// if let Ok(e) = rx2.try_recv() {
|
||||
/// println!("2 Received: {}", e);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Events {
|
||||
dispatchers: HashMap<TypeId, Box<dyn AnyEventDispatcher>>,
|
||||
// A single receiver to intercept events
|
||||
intercepts: HashMap<TypeId, Box<dyn AnyEventSender>>,
|
||||
}
|
||||
|
||||
impl Events {
|
||||
pub fn new() -> Events {
|
||||
Self {
|
||||
dispatchers: HashMap::new(),
|
||||
intercepts: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the internal dispatcher for the specified event type.
|
||||
pub fn dispatcher<T: Event>(&self) -> Option<&EventDispatcher<T>> {
|
||||
self.dispatchers.get(&TypeId::of::<T>()).map(|val| {
|
||||
val.downcast_ref::<EventDispatcher<T>>()
|
||||
.expect("Failed to downcast")
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the internal dispatcher for the specified event type.
|
||||
pub fn dispatcher_mut<T: Event + Clone>(&mut self) -> &mut EventDispatcher<T> {
|
||||
self.dispatchers
|
||||
.entry(TypeId::of::<T>())
|
||||
.or_insert_with(new_event_dispatcher::<T>)
|
||||
.downcast_mut::<EventDispatcher<T>>()
|
||||
.expect("Failed to downcast")
|
||||
}
|
||||
|
||||
/// Sends an event of type `T` to all subscribed listeners.
|
||||
/// If no dispatcher exists for event `T`, a new one will be created.
|
||||
pub fn send<T: Event + Clone>(&self, event: T) {
|
||||
if let Some(intercept) = self.intercepts.get(&TypeId::of::<T>()) {
|
||||
intercept
|
||||
.downcast_ref::<ConcreteSender<T>>()
|
||||
.unwrap()
|
||||
.send(event);
|
||||
} else if let Some(dispatcher) = self.dispatcher() {
|
||||
dispatcher.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send an event after intercept, this function avoids intercepts.
|
||||
/// It can also be useful if the message is not supposed to be intercepted
|
||||
pub fn intercepted_send<T: Event + Clone>(&self, event: T) {
|
||||
if let Some(dispatcher) = self.dispatcher() {
|
||||
dispatcher.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
/// Intercept an event before it is broadcasted. Use
|
||||
/// `Events::intercepted_send` to send.
|
||||
pub fn intercept<T: Event, S: EventSender<T>>(
|
||||
&mut self,
|
||||
sender: S,
|
||||
) -> Result<(), AlreadyIntercepted> {
|
||||
match self.intercepts.entry(TypeId::of::<T>()) {
|
||||
std::collections::hash_map::Entry::Occupied(_) => Err(AlreadyIntercepted {
|
||||
ty: type_name::<T>(),
|
||||
}),
|
||||
std::collections::hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(Box::new(ConcreteSender::new(sender)));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand to subscribe using a flume channel.
|
||||
pub fn subscribe<T: Event + Clone>(&mut self) -> flume::Receiver<T> {
|
||||
let (tx, rx) = flume::unbounded();
|
||||
|
||||
self.dispatcher_mut().subscribe(tx, |_| true);
|
||||
dbg!(self.dispatchers.len());
|
||||
rx
|
||||
}
|
||||
/// Subscribes to an event of type T by sending events to the provided
|
||||
/// channel
|
||||
pub fn subscribe_custom<S, T: Event>(&mut self, sender: S)
|
||||
where
|
||||
S: 'static + EventSender<T> + Send,
|
||||
{
|
||||
self.dispatcher_mut().subscribe(sender, |_| true)
|
||||
}
|
||||
|
||||
/// Subscribes to an event of type T by sending events to teh provided
|
||||
/// channel
|
||||
pub fn subscribe_filter<S, T: Event + Clone>(&mut self, sender: S, filter: fn(&T) -> bool)
|
||||
where
|
||||
S: EventSender<T>,
|
||||
{
|
||||
self.dispatcher_mut().subscribe(sender, filter)
|
||||
}
|
||||
|
||||
/// Blocks all events of a certain type. All events sent will be silently
|
||||
/// ignored.
|
||||
pub fn block<T: Event + Clone>(&mut self, block: bool) {
|
||||
self.dispatcher_mut::<T>().blocked = block
|
||||
}
|
||||
|
||||
/// Return true if events of type T are blocked
|
||||
pub fn is_blocked<T: Event + Clone>(&mut self) -> bool {
|
||||
self.dispatcher_mut::<T>().blocked
|
||||
}
|
||||
|
||||
/// Remove disconnected subscribers
|
||||
pub fn cleanup(&mut self) {
|
||||
for (_, dispatcher) in self.dispatchers.iter_mut() {
|
||||
dispatcher.cleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Events {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Blanket type for events.
|
||||
pub trait Event: Send + Sync + 'static + Clone {}
|
||||
impl<T: Send + Sync + 'static + Clone> Event for T {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn event_broadcast() {
|
||||
let mut events = Events::new();
|
||||
|
||||
let (tx1, rx1) = flume::unbounded::<&'static str>();
|
||||
events.subscribe_custom(tx1);
|
||||
|
||||
let (tx2, rx2) = flume::unbounded::<&'static str>();
|
||||
events.subscribe_custom(tx2);
|
||||
|
||||
events.send("Hello");
|
||||
|
||||
if let Ok(e) = rx1.try_recv() {
|
||||
assert_eq!(e, "Hello")
|
||||
}
|
||||
|
||||
if let Ok(e) = rx2.try_recv() {
|
||||
assert_eq!(e, "Hello")
|
||||
}
|
||||
}
|
||||
}
|
5
khors-core/src/lib.rs
Normal file
5
khors-core/src/lib.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub mod app;
|
||||
pub mod events;
|
||||
pub mod module;
|
||||
pub mod time;
|
||||
pub mod debug_gui;
|
126
khors-core/src/module.rs
Normal file
126
khors-core/src/module.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use flax::World;
|
||||
|
||||
use super::events::Events;
|
||||
|
||||
use super::debug_gui::DebugGuiStack;
|
||||
|
||||
pub trait Module {
|
||||
fn on_update(&mut self, world: &mut World, events: &mut Events, frame_time: Duration) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct ModulesStack {
|
||||
modules: Vec<Box<dyn Module>>,
|
||||
}
|
||||
|
||||
impl ModulesStack {
|
||||
pub fn new() -> Self {
|
||||
Self { modules: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<Box<dyn Module>> {
|
||||
self.modules.iter()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<Box<dyn Module>> {
|
||||
self.modules.iter_mut()
|
||||
}
|
||||
|
||||
pub fn push<T: 'static + Module>(&mut self, layer: T) {
|
||||
let layer = Box::new(layer);
|
||||
self.modules.push(layer);
|
||||
}
|
||||
|
||||
pub fn insert<T: 'static + Module>(&mut self, index: usize, layer: T) {
|
||||
let layer = Box::new(layer);
|
||||
self.modules.insert(index, layer);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ModulesStack {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a ModulesStack {
|
||||
type Item = &'a Box<dyn Module>;
|
||||
|
||||
type IntoIter = std::slice::Iter<'a, Box<dyn Module>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut ModulesStack {
|
||||
type Item = &'a mut Box<dyn Module>;
|
||||
|
||||
type IntoIter = std::slice::IterMut<'a, Box<dyn Module>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
// THREAD LOCAL STUFF
|
||||
|
||||
pub trait RenderModule {
|
||||
fn on_update(&mut self, gui_stack: &mut DebugGuiStack, vk_context: &mut vulkano_util::context::VulkanoContext, vk_windows: &mut vulkano_util::window::VulkanoWindows, world: &mut World, events: &mut Events, frame_time: Duration) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct RenderModulesStack {
|
||||
modules: Vec<Box<dyn RenderModule>>,
|
||||
}
|
||||
|
||||
impl RenderModulesStack {
|
||||
pub fn new() -> Self {
|
||||
Self { modules: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> std::slice::Iter<Box<dyn RenderModule>> {
|
||||
self.modules.iter()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> std::slice::IterMut<Box<dyn RenderModule>> {
|
||||
self.modules.iter_mut()
|
||||
}
|
||||
|
||||
pub fn push<T: 'static + RenderModule>(&mut self, layer: T) {
|
||||
let layer = Box::new(layer);
|
||||
self.modules.push(layer);
|
||||
}
|
||||
|
||||
pub fn insert<T: 'static + RenderModule>(&mut self, index: usize, layer: T) {
|
||||
let layer = Box::new(layer);
|
||||
self.modules.insert(index, layer);
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RenderModulesStack {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a RenderModulesStack {
|
||||
type Item = &'a Box<dyn RenderModule>;
|
||||
|
||||
type IntoIter = std::slice::Iter<'a, Box<dyn RenderModule>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut RenderModulesStack {
|
||||
type Item = &'a mut Box<dyn RenderModule>;
|
||||
|
||||
type IntoIter = std::slice::IterMut<'a, Box<dyn RenderModule>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
43
khors-core/src/time.rs
Normal file
43
khors-core/src/time.rs
Normal file
|
@ -0,0 +1,43 @@
|
|||
//! Provides time related functionality for Clocks.
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use flax::component;
|
||||
|
||||
component! {
|
||||
pub clock: Clock,
|
||||
}
|
||||
|
||||
/// Measures high precision time
|
||||
#[allow(dead_code)]
|
||||
pub struct Clock {
|
||||
start: Instant,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Clock {
|
||||
// Creates and starts a new clock
|
||||
pub fn new() -> Self {
|
||||
Clock {
|
||||
start: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the elapsed time
|
||||
pub fn elapsed(&self) -> Duration {
|
||||
Instant::now() - self.start
|
||||
}
|
||||
|
||||
// Resets the clock and returns the elapsed time
|
||||
pub fn reset(&mut self) -> Duration {
|
||||
let elapsed = self.elapsed();
|
||||
|
||||
self.start = Instant::now();
|
||||
elapsed
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Clock {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue