-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow Users to Create Custom Ops #18
Comments
Sounds good, it seems useful to make this extensible. The biggest question is probably what kinds of parameters and return types to allow. |
My current strategy has been to take make only one native Deno/Wasm Bindgen op, that has a signature in pseudo code like: #[op]
fn op_bevy_mod_js_scripting(
op_name: String,
args: serde_json::Value
) -> Result<serde_json::Value, AnyError> {
// Here we check on the op-name and run the core/user op registered with that name.
} That makes it super easy to register custom ops with our ops abstraction without having to generate new And, yes, for Haven't gotten far yet, but the concept seems to be sound so far. Also, as a simple optimization I'm going to try to reduce the |
Just got the log operation implemented through the new abstraction. I think the API's turning out pretty nice, and it should allow us to implement runtime agnostic ops: struct OpLog;
#[derive(Deserialize)]
struct OpLogArgs {
level: u8,
args: Vec<serde_json::Value>,
}
impl JsRuntimeOp for OpLog {
fn run(
&self,
script_info: &ScriptInfo,
_world: &mut World,
args: serde_json::Value,
) -> anyhow::Result<serde_json::Value> {
let args: OpLogArgs = serde_json::from_value(args).context("Parse `log` op args")?;
let level = args.level;
let text = args
.args
.iter()
.map(|arg| {
// Print string args without quotes
if let serde_json::Value::String(s) = &arg {
s.clone()
// Format other values as JSON
} else {
format!("{arg}")
}
})
.collect::<Vec<_>>()
.join(" ");
if level == 0 {
let _span = span!(Level::TRACE, "script", path = ?script_info.path).entered();
event!(target: "js_runtime", Level::TRACE, "{text}");
} else if level == 1 {
let _span = span!(Level::DEBUG, "script", path = ?script_info.path).entered();
event!(target: "js_runtime", Level::DEBUG, "{text}");
} else if level == 2 {
let _span = span!(Level::INFO, "script", path = ?script_info.path).entered();
event!(target: "js_runtime", Level::INFO, "{text}");
} else if level == 3 {
let _span = span!(Level::WARN, "script", path = ?script_info.path).entered();
event!(target: "js_runtime", Level::WARN, "{text}");
} else if level == 4 {
let _span = span!(Level::ERROR, "script", path = ?script_info.path).entered();
event!(target: "js_runtime", Level::ERROR, "{text}");
} else {
anyhow::bail!("Invalid log level");
}
Ok(serde_json::Value::Null)
}
fn js(&self) -> Option<&'static str> {
Some(
r#"
globalThis.trace = (...args) => bevyModJsScriptingOpSync("log", 0, args);
globalThis.debug = (...args) => bevyModJsScriptingOpSync("log", 1, args);
globalThis.info = (...args) => bevyModJsScriptingOpSync("log", 2, args);
globalThis.warn = (...args) => bevyModJsScriptingOpSync("log", 3, args);
globalThis.error = (...args) => bevyModJsScriptingOpSync("log", 4, args);
"#,
)
}
} |
Got native runtime fully migrated to the new op abstraction and away from raw pointers in draft PR ( #19 ). Now we can actually I've still got to migrate WASM to the new system, which shouldn't be difficult because all the ops are now runtime agnostic and implemented in one place only. First, though, I'm going to see about solving #16 real quick to make sure that will work well with the op abstraction and that it can stay runtime agnostic. |
I'm realizing as I try to integrate this with my game that it could be important to make it as easy as possible for users to create their own custom ops.
Right now we have some holes in the functionality that should be built-in eventually, such as being able to interact with reflected
Vec
s andHashMap
s or being able toworld.spawn()
, but I also was thinking about the fact that I could definitely have some kind of functionality in my game that I wanted to expose more directly to my scripts, especially with reflected functions being work-in-progress.To this end, I think I'm going to experiment a little with a way to create an abstraction for creating the ops, and at the same time try to reduce the code duplication between the native and WASM runtimes by using that ops abstraction for all the core ops we currently have.
The text was updated successfully, but these errors were encountered: