-
Notifications
You must be signed in to change notification settings - Fork 32
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
Scripting API #107
Comments
It doesn't look like Mun is ready for wasm targets at this moment: see gitlab issue mun-lang/mun#215 I tried it anyways. I inserted a basic mun example into the punchy world and it resulted in the following.
See the branch of "example/mun-example" in the following repo for a reference example of what it would look like to add a mun runtime to punchy. (reminder: This will not build to
|
Thanks! We don’t actually need to support scripting for the web build, so making scripting exclusive to native builds is acceptable. Scripts don’t even need to be able to run in production (although we will eventually figure that out); their primary function is iterative prototyping. A rust implementation might still be required for anything running in production. |
I'm going to try an experiment with scripting to see if we can have scripted systems that are able to modify any components in the world that implement I haven't looked super deep into As for language, I'm going to try Rust Python because you can create intuitive bindings to Rust objects with it and it will work in WASM out of the box. It's just the easiest commonly known language to experiment with that runs on native and web right now. Another possible language that could be good would be JavaScript. We might be able to use Deno Core to support JavaScript on native platforms, and it would be awesome if we could get away with using the browser's native JavaScript engine for web support. @erlend-sh, you said we don't need WASM support for plugins, and I know this is supposed to be more for quick prototyping, but I do want to at least explore what it could be if we used scripts possibly to implement even core items. If it wasn't a performance or other kind of problem, I imagine that scripts could be a great way to implement items and maybe fighter moves, even in production. This would make it incredibly easy for people to mod the game, and they wouldn't have to think about this division between Rust core plugins and their mods so much. Again, this is just a quick experiment and it might quickly come to nothing, but I think it's worth entertaining, as long as we put a limit on how long we spend on it before moving to more feasible options, if this turns out difficult. |
I haven't gotten super deep into this yet, but I think I'm leaning towards JavaScript/TypeScript for scripting. I'm not super comfortable with the development status of RustPython. It seem like a great project but without a lot of developer bandwidth. Besides, JavaScript is probably way faster, especially if we re-use the browser's built-in engine instead of compiling Python to run in WASM, and it is arguably one of the most well-known languages of all time, for better or worse. Also, we might get some fairly strong security features from embedding the V8 JavaScript engine on native platforms, which is a fairly big deal if we want to have a mod store one day where you might otherwise need to beware of malware in mods. Deno Deploy runs V8 Isolates for hundreds of users on shared hardware if I understand correctly. If V8 isolates can be secure enough to keep malicious code from impacting the server and other isolates, then I think that's good enough to keep mods from trying to break the user's computer. Though there could absolutely be things I'm unaware of. That could be a big advantage, though, over a language like Lua which I believe usually has un-fettered access to your system. Finally, I figured out that I can transpile TypeScript to JavaScript ( albeit without type checking ), even in the browser using SWC. That means that we can allow you to use TypeScript for writing mods without having a separate compile step, even when targeting the browser. And you can use your IDE such as VSCode for auto-completion and type checking. |
I'm in with Typescript 😄 I'm personally neutral to it as a language, but after the success with Visual Studio Code, I recognize popularity as a big advantage. |
Just a FYI, there is a pretty healthy Typescript to lua project https://github.com/TypeScriptToLua/TypeScriptToLua. I personally also wouldn't assume that using the browser's JS engine will be good for performance due to the need to copy everything you want to share with JS (Or what JS wants to share with Rust). While normally you might have been able to get away with exposing a reference instead. |
That's a good point. 👍 In the strategy I'm working on, copies are only made when getting a specific value on a component, or when getting a specific value on a component, so at least it isn't trying to copy the whole world to JavaScript or anything like that, but if JavaScript, for instance, iterated through every component of a certain kind and read every single value of that component, it would end up copying all that data. To avoid copying at the expense of a less-compliant JavaScript implementation we could try boa to run JavaScript in WASM. Yet another option is to use
Lua still isn't anything that we can run in the browser without a lot of extra binding/linking work of some sort, for all the strategies I've seen so far to do it, but it might not be harder than the Anyway, I think the strongest argument in favor of JavaScrpt is the sandboxing support on native. If we have a reliable sandbox, then it makes a huge difference for our ability to provide a safe mod store in the future. Either way, with the route I'm taking so far, the language we choose won't effect most of the design very much ( not yet anyway ), and we can change it later. I made a little bit of progress, and I think that it should be simple enough to get scripts modifying components that implement |
Sounds promising! Regarding language choice and implementation for scripting, we’re fine with exploring multiple different paths here. There might be more than one right way to do scripting, with different constraints to solve for in web vs local, or modding vs prototyping. On the topic of JavaScript and TypeScript, I’d also count Dart as an attractive alternative, as it can also compile to JS. It has a healthy gamedev community at this point: https://github.com/flame-engine/flame For Lua in the browser, there’s this: https://github.com/ceifa/wasmoon |
There are quite a few projects to get a lua VM in the browser. Some that are an entire lua implementation in JS and others that compile lua to wasm. However, none of them are working with mlua/rlua at this point. mlua-rs/mlua#23 for the issue about it. Funnily enough, it seems that someone managed to get mlua + luau working (or at least semi working?) not too long ago but it requires A big pain point that this disconnect creates is:
|
Yeah, that's the issue. We really need WASM linking so that we can link a WASM build of lua such as wasmoon with a separately built Rust WASM binary. There are some experiments for that that we should look into if we want to pursue that.
👍 👍 Totally agree. For development bandwidth we'll want to focus on a middle-ground of what we can get working and what solves the most problems, but at the same time we can probably extend it later and support plenty of different scripting languages if we we really want to. Just got to try stuff out and find out what we like. I've been on vacation so I haven't given this a lot of focus time yet, but I'm still making progress here and there, and hopefully I'll have a script modifying system components soon. |
Got the first Punchy script working! let counter = 0;
let systems = {
update() {
Punchy.log(`Script update #${counter}`, "trace");
counter++;
},
};
systems; And it has auto-completion and type checking with the typescript definitions working. You'll be able to specify different functions such as Next I'll pass in the scriptable components and entities as an argument to the Even once we've gotten that working there's still a lot to consider and figure out. For instance, I can allow you to modify the translation, rotation, and scale of objects by binding to the transform component, but that doesn't mean you can call any functions on But we'll figure it out as we go, we're making progress! |
Just got a big task done for scripting: I reworked the script runner to be able to asynchronously load modules and to use JavaScript modules instead of standalone scripts. This means that scripts can now use the This should make it easier to make more advanced scripts with dependencies on other code, and dependencies will be asynchronously downloaded over the network if necessary or can be packaged locally with the rest of the game assets. Also, I got the scripting engine implementation moved behind a scripting API trait that should make it easier to implement different scripting engines later. Here's what the latest script looks like: class Test1 implements ScriptSystems {
counter: number = 0;
update() {
if (this.counter == 0) {
Punchy.log("First update for my script!", "info");
}
Punchy.log(`Script update #${this.counter}`, "trace");
this.counter++;
}
}
export default new Test1(); |
Just realized that the browser won't be able to load TypeScript modules through URLs as normal modules, so I'm postponing dependencies. In the future we have two options.
That probably wouldn't be very difficult to implement, and you could probably use the Now I'm working on getting the script runner to work in the browser. |
If you do plan to have scripts be executed b6 the js vm of the browser then I advise you to make it work first and to then measure the cost of ffi before brainstorming about dependencies. Like I said before ffi is expensive if running this way so I wouldn't worry about the dependency thing yet too much. Also, rather than allowing every url as a dependency why not restrict it to just a server owned by spicy lobster studio? That way basic code scanning is possible to implement if it proves to be desired and any transformations needed to the code to make it work everywhere can be done by that server rather than needing to be bundled with said target. |
Also, keep in mind that if the scripts run in the browser that they will have access to cookies, local storage, etc. So it is probably best to also see how to properly sandbox it from that. |
The idea is that anybody can host their own punchy asset dir that can be played by the punchy web player, so that you can essentially push some assets to a Git repo, enable GitHub pages, and then instantly share a link to your game with anybody. This should actually work today with a link similar to this ( though I haven't tested yet, the code is there to make that work ): https://fishfight.github.io/punchy/player/v0.0.3/?asset_url=https://zicklag.github.io/my_punchy_game So if somebody wanted to host their own asset server that did automatic transformations of source files ( including spicy lobster ) that would be totally possible.
That's a good point. I think the only way to really sandbox that, though is to host it on it's own domain, which we should actually do, now that you mention it. It's similar to the concept of Another alternative that I think might be almost as secure ( or just as secure, I'd have to do some research ) is to allocate a sub-domain for the web player, as long as we are aware that anything on a sub-domain of that sub-domain could be potentially accessed by the game and it's potentially malicious mods. Anyway, as long as mods can only maliciously impact the game itself, that's totally fine. The browser is repsonsible for protecting the user's system. But yeah, I'm actually migrating back to using just plain scripts instead of modules now because of browser loading difficulties and the fact that we won't really need it. I think I'm getting close to a working MVP. |
Bevy 0.8.0 was released! And it has extra stuff needed for complete scripting support. And @jakobhellermann has a work-in-progress TypeScript plugin that looks like it has much more access to the Bevy API in the scripting interface than I thought anybody had yet. It uses Deno core just like I was using too. We might be able to borrow from it, contribute to it, or otherwise collaborate. I'll have to have a look at what it's doing exactly. |
For anyone that wants a bit more context on the change in 0.8 that enables this https://bevyengine.org/news/bevy-0-8/#scripting-modding-progress-untyped-ecs-apis It would be really cool if we could collaborate with bevy_mod_js_scripting if it has enough similarity to what youve been working on setting up. |
Just got the same script running in the browser and on native without changes! Now I've just got to get basic component access implemented and we should be able to get our first script working. 🤞
It's very similar from what I can tell so far and we should be able to borrow the most important pieces. The differences center around how we are trying to get JavaScript to work in the browser without changes, but that's also the part that's "least important" and nearly independent of Bevy itself. |
New tech for Rust scripting: https://robert.kra.hn/posts/hot-reloading-rust/ |
After 5 years of trying to get modding into a game engine, since the original time I tried to do it in Godot, I've finally moved the camera in a Bevy game, using a sandboxed TypeScript file, by directly accessing the ECS! That's really exciting. 🥳 Also, it sounds like we're pretty much on the same page on browser support for scripting as So I might be able to contribute to that in order to get browser support working, and we can just add it as a dependency. I've read through most of it's code and I'm really happy with the way it's implemented, so it'd be great to contribute instead of copying a chunk of it into Punchy. |
Almost finished with the scripting implementation for the browser! Here's a script incrementing the score in the browser every second. Now I've just got to implement one last browser operation, clean up the code, and get jakobhellermann/bevy_mod_js_scripting#11 ready to merge! |
Where’s this at? Did the browser present some additional challenges? Keep in mind that we don’t have to support the browser use case for the v1 of this feature. Seems to me like we’ve already got what we need to roll out local scripting, right? |
I'm actually just waiting on review for jakobhellermann/bevy_mod_js_scripting#11. Everything other than module loading, which is non-essential for MVP, is working on both native and browser. I can technically start working on finishing off the Punchy scripting MVP using my branch of |
I'll push the code tomorrow, or later tonight if I get the chance, but I just got a working scripted, health item!! It replaces our current health item with the hard coded item type. I can't believe it's working! The advantage of a script for a health item can be immediately seen in simple ways like the fact that instead of telling the item to refill 100000 health so that it goes to full, we can actually query the max health of the player and set the health to that. Or we could easily modify it to add 25% of the player's max health, which would previously be impossible without adding more YAML attributes that are hard-coded into the game. This doesn't mean we can just "script all the things" yet, because there are a lot of holes in the functionality and things that we have to work around, but it's a huge step, and it looks like the future is full of possibilities for how to improve. health.item.yaml: name: Health
image:
image: health.png
image_size: [22, 20]
kind: !Script
script: ./health.ts
type Health = {
0: number;
};
const Health: BevyType<Health> = { typeName: "punchy::damage::Health" };
type Stats = {
max_health: i32,
movement_speed: f32,
};
const Stats: BevyType<Stats> = { typeName: "punchy::fighter::Stats" };
export default {
post_update() {
const grabEvents = punchy.getItemGrabEvents();
for (const event of grabEvents) {
const fighter = event.fighter;
const [health, stats] = world.get(fighter, Health, Stats);
health[0] = stats.max_health;
}
},
}; |
Description
Lots of higher level coders won’t be interested in modding unless we support a higher level scripting language, like Lua.
Alternatives & Prior Art
While still being a very limited language, Mun would also be super exciting to start playing with:
Tasks
The text was updated successfully, but these errors were encountered: