wasvy-org/wasvy

Determine system params automatically

Open

#59 opened on Apr 18, 2026

View on GitHub
 (0 comments) (0 reactions) (0 assignees)Rust (6 forks)auto 404
enhancementgood first issuehelp wanted

Repository metrics

Stars
 (126 stars)
PR merge metrics
 (PR metrics pending)

Description

Currently all system params are declared in the setup. For example, once we implement https://github.com/wasvy-org/wasvy/issues/57 and https://github.com/wasvy-org/wasvy/issues/54, a user might write the following rust mod:

fn setup(app: App) {
    let system = System::new("system");
    system.add_commands();
    system.add_single("MyResource");
    app.add_systems(&Schedule::ModStartup, vec![system]);
}

fn system(commands: Commands, component: Single) {
    let my_resource = MyResource::new(component); // Generated by wasvy authoring
    ...
}

This has several problems.

It should be possible to write:

fn setup(app: App) {
    let system = System::new("system");
    app.add_systems(&Schedule::ModStartup, vec![system]);
}

// `Commands` resource is a know system param, so wasvy can resolve it to `Param::Commands`
// `MyResource` resource is a known component reflected by authoring, so wasvy knows its type path and can resolve it to `Param::Single(TypePath)`
fn system(commands: Commands, my_resource: MyResource) {
    ...
}

The modder would still be expected to provide param information for situations where params cannot be resolved.

fn setup(app: App) {
    let system = System::new("system");
    // TBD, api could look something like:
    system.param(0, Param::Query(vec![QueryFor::Mut("Transform"), QueryFor::With("Camera")]);
    system.param(2, Param::Single("MyResource"));
    app.add_systems(&Schedule::ModStartup, vec![system]);
}

fn system(camera_transform: Query, commands: Commands, component: Single) {
    ...
}

To make this work, after a mod constructs a new system resource (System::new("system")) wasvy must introspect the system export of the wasm binary before scheduling the system. This can be done with some code like like this:

let instance = todo!("Get the wasm instance somehow");
let system_name = system.name; // ie: "system" in our examples above
let system = instance
    .get_func(&mut store, system_name)
    .ok_or_else(|| anyhow!("export {system_name} not found"))?;

let user_params: &Vec<Option<Param>> = system.params; // params provided by the user in setup, some might be missing
let mut params: Vec<Param> = Vec::new();
let mut errors: Vec<Anyhow> = Vec::new();
for (index, (name, ty)) in system.ty(&store).params().enumerate() {
    let user_param = user_params.get(index).clone();
    let detected_param = match ty {
        Type::Own(ty) | Type::Borrow(ty) => {
            if is_commands(ty) {
                Some(Param::Commands)
            } else if is_query(ty) {
                // Unknown QueryFor
                None
            } else if is_any_component(ty) {
                // A Component could have any type path, so we can't resolver it automatically
                None
            } else if let Some(type_name) = reflected_component(ty) {
                Some(Param::ReflectedSingle(type_name)) // Produces the correct resource, not a generic `component` 
            } else {
                errors.push(anyhow!("System {system_name} param {index} \"{name}\" is unknown resource type {ty:?}"));
                continue;
            }
        }
        _ => {
           errors.push(anyhow!("System {system_name} param {index} \"{name}\" is unexpected type {ty:?}"));
           continue;
        }
    };

    let param = user_param.or(detected_param);
    if let Some(param) = user_param.or(detected_param) {
        params.push(param);
    } else {
        errors.push(anyhow!("System {system_name} could not resolve param {index}. Please set one via System::param."));
    }
}

// Instead of exiting early, we collect all errors and log these to the user
if !errors.is_empty() {
    bail!(SystemErrors(errors));
}

The code above is just to get started. Ideally we'd return all errors from all systems instead of bailing at the first one.

Relevant place in the codebase:

https://github.com/wasvy-org/wasvy/blob/0da11cba82c5837d18431974f2e86a95e44966ca/src/system.rs#L88-L117

Contributor guide