Skip to content

Commit

Permalink
integrate the worker messaging layer more
Browse files Browse the repository at this point in the history
  • Loading branch information
snendev committed Aug 6, 2024
1 parent cb2fc0b commit 93c1301
Show file tree
Hide file tree
Showing 12 changed files with 219 additions and 48 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = ["apps/*", "plugins/*"]

[workspace.dependencies]
active_game = { path = "plugins/active_game" }
game = { path = "plugins/game" }
server = { path = "plugins/server" }
ui = { path = "plugins/ui" }
Expand Down Expand Up @@ -33,7 +34,7 @@ codegen-units = 1
lto = "thin"
# Optimize with size in mind (also try "z", sometimes it is better).
# Slightly slows compile times, great improvements to file size and runtime performance.
opt-level = "s"
opt-level = "z"
# Strip all debugging information from the binary to slightly reduce file size.
strip = "debuginfo"

Expand All @@ -54,6 +55,7 @@ ui = ["dep:ui"]

[dependencies]
# plugins
active_game = { workspace = true }
game = { workspace = true }
server = { workspace = true, optional = true }
ui = { workspace = true, optional = true }
Expand Down
2 changes: 1 addition & 1 deletion apps/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<link data-trunk rel="copy-dir" href="../../assets" />
<link data-trunk rel="inline" href="./style.css" />
<link data-trunk rel="rust" href="Cargo.toml" data-bin="app" data-type="main" data-wasm-opt="s" />
<link data-trunk rel="rust" href="Cargo.toml" data-bin="worker" data-type="worker" data-wasm-opt="s" />
<link data-trunk rel="rust" href="Cargo.toml" data-bin="worker" data-type="worker" data-wasm-opt="z" />
</head>

