Skip to content
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

Projects using Rhai #69

Open
stevedonovan opened this issue Apr 7, 2018 · 30 comments
Open

Projects using Rhai #69

stevedonovan opened this issue Apr 7, 2018 · 30 comments

Comments

@stevedonovan
Copy link
Contributor

Just a tracking issue for projects.

Apparently I've got the first published crate that uses Rhai as a dependency:

https://github.com/stevedonovan/findr

@luciusmagn
Copy link
Collaborator

Awesome! I like the idea of a find that doesn't have an arcane obscure syntax. Yep, you are the first person to actually have a crate using it published. Good job :D

@zesterer
Copy link

zesterer commented Jul 6, 2018

@luciusmagn We're considering using Rhai for a game project (https://www.github.com/veloren/game/). The team has a few questions about the language first though. Where is best to ask them?

@luciusmagn
Copy link
Collaborator

luciusmagn commented Jul 6, 2018 via email

@GopherJ
Copy link
Contributor

GopherJ commented Apr 9, 2020

casbin-rs is also using rhai!

@MarkuBu
Copy link

MarkuBu commented Apr 17, 2020

I consider using Rhai for a game too. But I'm in the planning process and far away from any code

Another option for scripting would be Mun, but this is also in an early state

@schungx
Copy link
Collaborator

schungx commented Apr 17, 2020

@MarkuBu my $0.02 worth - use Rhai if you want tight integration with lots of functionalities written in Rust. If you want to basically write your game logic in a scripting language, probably Lua is much more suited for this purpose.

@MarkuBu
Copy link

MarkuBu commented Apr 17, 2020

@schungx why? Because of the performance? I know that Lua is fast, even without the JIT

@schungx
Copy link
Collaborator

schungx commented Apr 17, 2020

Yes. Rhai is an AST-walking interpreter. It is never going to break any speed records. So use it where the majority of your application's time is not spend in the script.

@MarkuBu
Copy link

MarkuBu commented Apr 17, 2020

Ok, thanks.

@sunng87
Copy link

sunng87 commented Apr 22, 2020

I'm looking for a scripting language to add dynamic helper support for my template engine handlebars-rust. By far Rhai seems to be a good fit because of its inter-operatability with rust data. I will be writing poc code recently and seeking for help with you.

@schungx
Copy link
Collaborator

schungx commented Apr 22, 2020

No prob!

@sunng87
Copy link

sunng87 commented Jun 3, 2020

FYI support for rhai script helper has been released in handlebars 3.1.0 with a feature flag script_helper https://github.com/sunng87/handlebars-rust/blob/master/examples/script.rs

@Pebaz
Copy link

Pebaz commented Jan 14, 2021

I'm trying to integrate Rhai into my game but for the life of me I can't figure out how to pass a Vec of a custom Rust type (already registered with the engine) and access the indices from it.

I can successfully pass it but when the script tries to access any index it crashes.

@schungx
Copy link
Collaborator

schungx commented Jan 15, 2021

When you pass a Vec into Rhai, it treats it as a custom type. You're responsible for registering an API for use with that API. In your case, you'd want to register an indexer.

engine
    .register_type_with_name::<Vec<MyType>>("Vec<MyType>")
    .register_indexer_get_set(
        |v: &mut Vec<MyType>, i: INT| v[i],
        |v: &mut Vec<MyType>, i: INT, value: MyType| v[i] = value)
    .register_fn("len", |v: &mut Vec<MyType>| v.len() as INT)
    .register_get("len", |v: &mut Vec<MyType>| v.len() as INT)
    .register_fn("contains", |v: &mut Vec<MyType>, value: MyType| v.contains(&value))
    .register_fn("is_empty", |v: &mut Vec<MyType>| v.is_empty())
    .register_get("is_empty", |v: &mut Vec<MyType>| v.is_empty());

After a while it gets clumsy. You'd want to use a plugin module to define the API instead.

@schungx
Copy link
Collaborator

schungx commented Jan 15, 2021

Alternatively, you can convert the Vec<T> into a Rhai Array and it'll work with Rhai just fine.

// assuming 'my_vec' is 'Vec<T>'
let array: Dynamic = my_vec.into();

The catch is that a Rhai Array is dynamically typed, meaning that you cannot restrict it to hold only MyType.

@Pebaz
Copy link

Pebaz commented Jan 15, 2021

@schungx Thank you very much for your help this is what I needed!

@koute
Copy link

koute commented Aug 17, 2021

I'm using Rhai as a DSL for my memory profiler.

@Kezii Kezii mentioned this issue Sep 23, 2021
@garypen
Copy link
Contributor

garypen commented May 12, 2022

We are using rhai in our new GraphQL router project: https://github.com/apollographql/router

The router has a "plugin" framework which allows customer to write plugins in rust which may inspect or modify the flow of data through the router. We have a rhai plugin, which exposes router functionality so that plugins can also be written in rhai. This is nice for our customers, since it's much simpler to write a rhai plugin than a rust plugin.

