bevy_mod_scripting icon indicating copy to clipboard operation
bevy_mod_scripting copied to clipboard

Improved proxy generating macros

Open makspll opened this issue 2 years ago • 7 comments

Current macros are very ugly, written before I had a full grasp of the capabilities of rust meta-programming,

I intend to make things like these possible:

#[derive(ScriptProxy, Reflect)]
#[proxy(languages("on_feature(lua)"))]
#[functions[

    #[lua(MutatingMetaMethod)]
    fn basd(mut self) {
            
    }

    #[lua(MetaMethod)]
    fn asd(self);

    #[lua(Function)]
    fn basda(self);
]]
pub struct Lol {}

makspll avatar May 13 '23 12:05 makspll

I can take a peek at this in a few days!

thedanvail avatar May 13 '23 19:05 thedanvail

Sure thing!

I've been working on the Lua version of this over at this branch: https://github.com/makspll/bevy_mod_scripting/tree/feature/derive_macros

I've got a basic macro going there. Similar work needs to be done for Rhai and can this easily be done in parallel if you're interested! Rhai however does not yet have existing proxy macros, but the crate itself has a lot of cool features which would make it interact nicely with a macro structure like the one above (and you'd probably want to bump its version).

Generally the vague steps would be:

  • [ ] Set up macro structure akin to how it's done on the branch above (the main crate uses a derive macro which then calls a proc macro for each language crate (since proc macros are more flexible)), output no code.
  • [ ] Let the macro generate the proxy definition (this is done via a helper macro bevy_script_api::make_script_wrapper!), think of these as a container on the script side which can either be a reference to a bevy thing (via their reflection mechanism) or an actual value of the type stored "script-side".
  • [ ] Once the macro generates a proxy now it needs to implement some traits on the PROXIED TYPE, the core one is RhaiProxyable, have a look at bevy_script_api/src/rhai/mod.rs where important traits are defined. The most important mechanism you need to be aware of is how our type gets resolved to its proxy on the script side, and this happens here:
   impl ToDynamic for ScriptRef {
   fn to_dynamic(self) -> Result<Dynamic, Box<EvalAltResult>> {
       // clone since it's cheap and we don't want to clone self later
       let world = self.world_ptr.clone();
       let world = world.read();

       let type_data = world.resource::<AppTypeRegistry>();
       let g = type_data.read();

       let type_id = self.get(|s| s.type_id())?;

       if let Some(v) = g.get_type_data::<ReflectRhaiProxyable>(type_id) { // if it implements ReflectRhaiProxyable, then the trait is used to generate a proxy and this gets used on the script side when interacting with the world
           v.ref_to_rhai(self)
       } else {
           // otherwise we just use a generic type which handles very simple operations but nothing more
           ReflectedValue { ref_: self }.to_dynamic()
       }
   }
}
  • [ ] if you register this type with the app using bevy_script_api::register_foreign_rhai_type, you will now be able to use ScriptWorld in a rhai script to retrieve your proxy in a script via (have a look at the bevy_api_rhai.rs example for how to setup a world to test this) :
    let my_component_type = world.get_type_by_name("MyComponent");
    let my_component = world.get_component(entity,my_component_type);
  • [ ] now what's left is actually automatically proxying functions ,fields etc, this is the hard bit. Like with the Lua macros, for this you'd look at the types in bevy_script_api/src/rhai/bevy/mod.rs which defines some script-side api types and for each function, field, operator etc, you'd want to use the proxy functions available to you to convert invocations of these functions on the script side, so that the arguments which correspond to other proxies generated with the same macro, are resolved to their "proxied" types, the operation is then performed over these "inner" types, and the result is converted to its proxy via the traits you implemented earlier and returned.

Doing any of the above would be helpful!

makspll avatar May 14 '23 08:05 makspll

Alright, will be starting to look at this tomorrow!

thedanvail avatar May 19 '23 00:05 thedanvail

Hey there - I'm finally getting around to this, and I'm currently spending my time trying to better understand the structure of the Lua interops so I can port most of them to Rhai, but progress has been started and will be built upon. It may take me a bit, as I've been laid off as of a few days ago and need to devote my time to finding a way to pay rent, but my down time will be tackling this issue. Just wanted to communicate that!

thedanvail avatar May 21 '23 02:05 thedanvail

Hey man, no worries. That's very rough and I hope you find a new job soon! Any help is much appreciated, but of course there is no rush!

makspll avatar May 21 '23 14:05 makspll

@makspll I've opened a draft PR which is very much a work in progress, but I wanted to open it so you could see the work that was being done as my understanding/knowledge on the project progresses and make any suggestions/comments as you see fit.

thedanvail avatar May 21 '23 23:05 thedanvail

@thedanvail of course I will have a look now and keep a regular watch in parallel to developing the Lua branch.

makspll avatar May 24 '23 17:05 makspll