<body>
Expand Down
35 changes: 29 additions & 6 deletions apps/web/src/bin/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,39 @@ extern "C" {
fn main() {
console_error_panic_hook::set_once();

let (left_word, set_left_word) = create_signal("".to_string());
let (right_word, set_right_word) = create_signal("".to_string());
let (arena_size, set_arena_size) = create_signal(0);

let bridge = BevyWorker::spawner()
.callback(move |message| {
log(format!("{:?}", message));
log(format!("Update from Bevy app: {:?}", message));
match message {
wordfight_web::WorkerMessage::UpdateState(state) => {
set_left_word.set(state.left_word);
set_right_word.set(state.right_word);
set_arena_size.set(state.arena_size);
}
}
})
.spawn("./worker.js");
let bridge = Box::leak(Box::new(bridge));

mount_to_body(|| {
mount_to_body(move || {
view! {
<main>
<Game handle_input=move |event: KeyboardEvent| {
<Game
left_word=left_word.get()
right_word=right_word.get()
arena_size=arena_size.get()
handle_input=move |event: KeyboardEvent| {
// event_target_value is a Leptos helper function
// it functions the same way as event.target.value
// in JavaScript, but smooths out some of the typecasting
// necessary to make this work in Rust
log(format!("{}", event.key().as_str()));
if let Some(message) = match event.key().as_str() {
"Backspace" | "Delete" | "ArrowLeft" => Some(AppMessage::backspace()),
"Backspace" | "Delete" | "ArrowLeft" => Some(AppMessage::Backspace),
letter => AppMessage::add_letter(letter),
} {
bridge.send(message);
Expand All @@ -43,9 +58,17 @@ fn main() {
}

#[component]
fn Game(handle_input: impl Fn(KeyboardEvent) + 'static) -> impl IntoView {
fn Game(
left_word: String,
right_word: String,
arena_size: usize,
handle_input: impl Fn(KeyboardEvent) + 'static,
) -> impl IntoView {
log(left_word);
log(right_word);
log(format!("{arena_size}"));
view! {
<div tabindex="1" on:keyup=handle_input>
<div class="center" tabindex="1" on:keyup=handle_input>
<p>Hello world!</p>
</div>
}
Expand Down
24 changes: 6 additions & 18 deletions apps/web/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,16 @@ pub use worker::*;
#[derive(Debug)]
#[derive(Deserialize, Serialize)]
pub enum AppMessage {
AddLetter(AddLetterMessage),
Backspace(BackspaceMessage),
AddLetter(Letter),
Backspace,
}

impl AppMessage {
pub fn add_letter(letter: &str) -> Option<Self> {
Letter::from_str(letter).map(|letter| Self::AddLetter(AddLetterMessage(letter)))
}

pub fn backspace() -> Self {
Self::Backspace(BackspaceMessage)
Letter::from_str(letter).map(|letter| Self::AddLetter(letter))
}
}

#[derive(Debug)]
#[derive(Deserialize, Serialize)]
pub struct AddLetterMessage(Letter);

#[derive(Debug)]
#[derive(Deserialize, Serialize)]
pub struct BackspaceMessage;

#[derive(Debug)]
#[derive(Deserialize, Serialize)]
pub enum WorkerMessage {
Expand All @@ -39,7 +27,7 @@ pub enum WorkerMessage {
#[derive(Debug)]
#[derive(Deserialize, Serialize)]
pub struct UpdateStateMessage {
left_word: String,
right_word: String,
arena_size: usize,
pub left_word: String,
pub right_word: String,
pub arena_size: usize,
}
52 changes: 40 additions & 12 deletions apps/web/src/worker.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use gloo_worker::{HandlerId, Worker, WorkerScope};
use wasm_bindgen::prelude::*;

use bevy::prelude::*;
use bevy::{prelude::*, utils::HashSet};

use wordfight::WordFightPlugins;
use wordfight::{ActiveGameUpdate, WordFightPlugins};

use crate::{AppMessage, UpdateStateMessage, WorkerMessage};

Expand All @@ -22,6 +22,8 @@ extern "C" {

pub struct BevyWorker {
app: App,
my_player: Entity,
subscriptions: HashSet<HandlerId>,
_trigger_update: Closure<dyn FnMut()>,
_interval: Interval,
}
Expand All @@ -34,33 +36,59 @@ impl Worker for BevyWorker {
fn create(scope: &WorkerScope<Self>) -> Self {
let mut app = App::new();
app.add_plugins(WordFightPlugins);
app.update();
app.update();

let events = app.world().resource::<Events<ActiveGameUpdate>>();
let mut reader = events.get_reader();
let update = reader.read(&events).last().unwrap();
let my_player = update.player_left;

let scope_clone = scope.clone();
let trigger_update = Closure::new(move || {
scope_clone.send_message(());
});
let interval = setInterval(&trigger_update, 10);
Self {
app,
my_player,
subscriptions: HashSet::default(),
_trigger_update: trigger_update,
_interval: Interval(interval),
}
}

fn update(&mut self, _: &WorkerScope<Self>, _: Self::Message) {
fn update(&mut self, scope: &WorkerScope<Self>, _: Self::Message) {
log("Update".to_string());
self.app.update();
let events = self.app.world().resource::<Events<ActiveGameUpdate>>();
let mut reader = events.get_reader();
if let Some(update) = reader.read(&events).last() {
for id in &self.subscriptions {
scope.respond(
*id,
WorkerMessage::UpdateState(UpdateStateMessage {
left_word: update.left_word.to_string(),
right_word: update.left_word.to_string(),
arena_size: update.arena_size,
}),
);
}
}
}

fn received(&mut self, scope: &WorkerScope<Self>, message: Self::Input, id: HandlerId) {
fn received(&mut self, _: &WorkerScope<Self>, message: Self::Input, id: HandlerId) {
log(format!("Message received! {:?}", message));
scope.respond(
id,
WorkerMessage::UpdateState(UpdateStateMessage {
left_word: "".to_string(),
right_word: "".to_string(),
arena_size: 7,
}),
);
self.subscriptions.insert(id);
let action: wordfight::Action = match message {
AppMessage::AddLetter(letter) => wordfight::Action::Append(letter),
AppMessage::Backspace => wordfight::Action::Delete,
};
self.app
.world_mut()
.send_event(action.made_by(self.my_player, wordfight::PlayerSide::Left));

self.app.update();
}
}

Expand Down
3 changes: 2 additions & 1 deletion apps/web/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
}

html,
body {
body,
main {
width: 100%;
height: 100%;
}
Expand Down
9 changes: 9 additions & 0 deletions plugins/active_game/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "active_game"
version = "0.1.0"
edition = "2021"
authors = ["Sean Sullivan <[email protected]>"]

[dependencies]
game = { workspace = true }
bevy = { workspace = true, default-features = false }
84 changes: 84 additions & 0 deletions plugins/active_game/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use bevy::prelude::*;

use game::{Arena, Game, GamePlayers, Word};

pub struct ActiveGamePlugin;

impl Plugin for ActiveGamePlugin {
fn build(&self, app: &mut App) {
app.add_event::<ActiveGameUpdate>()
.add_systems(
Update,
Self::set_active_game.run_if(not(resource_exists::<ActiveGame>)),
)
.add_systems(
Update,
Self::trigger_game_update.run_if(resource_exists::<ActiveGame>),
);
}
}

impl ActiveGamePlugin {
fn set_active_game(mut commands: Commands, spawned_games: Query<Entity, Added<Game>>) {
for game in spawned_games.iter() {
commands.insert_resource(ActiveGame(game));
}
}

fn trigger_game_update(
mut commands: Commands,
mut events: EventWriter<ActiveGameUpdate>,
active_game: Res<ActiveGame>,
games: Query<(&GamePlayers, &Arena)>,
words: Query<&Word>,
updated_words: Query<(), Changed<Word>>,
) {
let Some((game, players, arena)) = games
.get(active_game.0)
.ok()
.map(|(players, arena)| (active_game, players, arena))
.filter(|(game, players, _)| {
game.is_changed()
|| updated_words.contains(players.left)
|| updated_words.contains(players.right)
})
else {
return;
};

let left_word = words
.get(players.left)
.cloned()
.expect("PlayerSide::Left to have a Word");
let right_word = words
.get(players.right)
.cloned()
.expect("PlayerSide::Right should have a Word");
let event = ActiveGameUpdate {
game: game.0,
arena_size: arena.size(),
player_left: players.left,
left_word,
player_right: players.right,
right_word,
};
commands.trigger(event.clone());
events.send(event);
}
}

#[derive(Debug)]
#[derive(Deref, DerefMut, Resource, Reflect)]
pub struct ActiveGame(Entity);

// Can be read from an EventReader or observed as needed
#[derive(Clone, Debug)]
#[derive(Event)]
pub struct ActiveGameUpdate {
pub game: Entity,
pub arena_size: usize,
pub player_left: Entity,
pub left_word: Word,
pub player_right: Entity,
pub right_word: Word,
}
14 changes: 14 additions & 0 deletions plugins/game/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ pub struct Score(usize);
#[derive(Deserialize, Serialize)]
pub struct Word(Vec<Letter>);

impl std::fmt::Display for Word {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
self.0
.iter()
.map(|letter| letter.to_string())
.collect::<Vec<_>>()
.join("")
)
}
}

#[derive(Bundle)]
pub struct PlayerBundle {
pub client: Client,
Expand Down
Loading

0 comments on commit 93c1301

Please sign in to comment.