Compare commits

...

45 commits
v0.1.0 ... main

Author SHA1 Message Date
Jarosław Konik
fa70abac23
Add annotations about Ruby only being supported on Linux (#64)
Some checks failed
Book / test (push) Has been cancelled
Deploy book / deploy (push) Has been cancelled
Rust / build (push) Has been cancelled
2025-06-01 18:28:22 +02:00
41d0fd57f3 Bump version
Some checks failed
Book / test (push) Has been cancelled
Deploy book / deploy (push) Has been cancelled
Rust / build (push) Has been cancelled
2025-06-01 14:21:40 +02:00
Curt Reyes
e430795dce
Compile on windows (#62)
* Don't check for rdynamic linking on Windows for now, fixes Windows builds

---------

Co-authored-by: Jaroslaw Konik <konikjar@gmail.com>
2025-06-01 14:19:43 +02:00
Jarosław Konik
c6bdfb1400
Fix rb-sys version (#60) 2025-05-27 19:55:13 +02:00
Jarosław Konik
450962b1d0
Bump version to 0.9.0 (#59)
Some checks failed
Book / test (push) Has been cancelled
Deploy book / deploy (push) Has been cancelled
Rust / build (push) Has been cancelled
2025-05-27 19:35:32 +02:00
Jarosław Konik
53479f94b5
Ruby support (#19)
Some checks are pending
Book / test (push) Waiting to run
Deploy book / deploy (push) Waiting to run
Rust / build (push) Waiting to run
Add support for Ruby language
2025-05-27 19:19:52 +02:00
Jarosław Konik
f2bb079c60
Update README.md (#58) 2025-05-23 08:53:07 +02:00
56fd44062c Bump version
Some checks failed
Deploy book / deploy (push) Has been cancelled
Rust / build (push) Has been cancelled
2025-05-07 14:37:51 +02:00
Jarosław Konik
297bffd060
Make value struct fields public, add examples (#44) 2025-05-07 14:23:26 +02:00
Peter David Faria
2c82a2fa0e
Added initial support for bevy 0.16.0 (#39)
* Added initial support for bevy 0.16.0

* Removed unnecessary bevy feature

* update version references

---------

Co-authored-by: Jaroslaw Konik <konikjar@gmail.com>
2025-05-01 11:18:52 +02:00
Jarosław Konik
ec84d9e740
move demo up (#37) 2025-04-18 08:54:33 +00:00
GolDNenex
f9e872e111
Update bevy 0.15 (#36)
* Update bevy 0.15

* Removed unused import "AsyncReadExt"

* Update README.md

* Update lib.rs

* Update README.md

* Fix cargo test by not running examples that never end in lib.rs

* get rid of warnings

* update version references

* dont run examples

* deny warnings

* remove obsolete runners

---------

Co-authored-by: Jarosław Konik <konikjar@gmail.com>
2025-04-15 21:27:08 +02:00
gak
a718aa7ce6
smallest typo fix ever: life -> live (#25) 2024-07-28 07:09:33 +02:00
Dennis
be8a80519d
Added add_scripting_api function to AppBuilder (#23)
* Added `add_scripting_api` function to builder
2024-07-17 10:36:00 +02:00
Jarosław Konik
f85dbcffe2
update readme (#22) 2024-07-05 17:26:25 +02:00
Jarosław Konik
182f26e273
update book bevy support matrix (#21) 2024-07-05 17:21:00 +02:00
Jarosław Konik
7488de076b
support bevy 0.14 (#20)
* support bevy 0.14
2024-07-05 17:16:06 +02:00
Jarosław Konik
eaffce5c6d
add demo (#17) 2024-06-16 07:54:48 +02:00
0766aef930 update version 2024-06-16 07:26:25 +02:00
7a8721f78f readme 2024-06-16 07:25:40 +02:00
a2ddb9b5c1 link to book 2024-06-16 07:25:11 +02:00
94b64f37a8 fix book build 2024-06-16 07:20:19 +02:00
3887e544b1 fix book build 2024-06-16 07:14:41 +02:00
6a30384aef change book action 2024-06-16 07:12:28 +02:00
Jarosław Konik
6726e40768
Lua support (#16)
* adds Lua language support
* makes the library generic, allowing for easy extension for more languages
* small improvements and fixes
2024-06-16 07:06:09 +02:00
7846b556ca Support bevy 0.13 2024-04-10 19:53:58 +02:00
Jarosław Konik
407da8d608
0.3 (#10) 2024-04-10 19:45:42 +02:00
Jarosław Konik
60984220a0
Bevy 0.12 (#9)
* Update to bevy 0.12
2024-04-10 18:36:11 +02:00
Jarosław Konik
928f5437fc
Apply side effects for tuple args systems (#8)
* Apply side effects for tuple args systems.

Deferred side effects(Commands) that were pushed in scripting exposed
systems were only applied for argument-less systems.
This PR fixes this by applying deferred side effects in macro that
implement functions with arguments.

* Bump version
2023-08-23 18:29:48 +02:00
Jarosław Konik
8997159e2f
Release 0.2.1 (#6) 2023-08-23 17:36:11 +02:00
Jarosław Konik
6f35089693
Apply side effects after running system (#5)
Deferred side effects(Commands) that were pushed in scripting exposed
systems were never applied. This commit applies deferred
2023-08-23 17:23:32 +02:00
db3258e832 change email 2023-08-10 15:06:47 +02:00
Jarosław Konik
bed58a9c2b
Merge pull request #2 from jarkonik/jarkonik-patch-1
Update README.md
2023-07-24 21:51:30 +02:00
Jarosław Konik
4458ef2daf
Update README.md 2023-07-24 21:51:20 +02:00
Jarosław Konik
57459462d5
Merge pull request #1 from jarkonik/bevy0.11
Migrate to bevy 0.11
2023-07-11 20:52:57 +02:00
025cad413d Update docs 2023-07-11 20:48:22 +02:00
ab6cfe825e Migrate to bevy 0.11 2023-07-11 20:42:49 +02:00
Jarosław Konik
bce2e55d0d
Create pull_request_template.md 2023-07-01 18:53:15 +02:00
Jarosław Konik
db6c9381d0
Update issue templates 2023-07-01 18:52:32 +02:00
Jarosław Konik
30a2acd121
Create SECURITY.md 2023-07-01 18:52:06 +02:00
Jarosław Konik
d064450b78
Create CONTRIBUTING.md 2023-07-01 18:51:09 +02:00
Jarosław Konik
be630f9b86
Create CODE_OF_CONDUCT.md 2023-07-01 18:50:10 +02:00
Jarosław Konik
d574104412
Create FUNDING.yml 2023-06-21 17:40:46 +02:00
b4859e817e Cleanup 2023-06-21 17:01:19 +02:00
be7fa2e48b Add crate metadata 2023-06-21 17:00:29 +02:00
191 changed files with 8997 additions and 731 deletions

3
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [jarkonik]

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

10
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View file

@ -0,0 +1,10 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

54
.github/workflows/book.yml vendored Normal file
View file

@ -0,0 +1,54 @@
name: Book
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Cache Ruby
id: cache-ruby
uses: actions/cache@v4
with:
path: rubies
key: ${{ runner.os }}-ruby
- name: Install Ruby
if: steps.cache-ruby.outputs.cache-hit != 'true'
env:
CC: clang
run: |
url="https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.4.tar.gz"
prefix=`pwd`/rubies/ruby-3.4
mkdir rubies
mkdir ruby_src
curl -sSL $url | tar -xz
cd ruby-3.4.4
mkdir build
cd build
../configure --without-shared --prefix=$prefix
make install
echo $prefix/bin >> $GITHUB_PATH
- name: Add Ruby to PATH
run: |
prefix=`pwd`/rubies/ruby-3.4
echo $prefix/bin >> $GITHUB_PATH
- name: Install latest mdbook
run: |
tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name')
url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz"
mkdir mdbook
curl -sSL $url | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
- name: Test Book
run: |
cd book
export CARGO_MANIFEST_DIR=$(pwd)
cargo build
mdbook test -L target/debug/deps/

38
.github/workflows/deploy_book.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Deploy book
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
contents: write # To push a branch
pages: write # To push to a GitHub Pages site
id-token: write # To update the deployment status
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install latest mdbook
run: |
tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name')
url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz"
mkdir mdbook
curl -sSL $url | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
- name: Build Book
run: |
cd book
mdbook build
- name: Setup Pages
uses: actions/configure-pages@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
# Upload entire repository
path: 'book/book'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View file

@ -11,18 +11,39 @@ env:
jobs:
build:
runs-on: ubuntu-latest
env:
RUSTFLAGS: -D warnings
steps:
- uses: actions/checkout@v3
- name: Cache Ruby
id: cache-ruby
uses: actions/cache@v4
with:
path: rubies
key: ${{ runner.os }}-ruby
- name: Install Ruby
if: steps.cache-ruby.outputs.cache-hit != 'true'
env:
CC: clang
run: |
url="https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.4.tar.gz"
prefix=`pwd`/rubies/ruby-3.4
mkdir rubies
mkdir ruby_src
curl -sSL $url | tar -xz
cd ruby-3.4.4
mkdir build
cd build
../configure --without-shared --prefix=$prefix
make install
- name: Add Ruby to PATH
run: |
prefix=`pwd`/rubies/ruby-3.4
echo $prefix/bin >> $GITHUB_PATH
- name: Clippy
run: cargo clippy --verbose -- -D warnings
run: cargo clippy --all-features --verbose -- -D warnings
- name: Build
run: cargo build --verbose
run: cargo build --all-features --verbose
- name: Run tests
run: cargo test --verbose
- name: Install cargo-examples
run: cargo install cargo-examples
- name: Run all examples
run: cargo examples
run: cargo test --all-features --verbose

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target
/Cargo.lock
.vscode
.nvim.lua

128
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
yellow.egg4414@fastmail.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

4
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,4 @@
1. Fork.
2. Create a pull request.
3. Make sure all automated checks pass.

View file

@ -1,14 +1,217 @@
[package]
name = "bevy_scriptum"
version = "0.1.0"
edition = "2021"
authors = ["Jaroslaw Konik <konikjar@gmail.com>"]
version = "0.9.1"
edition = "2024"
license = "MIT OR Apache-2.0"
readme = "README.md"
categories = ["game-development"]
description = "Plugin for Bevy engine that allows you to write some of your game or application logic in a scripting language"
repository = "https://github.com/jarkonik/bevy_scriptum"
keywords = ["bevy", "lua", "scripting", "game", "script"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
lua = ["dep:mlua", "mlua/luajit"]
rhai = ["dep:rhai"]
ruby = ["dep:magnus", "dep:rb-sys"]
[dependencies]
bevy = { default-features = false, version = "0.10.1", features = [
"bevy_asset",
] }
bevy = { default-features = false, version = "0.16", features = ["bevy_asset", "bevy_log"] }
serde = "1.0.162"
rhai = { version = "1.14.0", features = ["sync", "internals", "unchecked"] }
rhai = { version = "1.14.0", features = [
"sync",
"internals",
"unchecked",
], optional = true }
thiserror = "1.0.40"
anyhow = "1.0.82"
tracing = "0.1.40"
mlua = { version = "0.9.8", features = [
"luajit",
"vendored",
"send",
], optional = true }
magnus = { version = "0.7.1", optional = true }
rb-sys = { version = "0.9", default-features = false, features = ["link-ruby", "ruby-static"], optional = true }
crossbeam-channel = "0.5.15"
libc = "0.2.172"
[[example]]
name = "call_function_from_rust_rhai"
path = "examples/rhai/call_function_from_rust.rs"
required-features = ["rhai"]
[[example]]
name = "current_entity_rhai"
path = "examples/rhai/current_entity.rs"
required-features = ["rhai"]
[[example]]
name = "custom_type_rhai"
path = "examples/rhai/custom_type.rs"
required-features = ["rhai"]
[[example]]
name = "ecs_rhai"
path = "examples/rhai/ecs.rs"
required-features = ["rhai"]
[[example]]
name = "entity_variable_rhai"
path = "examples/rhai/entity_variable.rs"
required-features = ["rhai"]
[[example]]
name = "function_params_rhai"
path = "examples/rhai/function_params.rs"
required-features = ["rhai"]
[[example]]
name = "hello_world_rhai"
path = "examples/rhai/hello_world.rs"
required-features = ["rhai"]
[[example]]
name = "multiple_plugins_rhai"
path = "examples/rhai/multiple_plugins.rs"
required-features = ["rhai"]
[[example]]
name = "non_closure_system_rhai"
path = "examples/rhai/non_closure_system.rs"
required-features = ["rhai"]
[[example]]
name = "promises_rhai"
path = "examples/rhai/promises.rs"
required-features = ["rhai"]
[[example]]
name = "side_effects_rhai"
path = "examples/rhai/side_effects.rs"
required-features = ["rhai"]
[[example]]
name = "function_return_value_rhai"
path = "examples/rhai/function_return_value.rs"
required-features = ["rhai"]
[[example]]
name = "call_function_from_rust_lua"
path = "examples/lua/call_function_from_rust.rs"
required-features = ["lua"]
[[example]]
name = "current_entity_lua"
path = "examples/lua/current_entity.rs"
required-features = ["lua"]
[[example]]
name = "custom_type_lua"
path = "examples/lua/custom_type.rs"
required-features = ["lua"]
[[example]]
name = "ecs_lua"
path = "examples/lua/ecs.rs"
required-features = ["lua"]
[[example]]
name = "entity_variable_lua"
path = "examples/lua/entity_variable.rs"
required-features = ["lua"]
[[example]]
name = "function_params_lua"
path = "examples/lua/function_params.rs"
required-features = ["lua"]
[[example]]
name = "hello_world_lua"
path = "examples/lua/hello_world.rs"
required-features = ["lua"]
[[example]]
name = "multiple_plugins_lua"
path = "examples/lua/multiple_plugins.rs"
required-features = ["lua"]
[[example]]
name = "non_closure_system_lua"
path = "examples/lua/non_closure_system.rs"
required-features = ["lua"]
[[example]]
name = "promises_lua"
path = "examples/lua/promises.rs"
required-features = ["lua"]
[[example]]
name = "side_effects_lua"
path = "examples/lua/side_effects.rs"
required-features = ["lua"]
[[example]]
name = "function_return_value_lua"
path = "examples/lua/function_return_value.rs"
required-features = ["lua"]
[[example]]
name = "call_function_from_rust_ruby"
path = "examples/ruby/call_function_from_rust.rs"
required-features = ["ruby"]
[[example]]
name = "current_entity_ruby"
path = "examples/ruby/current_entity.rs"
required-features = ["ruby"]
[[example]]
name = "custom_type_ruby"
path = "examples/ruby/custom_type.rs"
required-features = ["ruby"]
[[example]]
name = "ecs_ruby"
path = "examples/ruby/ecs.rs"
required-features = ["ruby"]
[[example]]
name = "entity_variable_ruby"
path = "examples/ruby/entity_variable.rs"
required-features = ["ruby"]
[[example]]
name = "function_params_ruby"
path = "examples/ruby/function_params.rs"
required-features = ["ruby"]
[[example]]
name = "function_return_value_ruby"
path = "examples/ruby/function_return_value.rs"
required-features = ["ruby"]
[[example]]
name = "hello_world_ruby"
path = "examples/ruby/hello_world.rs"
required-features = ["ruby"]
[[example]]
name = "multiple_plugins_ruby"
path = "examples/ruby/multiple_plugins.rs"
required-features = ["ruby"]
[[example]]
name = "promises_ruby"
path = "examples/ruby/promises.rs"
required-features = ["ruby"]
[[example]]
name = "side_effects_ruby"
path = "examples/ruby/side_effects.rs"
required-features = ["ruby"]
[dev-dependencies]
tracing-subscriber = "0.3.18"
mlua = { version = "0.9.8", features = ["luajit", "vendored", "send"] }
rhai = { version = "1.14.0", features = ["sync", "internals", "unchecked"] }

152
README.md
View file

@ -1,34 +1,47 @@
# bevy_scriptum 📜
⚠️ **Pre-release, alpha version**: API is bound to change, bugs are to be expected.
![demo](demo.gif)
bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game logic in a scripting language.
Currently, only [Rhai](https://rhai.rs/) is supported, but more languages may be added in the future.
bevy_scriptum is a a plugin for [Bevy](https://bevyengine.org/) that allows you to write some of your game or application logic in a scripting language.
### Supported scripting languages/runtimes
It's main advantages include:
| language/runtime | cargo feature | documentation chapter |
| ------------------------------------------ | ------------- | --------------------------------------------------------------- |
| 🌙 LuaJIT | `lua` | [link](https://jarkonik.github.io/bevy_scriptum/lua/lua.html) |
| 🌾 Rhai | `rhai` | [link](https://jarkonik.github.io/bevy_scriptum/rhai/rhai.html) |
| 💎 Ruby(currently only supported on Linux) | `ruby` | [link](https://jarkonik.github.io/bevy_scriptum/ruby/ruby.html) |
Documentation book is available [here](https://jarkonik.github.io/bevy_scriptum/) 📖
Full API docs are available at [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/) 🧑‍💻
bevy_scriptum's main advantages include:
- low-boilerplate
- easy to use
- asynchronicity with a promise-based API
- flexibility
- hot-reloading
Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game logic without having to recompile your game.
Scripts are separate files that can be hot-reloaded at runtime. This allows you to quickly iterate on your game or application logic without having to recompile it.
All you need to do is register callbacks on your Bevy app like this:
```rust
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(ScriptingPlugin::default())
.add_script_function(String::from("hello_bevy"), || {
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("hello_bevy"), || {
println!("hello bevy, called from script");
});
})
.run();
```
And you can call them in your scripts like this:
```rhai
hello_bevy();
```lua
hello_bevy()
```
Every callback function that you expose to the scripting language is also a Bevy system, so you can easily query and mutate ECS components and resources just like you would in a regular Bevy system:
@ -36,14 +49,15 @@ Every callback function that you expose to the scripting language is also a Bevy
```rust
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
#[derive(Component)]
struct Player;
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(ScriptingPlugin::default())
.add_script_function(
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(
String::from("print_player_names"),
|players: Query<&Name, With<Player>>| {
for player in &players {
@ -51,27 +65,31 @@ App::new()
}
},
);
})
.run();
```
You can also pass arguments to your callback functions, just like you would in a regular Bevy system - using `In` structs with tuples:
```rust
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use rhai::ImmutableString;
use bevy_scriptum::runtimes::lua::prelude::*;
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(ScriptingPlugin::default())
.add_script_function(
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(
String::from("fun_with_string_param"),
|In((x,)): In<(ImmutableString,)>| {
|In((x,)): In<(String,)>| {
println!("called with string: '{}'", x);
},
);
})
.run();
```
which you can then call in your script like this:
```rhai
fun_with_string_param("Hello world!");
```lua
fun_with_string_param("Hello world!")
```
### Usage
@ -80,94 +98,118 @@ Add the following to your `Cargo.toml`:
```toml
[dependencies]
bevy_scriptum = "0.1"
bevy_scriptum = { version = "0.9", features = ["lua"] }
```
or execute `cargo add bevy_scriptum` from your project directory.
Add the following to your `main.rs`:
```rust
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(ScriptingPlugin::default())
.run();
```
or execute `cargo add bevy_scriptum --features lua` from your project directory.
You can now start exposing functions to the scripting language. For example, you can expose a function that prints a message to the console:
```rust
use rhai::ImmutableString;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(ScriptingPlugin::default())
.add_script_function(
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(
String::from("my_print"),
|In((x,)): In<(ImmutableString,)>| {
|In((x,)): In<(String,)>| {
println!("my_print: '{}'", x);
},
);
})
.run();
```
Then you can create a script file in `assets` directory called `script.rhai` that calls this function:
Then you can create a script file in `assets` directory called `script.lua` that calls this function:
```rhai
my_print("Hello world!");
```lua
my_print("Hello world!")
```
And spawn a `Script` component with a handle to a script source file`:
And spawn an entity with attached `Script` component with a handle to a script source file:
```rust
use bevy::prelude::*;
use bevy_scriptum::Script;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
App::new()
.add_startup_system(|mut commands: Commands, asset_server: Res<AssetServer>| {
commands.spawn(Script::new(asset_server.load("script.rhai")));
});
.add_plugins(DefaultPlugins)
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(
String::from("my_print"),
|In((x,)): In<(String,)>| {
println!("my_print: '{}'", x);
},
);
})
.add_systems(Startup,|mut commands: Commands, asset_server: Res<AssetServer>| {
commands.spawn(Script::<LuaScript>::new(asset_server.load("script.lua")));
})
.run();
```
You should then see `my_print: 'Hello world!'` printed in your console.
### Provided examples
You can also try running provided examples by cloning this repository and running `cargo run --example <example_name>`. For example:
You can also try running provided examples by cloning this repository and running `cargo run --example <example_name>_<language_name>`. For example:
```bash
cargo run --example hello_world
cargo run --example hello_world_lua
```
The examples live in `examples` directory and their corresponding scripts live in `assets/examples` directory within the repository.
### Bevy compatibility
| bevy version | bevy_scriptum version |
|--------------|----------------------|
|--------------|-----------------------|
| 0.16 | 0.8-0.9 |
| 0.15 | 0.7 |
| 0.14 | 0.6 |
| 0.13 | 0.4-0.5 |
| 0.12 | 0.3 |
| 0.11 | 0.2 |
| 0.10 | 0.1 |
### Promises - getting return values from scripts
Every function called from script returns a promise that you can call `.then` with a callback function on. This callback function will be called when the promise is resolved, and will be passed the return value of the function called from script. For example:
Every function called from script returns a promise that you can call `:and_then` with a callback function on. This callback function will be called when the promise is resolved, and will be passed the return value of the function called from script. For example:
```rhai
get_player_name().then(|name| {
print(name);
});
```lua
get_player_name():and_then(function(name)
print(name)
end)
```
which will print out `John` when used with following exposed function:
### Access entity from script
```rust
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("get_player_name"), || String::from("John"));
});
````
## Access entity from script
A variable called `entity` is automatically available to all scripts - it represents bevy entity that the `Script` component is attached to.
It exposes `.index()` method that returns bevy entity index.
It exposes `index` property that returns bevy entity index.
It is useful for accessing entity's components from scripts.
It can be used in the following way:
```rhai
print("Current entity index: " + entity.index());
```lua
print("Current entity index: " .. entity.index)
```
`entity` variable is currently not available within promise callbacks.
### Contributing
Contributions are welcome! Feel free to open an issue or submit a pull request.

14
SECURITY.md Normal file
View file

@ -0,0 +1,14 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 0.9 | :white_check_mark: |
## Reporting a Vulnerability
Vulnerabilities should be reported at konikjar@gmail.com

View file

@ -0,0 +1,13 @@
local my_state = {
iterations = 0,
}
function on_update()
my_state.iterations = my_state.iterations + 1;
print("on_update called " .. my_state.iterations .. " times")
if my_state.iterations >= 10 then
print("calling quit");
quit();
end
end

View file

@ -0,0 +1,7 @@
-- entity is a global variable that is set to the entity that is currently being processed,
-- it is automatically available in all scripts
-- get name of the entity
get_name(entity):and_then(function(name)
print(name)
end)

View file

@ -0,0 +1,4 @@
-- Create a new instance of MyType
my_type = MyType();
-- Call registered method
print(my_type:my_method())

View file

@ -0,0 +1 @@
print_player_names()

View file

@ -0,0 +1,3 @@
-- entity is a global variable that is set to the entity that is currently being processed,
-- it is automatically available in all scripts
print("Current entity index: " .. entity.index)

View file

@ -0,0 +1,4 @@
fun_with_string_param("hello")
fun_with_i64_param(5)
fun_with_multiple_params(5, "hello")
fun_with_i64_and_array_param(5, { 1, 2, "third element" })

View file

@ -0,0 +1,3 @@
function get_value()
return 42
end

View file

@ -0,0 +1 @@
hello_from_plugin_a()

View file

@ -0,0 +1 @@
hello_from_plugin_b_with_parameters("hello", 42)

View file

@ -0,0 +1,3 @@
get_player_name():and_then(function(name)
print(name)
end);

View file

@ -0,0 +1 @@
spawn_entity();

View file

@ -1,3 +1,3 @@
// entity is a global variable that is set to the entity that is currently being processed,
// it is automatically available in all scripts
print("Current entity index: " + entity.index());
print("Current entity index: " + entity.index);

View file

@ -0,0 +1,3 @@
fn get_value() {
42
}

View file

@ -0,0 +1 @@
hello_bevy();

View file

@ -0,0 +1 @@
hello_from_plugin_a();

View file

@ -0,0 +1 @@
hello_from_plugin_b_with_parameters("hello", 42);

View file

@ -0,0 +1 @@
spawn_entity();

View file

@ -0,0 +1,13 @@
$my_state = {
iterations: 0,
}
def on_update
$my_state[:iterations] += 1
puts("on_update called #{$my_state[:iterations]} times")
if $my_state[:iterations] >= 10
print("calling quit");
quit()
end
end

View file

@ -0,0 +1,3 @@
get_name(Bevy::Entity.current).and_then do |name|
puts(name)
end

View file

@ -0,0 +1,4 @@
# Create a new instance of MyType
my_type = MyType.new()
# Call registered method
puts(my_type.my_method)

View file

@ -0,0 +1 @@
print_player_names

View file

@ -0,0 +1,2 @@
# Bevy::Entity.current can be used to access the entity that is currently being processed
puts("Current entity index: #{Bevy::Entity.current.index}")

View file

@ -0,0 +1,4 @@
fun_with_string_param("hello")
fun_with_i64_param(5)
fun_with_multiple_params(5, "hello")
fun_with_i64_and_array_param(5, [1, 2, "third element"])

View file

@ -0,0 +1,3 @@
def get_value
42
end

View file

@ -0,0 +1 @@
hello_bevy()

View file

@ -0,0 +1 @@
hello_from_plugin_a

View file

@ -0,0 +1 @@
hello_from_plugin_b_with_parameters("hello", 42)

View file

@ -0,0 +1,3 @@
get_player_name.and_then do |name|
puts name
end

View file

@ -0,0 +1 @@
spawn_entity()

View file

@ -0,0 +1,3 @@
function test_func()
print("abc" + 5)
end

View file

@ -0,0 +1,7 @@
State = {
called_with = nil
}
function test_func(x)
called_with = x
end

View file

@ -0,0 +1,3 @@
function test_func()
rust_func(entity.index)
end

View file

@ -0,0 +1,5 @@
index = entity.index
function test_func()
rust_func(index)
end

View file

@ -0,0 +1,2 @@
mark_called()
error()

View file

@ -0,0 +1,3 @@
function test_func()
rust_func(entity)
end

View file

@ -0,0 +1,3 @@
function test_func()
rust_func(Vec3(1.5, 2.5, -3.5))
end

View file

@ -0,0 +1,6 @@
function test_func(vec3)
assert(vec3.x == 1.5)
assert(vec3.y == 2.5)
assert(vec3.z == -3.5)
mark_success()
end

View file

@ -0,0 +1,5 @@
function test_func()
rust_func():and_then(function(x)
print("abc" + 5)
end)
end

View file

@ -0,0 +1,9 @@
State = {
x = nil
}
function test_func()
rust_func():and_then(function(x)
State.x = x
end)
end

View file

@ -0,0 +1,3 @@
function test_func()
rust_func()
end

View file

@ -0,0 +1,3 @@
function test_func()
rust_func(5, "test")
end

View file

@ -0,0 +1,3 @@
function test_func()
rust_func(5)
end

View file

@ -0,0 +1,7 @@
State = {
times_called = 0
}
function test_func()
State.times_called = State.times_called + 1;
end

View file

@ -0,0 +1,9 @@
State = {
a_value = nil,
b_value = nil
}
function test_func(a, b)
State.a_value = a
State.b_value = b
end

View file

@ -0,0 +1,7 @@
State = {
a_value = nil
}
function test_func(a)
State.a_value = a
end

View file

@ -0,0 +1,3 @@
function test_func()
spawn_entity()
end

View file

@ -0,0 +1,3 @@
fn test_func() {
print("abc" * 5)
}

View file

@ -0,0 +1,2 @@
fn test_func(x) {
}

View file

@ -0,0 +1,3 @@
fn test_func() {
rust_func(entity.index);
}

View file

@ -0,0 +1,5 @@
let index = entity.index;
fn test_func() {
rust_func(index);
}

View file

@ -0,0 +1,2 @@
mark_called();
throw();

View file

@ -0,0 +1,3 @@
fn test_func() {
rust_func(entity);
}

View file

@ -0,0 +1,3 @@
fn test_func() {
rust_func(new_vec3(1.5, 2.5, -3.5));
}

View file

@ -0,0 +1,7 @@
fn test_func(vec3) {
if type_of(vec3) != "Vec3" { throw() }
if vec3.x != 1.5 { throw() }
if vec3.y != 2.5 { throw() }
if vec3.z != -3.5 { throw() }
mark_success();
}

View file

@ -0,0 +1,5 @@
fn test_func() {
rust_func().then(|x| {
print("abc" * 5)
})
}

View file

@ -0,0 +1,9 @@
let state = #{
x: 0
};
fn test_func() {
rust_func().then(|x| {
state.x = x;
})
}

View file

@ -0,0 +1,3 @@
fn test_func() {
rust_func();
}

View file

@ -0,0 +1,3 @@
fn test_func() {
rust_func(5, "test");
}

View file

@ -0,0 +1,3 @@
fn test_func() {
rust_func(5);
}

View file

@ -0,0 +1,7 @@
let state = #{
times_called: 0
};
fn test_func() {
state.times_called += 1;
}

View file

@ -0,0 +1,9 @@
let state = #{
a_value: (),
b_value: ()
};
fn test_func(a, b) {
state.a_value = a;
state.b_value = b;
}

View file

@ -0,0 +1,7 @@
let state = #{
a_value: ()
};
fn test_func(a) {
state.a_value = a
}

View file

@ -0,0 +1,3 @@
fn test_func() {
spawn_entity()
}

View file

@ -0,0 +1,3 @@
def test_func
raise
end

View file

@ -0,0 +1,7 @@
$state = {
'called_with' => nil
}
def test_func(val)
$state['called_with'] = val
end

View file

@ -0,0 +1,3 @@
def test_func
rust_func(Bevy::Entity.current.index)
end

View file

@ -0,0 +1,5 @@
$index = Bevy::Entity.current.index
def test_func
rust_func($index)
end

View file

@ -0,0 +1,2 @@
mark_called
raise

View file

@ -0,0 +1,3 @@
def test_func
rust_func(Bevy::Entity.current)
end

View file

@ -0,0 +1,3 @@
def test_func
rust_func(Bevy::Vec3.new(1.5, 2.5, -3.5))
end

View file

@ -0,0 +1,7 @@
def test_func(vec3)
raise unless vec3.is_a?(Bevy::Vec3)
raise unless vec3.x == 1.5
raise unless vec3.y == 2.5
raise unless vec3.z == -3.5
mark_success
end

View file

@ -0,0 +1,5 @@
def test_func
rust_func.and_then do |x|
raise
end
end

View file

@ -0,0 +1,9 @@
$state = {
'x' => nil
}
def test_func
rust_func.and_then do |x|
$state['x'] = x
end
end

View file

@ -0,0 +1,3 @@
def test_func
rust_func
end

View file

@ -0,0 +1,3 @@
def test_func
rust_func(5, 'test')
end

View file

@ -0,0 +1,3 @@
def test_func
rust_func(5)
end

View file

@ -0,0 +1,7 @@
$state = {
'times_called' => 0
}
def test_func
$state['times_called'] += 1
end

View file

@ -0,0 +1,9 @@
$state = {
'a_value' => nil,
'b_value' => nil
}
def test_func(a, b)
$state['a_value'] = a
$state['b_value'] = b
end

View file

@ -0,0 +1,7 @@
$state = {
'a_value' => nil
}
def test_func(a)
$state['a_value'] = a
end

View file

@ -0,0 +1,3 @@
def test_func
spawn_entity
end

Some files were not shown because too many files have changed in this diff Show more