Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Cargo.lock

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

14 changes: 14 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ bevy_simple_subsecond_system_macros = { path = "macros", version = "0.1.9" }
crossbeam-channel = "0.5.15"
send_wrapper = "0.6.0"

[target.'cfg(target_arch = "wasm32")'.dependencies]
dioxus-cli-config = { version = "0.7.0-alpha.1", features = ["web"] }
serde_json = { version = "1.0" }

[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys]
version = "0.3.77"
features = [
"Window",
"Location",
"CloseEvent",
"MessageEvent",
"WebSocket"
]

[dev-dependencies]
bevy = { version = "0.16.0", features = ["bevy_ui_debug", "track_location"] }

Expand Down
20 changes: 5 additions & 15 deletions macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,14 @@ pub fn hot(attr: TokenStream, item: TokenStream) -> TokenStream {

if !hot_patch_signature && !rerun_on_hot_patch {
let result = quote! {
#[cfg(any(target_family = "wasm", not(debug_assertions)))]
#vis fn #original_fn_name #impl_generics(#inputs) #where_clause #original_output {
#block
}


#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
#[allow(unused_mut)]
#vis fn #original_fn_name #impl_generics(#inputs) #where_clause #original_output {
#hot_fn.call((#(#param_idents,)*))
}


#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
#vis fn #hotpatched_fn #impl_generics(#inputs) #where_clause #original_output {
#block
}
Expand Down Expand Up @@ -215,12 +209,8 @@ pub fn hot(attr: TokenStream, item: TokenStream) -> TokenStream {
};

let result = quote! {
#[cfg(any(target_family = "wasm", not(debug_assertions)))]
#vis fn #original_fn_name #impl_generics(#inputs) #where_clause #original_output {
#block
}
// Outer entry point: stable ABI, hot-reload safe
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
#vis fn #original_fn_name #impl_generics(world: &mut ::bevy_simple_subsecond_system::__macros_internal::World) #where_clause #original_output {
use std::any::Any as _;
let type_id = #hotpatched_fn #maybe_generics.type_id();
Expand Down Expand Up @@ -258,11 +248,11 @@ pub fn hot(attr: TokenStream, item: TokenStream) -> TokenStream {
}

// Hotpatched version with stable signature
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
#hotpatched_fn_definition

// Original function body moved into a standalone fn
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
#vis fn #original_wrapper_fn #impl_generics(#inputs) #where_clause #original_output {
#block
}
Expand Down
2 changes: 1 addition & 1 deletion src/hot_patched_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use bevy_app::{
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::prelude::*;
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
use bevy_ecs::system::{Commands, Res};
use bevy_ecs_macros::ScheduleLabel;
use bevy_log::{debug, error};
Expand Down
76 changes: 66 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@

pub mod migration;

#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
use __macros_internal::__HotPatchedSystems as HotPatchedSystems;
use bevy_app::{App, Last, Plugin, PostStartup, PreUpdate};
use bevy_ecs::prelude::*;
pub use bevy_simple_subsecond_system_macros::*;
pub use dioxus_devtools;
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
use dioxus_devtools::{subsecond::apply_patch, *};

#[cfg(target_arch = "wasm32")]
use web_sys::{
CloseEvent, MessageEvent, WebSocket,
js_sys::JsString,
wasm_bindgen::{JsCast, JsValue, closure::Closure},
window,
};

pub mod hot_patched_app;

/// Everything you need to use hotpatching
Expand Down Expand Up @@ -51,20 +59,15 @@ impl Plugin for SimpleSubsecondPlugin {
)
.chain(),
);
#[cfg(target_family = "wasm")]
{
bevy_log::warn!(
"Hotpatching is not supported on Wasm yet. Disabling SimpleSubsecondPlugin."
);
return;
}
#[cfg(not(debug_assertions))]
{
return;
}
#[cfg(all(not(target_family = "wasm"), debug_assertions))]
#[cfg(debug_assertions)]
{
let (sender, receiver) = crossbeam_channel::bounded::<HotPatched>(1);

#[cfg(not(target_arch = "wasm32"))]
connect(move |msg| {
if let DevserverMsg::HotReload(hot_reload_msg) = msg {
if let Some(jumptable) = hot_reload_msg.jump_table {
Expand All @@ -76,6 +79,59 @@ impl Plugin for SimpleSubsecondPlugin {
}
});

#[cfg(target_arch = "wasm32")]
{
let location = web_sys::window().unwrap().location();
let url = format!(
"{protocol}//{host}/_dioxus?build_id={build_id}",
protocol = match location.protocol().unwrap() {
prot if prot == "https:" => "wss:",
_ => "ws:",
},
host = location.host().unwrap(),
build_id = dioxus_cli_config::build_id(),
);

let ws = WebSocket::new(&url).unwrap();

ws.set_onmessage(Some(
Closure::<dyn FnMut(MessageEvent)>::new(move |e: MessageEvent| {
let Ok(text) = e.data().dyn_into::<JsString>() else {
return;
};

// The devserver messages have some &'static strs in them, so we need to leak the source string
let string: String = text.into();
let string = Box::leak(string.into_boxed_str());

match serde_json::from_str::<DevserverMsg>(string) {
Ok(DevserverMsg::HotReload(hr)) => {
if let Some(jumptable) = hr.jump_table {
// SAFETY: This is not unsafe, but anything using the updated jump table is.
// The table must be built carefully
unsafe { apply_patch(jumptable).unwrap() };
sender.send(HotPatched).unwrap();
}
}
Ok(_) => {
// Other devserver messages not used
}
Err(e) => web_sys::console::error_1(
&format!("Error parsing devserver message: {}", e).into(),
),
e => {
web_sys::console::error_1(
&format!("Error parsing devserver message: {:?}", e).into(),
);
}
}
})
.into_js_value()
.as_ref()
.unchecked_ref(),
));
}

app.init_resource::<HotPatchedSystems>();

app.add_event::<HotPatched>().add_systems(
Expand Down
Loading