Skip to content

Commit

Permalink
22 events (#26)
Browse files Browse the repository at this point in the history
* add reloadable state

Signed-off-by: lee-orr <[email protected]>

* readme and docs

Signed-off-by: lee-orr <[email protected]>

* fix intro

Signed-off-by: lee-orr <[email protected]>

* start adding events

Signed-off-by: lee-orr <[email protected]>

* events

Signed-off-by: lee-orr <[email protected]>

---------

Signed-off-by: lee-orr <[email protected]>
  • Loading branch information
lee-orr authored Oct 13, 2023
1 parent a19fd86 commit 93b7f5e
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 3 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ jobs:
setup_on_reload,
setup_in_state,
replacable_state,
update_reloadable_event,
remote,
asset,
]
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Fuller documentation is available at: <https://lee-orr.github.io/dexterous_devel

## Features

- Define the reloadable areas of your game explicitly - which can include systems, components, states and resources (w/ some limitations)
- Define the reloadable areas of your game explicitly - which can include systems, components, states, events and resources (w/ some limitations)
- Reset resources to a default or pre-determined value upon reload
- Serialize/deserialize your reloadable resources & components, allowing you to evolve their schemas so long as they are compatible with the de-serializer (using rmp_serde)
- Mark entities to get removed on hot reload
Expand All @@ -23,7 +23,6 @@ Fuller documentation is available at: <https://lee-orr.github.io/dexterous_devel
## Known issues

- Won't work on mobile or WASM
- events still need to be pre-defined

## Installation

Expand Down
1 change: 1 addition & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## UPCOMING

- add support for hot re-loading states
- add support for hot re-loading events

## Version 0.0.11

Expand Down
5 changes: 5 additions & 0 deletions dexterous_developer_internal/src/bevy_support/cold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ impl<'a> ReloadableApp for ReloadableAppContents<'a> {
self
}

fn add_event<T: ReplacableEvent>(&mut self) -> &mut Self {
self.0.add_event::<T>();
self
}

fn add_state<S: super::ReplacableState>(&mut self) -> &mut Self {
self.0.add_state::<S>();
self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ impl<'a> crate::ReloadableApp for ReloadableAppContents<'a> {

self
}

fn add_event<T: ReplacableEvent>(&mut self) -> &mut Self {
self.insert_replacable_resource::<Events<T>>()
.add_systems(First, Events::<T>::update_system)
}
}

fn element_selection_condition(name: &'static str) -> impl Fn(Option<Res<ReloadSettings>>) -> bool {
Expand Down
17 changes: 17 additions & 0 deletions dexterous_developer_internal/src/bevy_support/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ impl<T: ReplacableResource> CustomReplacableResource for T {
pub trait ReplacableComponent: Component + Serialize + DeserializeOwned + Default {
fn get_type_name() -> &'static str;
}
pub trait ReplacableEvent: Event + Serialize + DeserializeOwned {
fn get_type_name() -> &'static str;
}

pub trait ReplacableState: States + Serialize + DeserializeOwned {
fn get_type_name() -> &'static str;
Expand Down Expand Up @@ -66,6 +69,19 @@ impl<S: ReplacableState> CustomReplacableResource for NextState<S> {
}
}

impl<S: ReplacableEvent> CustomReplacableResource for Events<S> {
fn get_type_name() -> &'static str {
S::get_type_name()
}

fn to_vec(&self) -> anyhow::Result<Vec<u8>> {
Ok(vec![])
}

fn from_slice(_: &[u8]) -> anyhow::Result<Self> {
Ok(Self::default())
}
}
pub(crate) mod private {
pub trait ReloadableAppSealed {}
}
Expand All @@ -88,6 +104,7 @@ pub trait ReloadableApp: private::ReloadableAppSealed {
state: S,
systems: impl IntoSystemConfigs<M>,
) -> &mut Self;
fn add_event<T: ReplacableEvent>(&mut self) -> &mut Self;
fn add_state<S: ReplacableState>(&mut self) -> &mut Self;
}

Expand Down
2 changes: 1 addition & 1 deletion docs/src/Intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This library provides an experimental hot reload system for Bevy.

## Features