Support is deemed to be experimental at the moment, but our experience so far is good!

@schungx
Copy link
Collaborator

schungx commented May 12, 2022

This is interesting. Is your payload mostly binary-encoded or text-encoded (like JSON)?

Are you manipulating low-level transport streams with Rhai or pre-filled data objects?

BTW, it is usually simpler for the user to provide an overloaded version of a function that takes a string (as function name) in addition to one that takes an FnPtr. This way, the user can skip the Fn creation step. The implementation is usually simply to have the string version construct an FnPtr and then call the FnPtr version.

    let request_callback = Fn("process_request");
    service.map_request(request_callback);
    let response_callback = Fn("process_response");
    service.map_response(response_callback);

    // can also call like this
    service.map_request("process_request");
    service.map_response("process_response");

    // and the FnPtr versions will support this
    service.map_request(|req| { ... });
    service.map_response(|resp| { ... });

This is similar to early JavaScript style.

@garypen
Copy link
Contributor

garypen commented May 12, 2022

It's text-encoded (JSON), so it's mainly about manipulating pre-filled data objects.

Your comment made me chuckle because I originally (before I found out about Fn()) was just calling functions by string representation. I can't make my mind up if that looks better or not, but I'll try it out in a few examples so people are aware it exists.

We are using closures in one of the examples in a soon to land PR...

@schungx
Copy link
Collaborator

schungx commented May 12, 2022

Your comment made me chuckle because I originally (before I found out about Fn()) was just calling functions by string representation. I can't make my mind up if that looks better or not, but I'll try it out in a few examples so people are aware it exists.

Why not both? I find Rhai's function overloading to be very useful in this regard, almost like writing JavaScript.

But you have to physically overload the function with a version that takes &str parameter in order for that to work, so it is essentially a duplicated API.

Another tip:

    // Leverage constants propagation to make scripts run faster
    const request_callback = Fn("process_request");
    service.map_request(request_callback);
    const response_callback = Fn("process_response");
    service.map_response(response_callback);

@ltabis
Copy link
Contributor

ltabis commented May 16, 2022

We are using rhai in vSMTP, a mail transfer agent made with rust.

Instead of filtering emails with a standard configuration format (like json or toml) we use Rhai to make powerful rules for filtering, enabling you to execute behavior on specific email addresses, clients, ip addresses etc ...

We are constantly trying to optimize the "rule engine" (the part that handles rhai) because the server needs to be really fast. But with the help of the team & rhai's great docs we are getting there :)

@schungx
Copy link
Collaborator

schungx commented May 16, 2022

We are constantly trying to optimize the "rule engine" (the part that handles rhai) because the server needs to be really fast. But with the help of the team & rhai's great docs we are getting there :)

Let us know if you have an issue with this. The trick with performance is to avoid cloning like the plague, and sometimes it is not obvious.

You can also join the Discord channel for discussions.

@schungx
Copy link
Collaborator

schungx commented May 16, 2022

BTW, just another tip-up. In your docs, you can link to https://rhai.rs/book/ref for a "Language Reference" section that contains only the scripting language, not the Rust engine bits. It would be easier on your users.

@YaLTeR
Copy link

YaLTeR commented Sep 12, 2022

I'm using Rhai in https://github.com/YaLTeR/bxt-rs to script the goal and constraints for a brute-force optimizer. At first it was Lua (luajit), but then I couldn't figure out how to make it cross-compile out of the box on all system configurations that I care about, and tried Rhai as a pure-Rust option. Seems to work great so far; on the small scripts that I'm using it doesn't seem any slower than luajit.

@schungx
Copy link
Collaborator

schungx commented Sep 12, 2022

it doesn't seem any slower than luajit.

If you're not repeatedly running a script in a tight loop, then the JIT doesn't really show its power...

@YaLTeR
Copy link

YaLTeR commented Sep 12, 2022

During the optimization process normally the rest of the code takes much longer than the goal checking script, so yeah.

One performance bottleneck that I did hit with both luajit and Rhai is passing large arrays to the script (I guess specifically converting to luajit's tables or Rhai's Dynamic type). The script accepts either a single struct with a few fields (player data for the last state) or an array with several hundred of those structs (player data for all states). Ideally I'd unconditionally pass all states but it's quite slow to convert. Not sure how to improve this in a way that doesn't require writing manual field access or serialization code.

@schungx
Copy link
Collaborator

schungx commented Sep 12, 2022

An option is to wrap that data structure in an Rc<Vec<Rc<Item>>>. Then it is shared access and in general very very fast.

But then you'll have to register the relevant API's to access elements in the array (e.g. indexers) and fields access for Item. The array must hold Rc-wrapped values because you don't want to be copying items whenever you access an array slot - an indexer cannot return a ref mut to the parent data.

But for high performance work when you don't want to copy things, it is worth it.

@ryankopf
Copy link

I am using RHAI in https://rpgfx.com/ RPG Studio

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests