Skip to content

Commit

Permalink
add control channel / scene fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kayhhh committed Oct 10, 2024
1 parent 3379f04 commit c846ab8
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 36 deletions.
2 changes: 1 addition & 1 deletion crates/unavi-scripting/src/api/wired/scene/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ pub fn try_create_primitive(
let material = primitive
.material
.as_ref()
.map(|m| m.read().handle.get().unwrap().clone())
.and_then(|m| m.read().handle.get().cloned())
.unwrap_or_else(|| default_material.clone());

let entity = world
Expand Down
32 changes: 24 additions & 8 deletions crates/unavi-scripting/src/api/wired/scene/nodes/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ use crate::{
scene::{composition::CompositionRes, document::DocumentRes, mesh::MeshRes},
},
},
data::{CommandSender, ScriptData},
data::{CommandSender, ControlSender, ScriptControl, ScriptData},
};

#[derive(Debug, Clone)]
pub struct NodeRes {
command_send: CommandSender,
control_send: ControlSender,
data: Arc<RwLock<NodeData>>,
}

Expand Down Expand Up @@ -83,14 +84,14 @@ impl NodeRes {
pub fn new(data: &mut ScriptData) -> Self {
let node = Self {
command_send: data.command_send.clone(),
control_send: data.control_send.clone(),
data: Arc::new(RwLock::new(NodeData::default())),
};

{
let node = node.clone();
data.command_send
.try_send(Box::new(move |world: &mut World| {
println!("new NodeRes command");
let node_data = node.write();
let entity = world.spawn(WiredNodeBundle::new(node_data.id.into())).id();
node_data.entity.set(entity).unwrap();
Expand Down Expand Up @@ -167,6 +168,10 @@ impl NodeRes {
Ok(res)
}
pub fn add_child(data: &mut ScriptData, parent: Self, child: Self) {
if parent.read().id == child.read().id {
return;
}

// Add to children.
parent.write().children.push(child.clone());

Expand All @@ -190,12 +195,18 @@ impl NodeRes {
}
pub fn remove_child(data: &mut ScriptData, parent: Self, child: Self) {
// Remove from children.
let parent_read = parent.read();
parent_read
let mut parent_write = parent.write();
let child_id = child.read().id;

let found = parent_write
.children
.iter()
.position(|n| n.read().id == parent_read.id)
.map(|index| parent.write().children.remove(index));
.position(|c| c.read().id == child_id)
.map(|index| parent_write.children.remove(index));

if found.is_none() {
return;
}

// Remove parent.
child.write().parent = None;
Expand Down Expand Up @@ -227,12 +238,17 @@ impl Drop for NodeRes {
if Arc::strong_count(&self.data) == 1 {
let data = self.data.clone();

self.command_send
if let Err(e) = self
.command_send
.try_send(Box::new(move |world: &mut World| {
let entity = *data.read().unwrap().entity.get().unwrap();
world.entity_mut(entity).despawn();
}))
.unwrap()
{
// Should only error when the entire script environment is being
// dropped, in which case we do not matter.
let _ = self.control_send.send(ScriptControl::Exit(e.to_string()));
}
}
}
}
16 changes: 8 additions & 8 deletions crates/unavi-scripting/src/api/wired/scene/primitive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,12 @@ impl HostPrimitive for ScriptData {
self_: Resource<PrimitiveRes>,
value: Vec<u32>,
) -> wasm_bridge::Result<()> {
let handle = self.table.get(&self_)?.read().handle.clone();
let data = self.table.get(&self_)?.clone();

self.command_send
.try_send(Box::new(move |world: &mut World| {
let mut assets = world.resource_mut::<Assets<Mesh>>();
let mesh = assets.get_mut(handle.get().unwrap()).unwrap();
let mesh = assets.get_mut(data.read().handle.get().unwrap()).unwrap();
mesh.insert_indices(Indices::U32(value));
}))
.unwrap();
Expand All @@ -154,12 +154,12 @@ impl HostPrimitive for ScriptData {
self_: Resource<PrimitiveRes>,
value: Vec<f32>,
) -> wasm_bridge::Result<()> {
let handle = self.table.get(&self_)?.read().handle.clone();
let data = self.table.get(&self_)?.clone();

self.command_send
.try_send(Box::new(move |world: &mut World| {
let mut assets = world.resource_mut::<Assets<Mesh>>();
let mesh = assets.get_mut(handle.get().unwrap()).unwrap();
let mesh = assets.get_mut(data.read().handle.get().unwrap()).unwrap();

let value = value.chunks(3).map(|x| [x[0], x[1], x[2]]).collect();

Expand All @@ -177,12 +177,12 @@ impl HostPrimitive for ScriptData {
self_: Resource<PrimitiveRes>,
value: Vec<f32>,
) -> wasm_bridge::Result<()> {
let handle = self.table.get(&self_)?.read().handle.clone();
let data = self.table.get(&self_)?.clone();

self.command_send
.try_send(Box::new(move |world: &mut World| {
let mut assets = world.resource_mut::<Assets<Mesh>>();
let mesh = assets.get_mut(handle.get().unwrap()).unwrap();
let mesh = assets.get_mut(data.read().handle.get().unwrap()).unwrap();

let value = value.chunks(3).map(|x| [x[0], x[1], x[2]]).collect();

Expand All @@ -200,12 +200,12 @@ impl HostPrimitive for ScriptData {
self_: Resource<PrimitiveRes>,
value: Vec<f32>,
) -> wasm_bridge::Result<()> {
let handle = self.table.get(&self_)?.read().handle.clone();
let data = self.table.get(&self_)?.clone();

self.command_send
.try_send(Box::new(move |world: &mut World| {
let mut assets = world.resource_mut::<Assets<Mesh>>();
let mesh = assets.get_mut(handle.get().unwrap()).unwrap();
let mesh = assets.get_mut(data.read().handle.get().unwrap()).unwrap();

if value.len() % 2 != 0 {
warn!("UVs do not have an even length! Got: {}", value.len());
Expand Down
24 changes: 13 additions & 11 deletions crates/unavi-scripting/src/data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::mpsc::{Receiver, SyncSender};
use std::sync::mpsc::{Receiver, Sender, SyncSender};

use bevy::prelude::*;
use wasm_bridge::component::{Resource, ResourceTable, ResourceTableError};
Expand All @@ -9,10 +9,19 @@ use crate::api::{wired::scene::nodes::base::NodeRes, ApiData};
pub type ScriptCommand = Box<dyn FnOnce(&mut World) + Send>;
pub type CommandSender = SyncSender<ScriptCommand>;

pub enum ScriptControl {
/// Unrecoverable error.
/// Stops script execution.
Exit(String),
}
pub type ControlSender = Sender<ScriptControl>;

pub struct ScriptData {
pub api: ApiData,
pub command_recv: Receiver<ScriptCommand>,
pub command_send: CommandSender,
pub control_recv: Receiver<ScriptControl>,
pub control_send: ControlSender,
pub table: ResourceTable,
pub wasi: WasiCtx,
pub wasi_table: wasm_bridge_wasi::ResourceTable,
Expand Down Expand Up @@ -40,11 +49,14 @@ impl Default for ScriptData {
//
// TODO: Test that script ECS data is indeed within the script scene
let (command_send, command_recv) = std::sync::mpsc::sync_channel(10_000);
let (control_send, control_recv) = std::sync::mpsc::channel();

Self {
api: ApiData::default(),
command_recv,
command_send,
control_recv,
control_send,
table: ResourceTable::default(),
wasi: WasiCtxBuilder::new().build(),
wasi_table: wasm_bridge_wasi::ResourceTable::default(),
Expand Down Expand Up @@ -97,13 +109,3 @@ impl ScriptData {
self.node_insert_option::<T>(node, None)
}
}

impl Drop for ScriptData {
fn drop(&mut self) {
// Command queue must be cleared before the reciever is dropped.
// Commands in the queue may hold references to resource data that will
// attempt to send cleanup commands on drop. If the reciever is not open
// this will cause a panic.
while self.command_recv.try_recv().is_ok() {}
}
}
20 changes: 15 additions & 5 deletions crates/unavi-scripting/src/execution.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bevy::{prelude::*, tasks::block_on};
use wasm_bridge::component::ResourceAny;

use crate::load::LoadedScript;
use crate::{data::ScriptControl, load::LoadedScript};

use super::load::ScriptMap;

Expand Down Expand Up @@ -60,7 +60,7 @@ pub fn update_scripts(

#[allow(clippy::await_holding_lock)]
let result: anyhow::Result<_> = block_on(async {
let span = trace_span!("ScriptUpdate", name = name.to_string(), tickrate.delta);
let span = trace_span!("ScriptUpdate", name = name.to_string());
let span = span.enter();

trace!("Updating script");
Expand All @@ -77,6 +77,15 @@ pub fn update_scripts(

script.store.data().push_commands(&mut commands);

while let Ok(control) = script.store.data().control_recv.try_recv() {
match control {
ScriptControl::Exit(e) => {
error!("Controlled exit: {}", e);
todo!("stop script execution");
}
}
}

trace!("Done");
drop(span);

Expand All @@ -93,17 +102,18 @@ pub fn update_scripts(
pub struct ScriptTickrate {
delta: f32,
last: f32,
/// Ticks per second.
tps: f32,
pub ready_for_update: bool,
tickrate: f32,
}

impl Default for ScriptTickrate {
fn default() -> Self {
Self {
delta: 0.0,
last: 0.0,
tps: 1.0 / 20.0,
ready_for_update: true,
tickrate: 1.0 / 20.0,
}
}
}
Expand All @@ -119,7 +129,7 @@ pub fn tick_scripts(time: Res<Time>, mut scripts: Query<&mut ScriptTickrate>) {

let delta = now - tickrate.last;

if delta > tickrate.tickrate {
if delta > tickrate.tps {
tickrate.delta = delta;
tickrate.last = now;
tickrate.ready_for_update = true;
Expand Down
1 change: 1 addition & 0 deletions crates/unavi-scripting/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl Plugin for ScriptingPlugin {
),
execution::init_scripts,
execution::update_scripts,
apply_deferred,
)
.chain(),
);
Expand Down
9 changes: 6 additions & 3 deletions crates/unavi-scripting/tests/scripts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ use tracing_test::traced_test;
use unavi_scripting::{load::LoadedScript, ScriptBundle, ScriptingPlugin};
use unavi_world::util::init_user_actor;

const NUM_UPDATES: usize = 4;

pub async fn test_script(name: &str) {
let mut app = App::new();
init_user_actor(app.world_mut()).await;
Expand Down Expand Up @@ -52,10 +54,10 @@ pub async fn test_script(name: &str) {

assert!(did_load);

for i in 0..4 {
for i in 0..NUM_UPDATES {
debug!("Updating... ({})", i);
tokio::time::sleep(Duration::from_secs_f32(0.1)).await;
app.update();
app.world_mut().run_schedule(FixedUpdate);
}
}

Expand Down Expand Up @@ -88,6 +90,7 @@ async fn test_wired_scene() {
assert!(!logs_contain("WARN"));
assert!(!logs_contain("warn"));

// TODO: Move this to an independent test
logs_assert(|logs| {
let mut found_constructs = 0;
let mut found_updates = 0;
Expand All @@ -103,7 +106,7 @@ async fn test_wired_scene() {
}

assert_eq!(found_constructs, 1);
assert_eq!(found_updates, 1);
assert_eq!(found_updates, NUM_UPDATES);

Ok(())
});
Expand Down

0 comments on commit c846ab8

Please sign in to comment.