- Define the reloadable areas of your game explicitly - which can include systems, components, state and resources (w/ some limitations)
- Define the reloadable areas of your game explicitly - which can include systems, components, state, events and resources (w/ some limitations)
- Reset resources to a default or pre-determined value upon reload
- serialize/deserialize your reloadable resources & components, allowing you to evolve their schemas so long as they are compatible with the de-serializer (using rmp_serde)
- mark entities to get removed on hot reload
Expand Down
25 changes: 25 additions & 0 deletions docs/src/reloadables.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,28 @@ impl ReplacableState for AppState {
Note that unlike `ReplacableResource` or `ReplacableComponent`, with `ReplacableState` you need to give it a name as well as giving a name for the `NextState<S>` resource it'll create.

You can then add the state using `.add_state::<ReplacableComponent>()`.

## Replacable Event

You can also create replacable Events. Here you implement the `ReplacableEvent` trait:

```rust
#[derive(Event, Clone, Debug, Serialize, Deserialize)]
pub enum AppEvent {
Text(String),
Shout(String)
}


impl ReplacableEvent for AppEvent {
fn get_type_name() -> &'static str {
"app-event"
}
}


```

Note that when events get replaced it *resets the event queue* - so all existing events will be cleared! Since as a rule events only persist to the next frame generally, this shouldn't be too much of an issue - depending on when you trigger the reload.

You can then add the state using `.add_event::<ReplacableEvent>()`.
45 changes: 45 additions & 0 deletions testing/dexterous_developer_tests/src/insert_replacable_event.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use bevy::prelude::*;
use dexterous_developer::*;
use serde::{Deserialize, Serialize};
use crate::shared::{StdInput};

fn update(input: Res<StdInput>, mut event: EventWriter<AppEvent>) {
let text = input.0.as_str();
println!("Got: {text}");
event.send(AppEvent::Text(text.to_string()))
}

fn read(input: Res<StdInput>, mut event: EventReader<AppEvent>) {
for event in event.iter() {
let text = match event {
AppEvent::Text(s) => format!("Text - {s}"),
};
println!("Event: {text}");
}
}



fn startup() {
println!("Press Enter to Progress, or type 'exit' to exit");
}

#[derive(Event, Clone, Debug, Serialize, Deserialize)]
pub enum AppEvent {
Text(String),
}


impl ReplacableEvent for AppEvent {
fn get_type_name() -> &'static str {
"app-event"
}
}

#[dexterous_developer_setup]
pub fn reloadable(app: &mut ReloadableAppContents) {
app
.add_event::<AppEvent>()
.add_systems(Startup, startup)
.add_systems(Update, (update, read).chain());
}
65 changes: 65 additions & 0 deletions testing/dexterous_developer_tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,66 @@ async fn can_run_existing(path: &Path) {
process.exit().await;
}

async fn update_reloadable_event() {
let mut project: TestProject =
TestProject::new("reloadables_test", "insert_replacable_event").unwrap();
let mut process = project.run_hot_cli().await.unwrap();

process.is_ready().await;

process.send("\n").expect("Failed to send empty line");

process.wait_for_lines(&["Ran Update"]).await;

project
.write_file(
PathBuf::from("src/update.rs").as_path(),
include_str!("./insert_replacable_event.txt"),
)
.expect("Couldn't update file");

process.has_updated().await;

process.send("test\n").expect("Failed to send empty line");

process
.wait_for_lines(&["Got: test", "Event: Text - test"])
.await;

process
.send("shout: test\n")
.expect("Failed to send empty line");

process
.wait_for_lines(&["Got: shout: test", "Event: Text - shout: test"])
.await;

project
.write_file(
PathBuf::from("src/update.rs").as_path(),
include_str!("./update_replacable_event.txt"),
)
.expect("Couldn't update file");

process.has_updated().await;

process.send("test\n").expect("Failed to send empty line");

process
.wait_for_lines(&["Got: test", "Event: Text - test"])
.await;

process
.send("shout: test\n")
.expect("Failed to send empty line");

process
.wait_for_lines(&["Got: shout: test", "Event: Shout - test"])
.await;

process.exit().await;
}

async fn replacable_state() {
let mut project: TestProject =
TestProject::new("reloadables_test", "insert_replacable_state").unwrap();
Expand Down Expand Up @@ -709,6 +769,9 @@ pub async fn run_tests() {
"setup_in_state" => {
run_setup_in_state().await;
}
"update_reloadable_event" => {
update_reloadable_event().await;
}
"replacable_state" => {
replacable_state().await;
}
Expand Down Expand Up @@ -752,6 +815,8 @@ pub async fn run_tests() {
println!("clear_on_reload");
println!("setup_on_reload");
println!("setup_in_state");
println!("update_reloadable_event");
println!("replacable_state");
std::process::exit(1)
}
}
Expand Down
50 changes: 50 additions & 0 deletions testing/dexterous_developer_tests/src/update_replacable_event.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use crate::shared::StdInput;
use bevy::prelude::*;
use dexterous_developer::*;
use serde::{Deserialize, Serialize};

fn update(input: Res<StdInput>, mut event: EventWriter<AppEvent>) {
let text = input.0.as_str();
println!("Got: {text}");
let e = if text.starts_with("shout:") {
let text_a = text.replace("shout:", "");
let text = text_a.trim();
AppEvent::Shout(text.to_string())
} else {
AppEvent::Text(text.to_string())
};
event.send(e)
}

fn read(input: Res<StdInput>, mut event: EventReader<AppEvent>) {
for event in event.iter() {
let text = match event {
AppEvent::Text(s) => format!("Text - {s}"),
AppEvent::Shout(s) => format!("Shout - {s}"),
};
println!("Event: {text}");
}
}

fn startup() {
println!("Press Enter to Progress, or type 'exit' to exit");
}

#[derive(Event, PartialEq, Eq, Clone, Debug, Hash, Serialize, Deserialize)]
pub enum AppEvent {
Text(String),
Shout(String),
}

impl ReplacableEvent for AppEvent {
fn get_type_name() -> &'static str {
"app-event"
}
}

#[dexterous_developer_setup]
pub fn reloadable(app: &mut ReloadableAppContents) {
app.add_event::<AppEvent>()
.add_systems(Startup, startup)
.add_systems(Update, (update, read).chain());
}

0 comments on commit 93b7f5e

Please sign in to comment.