Skip to content
This repository has been archived by the owner on Apr 18, 2022. It is now read-only.

Callback Queue: Future support #1125

Merged
merged 6 commits into from Nov 13, 2018
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -71,6 +71,7 @@ amethyst_renderer = { path = "amethyst_renderer", version = "0.9.0" }
amethyst_input = { path = "amethyst_input", version = "0.5.0" }
amethyst_ui = { path = "amethyst_ui", version = "0.4.0" }
amethyst_utils = { path = "amethyst_utils", version = "0.4.0" }
crossbeam-channel = "0.3.1"
derivative = "1.0"
fern = { version = "0.5", features = ["colored"] }
log = { version = "0.4", features = ["serde"] }
Expand Down
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Expand Up @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog][kc], and this project adheres to
* `UiWidget` variant `Custom` for custom composited widgets ([#1112])
* `AssetLoaderSystemData` abstracts resources needed from `World` to do asset loading ([#1090])
* `amethyst_ui::get_default_font` supports loading system font from Path. ([#1108])
* Added `Callback` and `CallbackQueue` for use in asynchronous contexts. ([#1125])

### Changed

Expand All @@ -41,6 +42,7 @@ The format is based on [Keep a Changelog][kc], and this project adheres to
[#1112]: https://github.com/amethyst/amethyst/pull/1112
[#1089]: https://github.com/amethyst/amethyst/pull/1089
[#1108]: https://github.com/amethyst/amethyst/pull/1108
[#1125]: https://github.com/amethyst/amethyst/pull/1125

## [0.9.0] - 2018-10
### Added
Expand Down
12 changes: 12 additions & 0 deletions src/app.rs
Expand Up @@ -11,6 +11,7 @@ use winit::Event;

use {
assets::{Loader, Source},
callback_queue::CallbackQueue,
core::{
frame_limiter::{FrameLimiter, FrameRateLimitConfig, FrameRateLimitStrategy},
shrev::{EventChannel, ReaderId},
Expand Down Expand Up @@ -292,6 +293,16 @@ where
states.stop(StateData::new(world, &mut self.data));
}

{
#[cfg(feature = "profiler")]
profile_scope!("run_callback_queue");
let mut world = &mut self.world;
let receiver = world.read_resource::<CallbackQueue>().receiver.clone();
while let Ok(func) = receiver.try_recv() {
func(&mut world);
}
}

{
#[cfg(feature = "profiler")]
profile_scope!("handle_event");
Expand Down Expand Up @@ -472,6 +483,7 @@ impl<S, E, X> ApplicationBuilder<S, E, X> {
world.add_resource(FrameLimiter::default());
world.add_resource(Stopwatch::default());
world.add_resource(Time::default());
world.add_resource(CallbackQueue::default());

world.register::<Named>();

Expand Down
46 changes: 46 additions & 0 deletions src/callback_queue.rs
@@ -0,0 +1,46 @@
use core::specs::World;
use crossbeam_channel::{Receiver, Sender};

/// The type of a callback.
/// This is meant to be created from within asynchonous functions (`Future` for example).
/// See `CallbackQueue` for more details.
pub type Callback = Box<Fn(&mut World) + Send>;

/// A simple `Callback` queue.
/// Using the `Sender` you can get using the `send_handle` method, you
/// can add functions modifying `World` from an asynchronous context.
/// Those callbacks will be ran sequentially without preserving ordering.
/// # Example
/// ```rust,ignore
/// // First, get a `Sender` handle.
/// let handle = world.read_resource::<CallbackQueue>().send_handle();
/// // Then, create your asynchronous context (Future, Callback-based library, etc..)
/// let future = ...;
/// // Finally, use that handle inside of the asynchronous context to run code that can affect `World`.
/// future.on_complete(move || {
/// handle.send(|mut world| world.create_entity().build()).expect("Failed to add Callback to CallbackQueue.");
/// });
/// ```
pub struct CallbackQueue {
sender: Sender<Callback>,
pub(crate) receiver: Receiver<Callback>,
}

impl CallbackQueue {
/// Creates a new `CallbackQueue`.
pub fn new() -> Self {
Default::default()
}

/// Creates a new handle that allows sending `Callback`s to the `CallbackQueue`.
pub fn send_handle(&self) -> Sender<Callback> {
self.sender.clone()
}
}

impl Default for CallbackQueue {
fn default() -> Self {
let (sender, receiver) = crossbeam_channel::unbounded();
CallbackQueue { sender, receiver }
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Expand Up @@ -78,6 +78,7 @@ pub extern crate amethyst_utils as utils;
pub extern crate winit;

extern crate amethyst_ui;
extern crate crossbeam_channel;
#[macro_use]
extern crate derivative;
extern crate fern;
Expand All @@ -93,6 +94,7 @@ pub use core::{shred, shrev, specs as ecs};

pub use self::{
app::{Application, ApplicationBuilder, CoreApplication},
callback_queue::{Callback, CallbackQueue},
error::{Error, Result},
game_data::{DataInit, GameData, GameDataBuilder},
logger::{start_logger, LevelFilter as LogLevelFilter, LoggerConfig, StdoutLog},
Expand All @@ -108,6 +110,7 @@ pub use derive::*;
pub mod prelude;

mod app;
mod callback_queue;
mod error;
mod game_data;
mod logger;
Expand Down
1 change: 1 addition & 0 deletions src/prelude.rs
Expand Up @@ -2,6 +2,7 @@

pub use {
app::{Application, ApplicationBuilder, CoreApplication},
callback_queue::{Callback, CallbackQueue},
config::Config,
core::WithNamed,
ecs::prelude::{Builder, World},
Expand Down