Compare commits

...

16 commits
v0.6.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
112 changed files with 5522 additions and 305 deletions

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/

View file

@ -2,27 +2,48 @@ name: Rust
on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
env:
RUSTFLAGS: -D warnings
steps:
- uses: actions/checkout@v3
- name: Clippy
run: cargo clippy --all-features --verbose -- -D warnings
- name: Build
run: cargo build --all-features --verbose
- name: Run tests
run: cargo test --all-features --verbose
- name: Install cargo-examples
run: cargo install cargo-examples
- name: Run all examples
run: cargo examples --features=lua,rhai
- 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 --all-features --verbose -- -D warnings
- name: Build
run: cargo build --all-features --verbose
- name: Run tests
run: cargo test --all-features --verbose

2
.gitignore vendored
View file

@ -1,4 +1,4 @@
/target
/Cargo.lock
.vscode
rust-analyzer.json
.nvim.lua

View file

@ -1,109 +1,215 @@
[package]
name = "bevy_scriptum"
authors = ["Jaroslaw Konik <konikjar@gmail.com>"]
version = "0.6.0"
edition = "2021"
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 logic in a scripting language"
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", "rhai", "scripting", "game", "gamedev"]
keywords = ["bevy", "lua", "scripting", "game", "script"]
[features]
lua = ["mlua/luajit"]
lua = ["dep:mlua", "mlua/luajit"]
rhai = ["dep:rhai"]
ruby = ["dep:magnus", "dep:rb-sys"]
[dependencies]
bevy = { default-features = false, version = "0.14", 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"], optional = true }
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 }
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"

View file

@ -1,12 +1,19 @@
# bevy_scriptum 📜
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 [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are supported, but more languages may be added in the future.
![demo](demo.gif)
Everything you need to know to get started with using this library is contained in the
[bevy_scriptum book](https://jarkonik.github.io/bevy_scriptum/)
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
API docs are available in [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/)
| 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
@ -15,7 +22,7 @@ bevy_scriptum's main advantages include:
- 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
@ -91,7 +98,7 @@ Add the following to your `Cargo.toml`:
```toml
[dependencies]
bevy_scriptum = { version = "0.5", features = ["lua"] }
bevy_scriptum = { version = "0.9", features = ["lua"] }
```
or execute `cargo add bevy_scriptum --features lua` from your project directory.
@ -147,10 +154,6 @@ App::new()
You should then see `my_print: 'Hello world!'` printed in your console.
### Demo
![demo](demo.gif)
### Provided examples
You can also try running provided examples by cloning this repository and running `cargo run --example <example_name>_<language_name>`. For example:
@ -164,6 +167,8 @@ The examples live in `examples` directory and their corresponding scripts live i
| 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 |

View file

@ -7,7 +7,7 @@ currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 0.6 | :white_check_mark: |
| 0.9 | :white_check_mark: |
## Reporting a Vulnerability

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 @@
fn get_value() {
42
}

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,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()
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,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,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

1
book/.gitignore vendored
View file

@ -1,2 +1,3 @@
book
doctest_cache
target

2498
book/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

7
book/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "bevy_scriptum_book"
publish = false
edition = "2024"
[dependencies]
bevy_scriptum = { path = "../", features = ["ruby", "lua", "rhai"] }

View file

@ -4,9 +4,3 @@ language = "en"
multilingual = false
src = "src"
title = "bevy_scriptum"
[preprocessor.keeper]
command = "mdbook-keeper"
manifest_dir = "../"
externs = ["bevy", "bevy_scriptum"]
build_features = ["lua", "rhai"]

15
book/justfile Normal file
View file

@ -0,0 +1,15 @@
export CARGO_MANIFEST_DIR := `pwd`
build-deps:
cargo clean && cargo build
_test:
mdbook test -L target/debug/deps/
test: build-deps _test
test-watch: build-deps
watchexec --exts md -r just _test
serve:
mdbook serve

View file

@ -2,20 +2,29 @@
- [Introduction](./introduction.md)
- [Runtimes](./runtimes.md)
- [Lua](./lua/lua.md)
- [Installation](./lua/installation.md)
- [Hello World](./lua/hello_world.md)
- [Spawning scripts](./lua/spawning_scripts.md)
- [Calling Rust from Lua](./lua/calling_rust_from_script.md)
- [Calling Lua from Rust](./lua/calling_script_from_rust.md)
- [Interacting with bevy in callbacks](./lua/interacting_with_bevy.md)
- [Builtin types](./lua/builtin_types.md)
- [Builtin variables](./lua/builtin_variables.md)
- [Rhai](./rhai/rhai.md)
- [Installation](./rhai/installation.md)
- [Hello World(TBD)]()
- [Lua](./lua/lua.md)
- [Installation](./lua/installation.md)
- [Hello World](./lua/hello_world.md)
- [Spawning scripts](./lua/spawning_scripts.md)
- [Calling Rust from Lua](./lua/calling_rust_from_script.md)
- [Calling Lua from Rust](./lua/calling_script_from_rust.md)
- [Interacting with bevy in callbacks](./lua/interacting_with_bevy.md)
- [Builtin types](./lua/builtin_types.md)
- [Builtin variables](./lua/builtin_variables.md)
- [Ruby](./ruby/ruby.md)
- [Installation](./ruby/installation.md)
- [Hello World](./ruby/hello_world.md)
- [Spawning scripts](./ruby/spawning_scripts.md)
- [Calling Rust from Ruby](./ruby/calling_rust_from_script.md)
- [Calling Ruby from Rust](./ruby/calling_script_from_rust.md)
- [Interacting with bevy in callbacks](./ruby/interacting_with_bevy.md)
- [Builtin types](./ruby/builtin_types.md)
- [Rhai](./rhai/rhai.md)
- [Installation](./rhai/installation.md)
- [Hello World(TBD)]()
- [Multiple plugins](./multiple_plugins.md)
- [Multiple runtimes(TBD)]()
- [Implementing custom runtimes(TBD)]()
- [Workflow](./workflow/workflow.md)
- [Live-reload](./workflow/live_reload.md)
- [Live-reload](./workflow/live_reload.md)
- [Bevy support matrix](./bevy_support_matrix.md)

View file

@ -1,8 +1,11 @@
# Bevy support matrix
| bevy version | bevy_scriptum version |
|--------------|----------------------|
| 0.13 | 0.4-0.5 |
| 0.12 | 0.3 |
| 0.11 | 0.2 |
| 0.10 | 0.1 |
| ------------ | --------------------- |
| 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 |

View file

@ -1,21 +1,35 @@
# bevy_scriptum 📜
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 [Rhai](https://rhai.rs/) and [Lua](https://lua.org/) are 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.
API docs are available in [docs.rs](https://docs.rs/bevy_scriptum/latest/bevy_scriptum/)
## Supported scripting languages/runtimes
| 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 | `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 logic without having to recompile it.
All you need to do is register callbacks on your Bevy app like this:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -31,14 +45,20 @@ fn main() {
.run();
}
```
And you can call them in your scripts like this:
```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:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_ecs;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -64,7 +84,11 @@ fn main() {
```
You can also pass arguments to your callback functions, just like you would in a regular Bevy system - using `In` structs with tuples:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -83,7 +107,9 @@ fn main() {
.run();
}
```
which you can then call in your script like this:
```lua
fun_with_string_param("Hello world!")
```
@ -94,14 +120,17 @@ Add the following to your `Cargo.toml`:
```toml
[dependencies]
bevy_scriptum = { version = "0.6", features = ["lua"] }
bevy_scriptum = { version = "0.9", features = ["lua"] }
```
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
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -129,7 +158,10 @@ my_print("Hello world!")
And spawn an entity with attached `Script` component with a handle to a script source file:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -156,11 +188,12 @@ 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>_<language_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_lua
```
The examples live in `examples` directory and their corresponding scripts live in `assets/examples` directory within the repository.
### Promises - getting return values from scripts
@ -172,9 +205,13 @@ get_player_name():and_then(function(name)
print(name)
end)
```
which will print out `John` when used with following exposed function:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -186,7 +223,7 @@ fn main() {
runtime.add_function(String::from("get_player_name"), || String::from("John"));
});
}
````
```
## Access entity from script
@ -194,6 +231,7 @@ A variable called `entity` is automatically available to all scripts - it repres
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:
```lua
print("Current entity index: " .. entity.index)
```

0
book/src/lib.rs Normal file
View file

View file

@ -20,14 +20,17 @@ bevy_scriptum provides following types that can be used in Lua:
### Example Lua usage
```
```lua
my_vec = Vec3(1, 2, 3)
set_translation(entity, my_vec)
```
### Example Rust usage
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -69,7 +72,10 @@ pass_to_rust(entity)
### Example Rust usage
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;

View file

@ -3,7 +3,10 @@
To call a rust function from Lua first you need to register a function
within Rust using builder pattern.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -20,7 +23,10 @@ fn main() {
For example to register a function called `my_rust_func` you can do the following:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -49,7 +55,10 @@ that implements `FromLua`.
Since a registered callback function is a Bevy system, the parameters are passed
to it as `In` struct with tuple, which has to be the first parameter of the closure.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -68,7 +77,10 @@ fn main() {
To make it look nicer you can destructure the `In` struct.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -97,7 +109,10 @@ Any registered rust function that returns a value will retrurn a promise when
called within a script. By calling `:and_then` on the promise you can register
a callback that will receive the value returned from Rust function.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;

View file

@ -13,7 +13,10 @@ of the function to call, `LuaScriptData` that has been automatically
attached to entity after an entity with script attached has been spawned
and its script evaluated, the entity and optionally some arguments.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -29,14 +32,15 @@ fn call_lua_on_update_from_rust(
.unwrap();
}
}
fn main() {}
```
We can also pass some arguments by providing a tuple or `Vec` as the last
`call_fn` argument.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -51,8 +55,6 @@ fn call_lua_on_update_from_rust(
.unwrap();
}
}
fn main() {}
```
They will be passed to `on_update` Lua function

View file

@ -12,7 +12,10 @@ a create feature.
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
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -40,7 +43,10 @@ my_print("Hello world!")
And spawn an entity with attached `Script` component with a handle to a script source file:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;

View file

@ -4,8 +4,8 @@ Add the following to your `Cargo.toml`:
```toml
[dependencies]
bevy = "0.13"
bevy_scriptum = { version = "0.6", features = ["lua"] }
bevy = "0.16"
bevy_scriptum = { version = "0.9", features = ["lua"] }
```
If you need a different version of bevy you need to use a matching bevy_scriptum

View file

@ -7,7 +7,11 @@ That allows you to do anything you would do in a Bevy system.
You could for example create a callback system function that prints names
of all entities with `Player` component.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_ecs;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -42,7 +46,11 @@ You can use functions that interact with Bevy entities and resources and
take arguments at the same time. It could be used for example to mutate a
component.
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_ecs;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -59,7 +67,7 @@ fn main() {
runtime.add_function(
String::from("hurt_player"),
|In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| {
let mut player = players.single_mut();
let mut player = players.single_mut().unwrap();
player.health -= hit_value;
},
);

View file

@ -4,6 +4,9 @@ To spawn a Lua script you will need to get a handle to a script asset using
bevy's `AssetServer`.
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -13,8 +16,6 @@ fn my_spawner(mut commands: Commands, assets_server: Res<AssetServer>) {
assets_server.load("my_script.lua"),
));
}
fn main() {}
```
After they scripts have been evaled by bevy_scriptum, the entities that they've
@ -24,6 +25,9 @@ been attached to will get the `Script::<LuaScript>` component stripped and inste
So to query scipted entities you could do something like:
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -35,6 +39,4 @@ fn my_system(
// do something with scripted entities
}
}
fn main() {}
```

View file

@ -0,0 +1,34 @@
# Multiple plugins
It is possible to split the definition of your callback functions up over multiple plugins. This enables you to split up your code by subject and keep the main initialization light and clean.
This can be accomplished by using `add_scripting_api`. Be careful though, `add_scripting` has to be called before adding plugins.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
struct MyPlugin;
impl Plugin for MyPlugin {
fn build(&self, app: &mut App) {
app.add_scripting_api::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("hello_from_my_plugin"), || {
info!("Hello from MyPlugin");
});
});
}
}
// Main
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<LuaRuntime>(|_| {
// nice and clean
})
.add_plugins(MyPlugin)
.run();
}
```

View file

@ -4,8 +4,8 @@ Add the following to your `Cargo.toml`:
```toml
[dependencies]
bevy = "0.13"
bevy_scriptum = { version = "0.6", features = ["rhai"] }
bevy = "0.16"
bevy_scriptum = { version = "0.9", features = ["rhai"] }
```
If you need a different version of bevy you need to use a matching bevy_scriptum

View file

@ -0,0 +1,95 @@
# Builtin types
bevy_scriptum provides following types that can be used in Ruby:
- `Bevy::Vec3`
- `Bevy::Entity`
## Bevy::Vec3
### Class Methods
- `new(x, y, z)`
- `current`
### Instance Methods
- `x`
- `y`
- `z`
### Example Ruby usage
```ruby
my_vec = Bevy::Vec3.new(1, 2, 3)
set_translation(entity, my_vec)
```
### Example Rust usage
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("set_translation"), set_translation);
})
.run();
}
fn set_translation(
In((entity, translation)): In<(BevyEntity, BevyVec3)>,
mut entities: Query<&mut Transform>,
) {
let mut transform = entities.get_mut(entity.0).unwrap();
transform.translation = translation.0;
}
```
## Bevy::Entity
`Bevy::Entity.current` is currently not available within promise callbacks.
### Constructor
None - instances can only be acquired by using `Bevy::Entity.current`
### Class method
- `index`
### Example Ruby usage
```ruby
puts(Bevy::Entity.current.index)
pass_to_rust(Bevy::Entity.current)
```
### Example Rust usage
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("pass_to_rust"), |In((entity,)): In<(BevyEntity,)>| {
println!("pass_to_rust called with entity: {:?}", entity);
});
})
.run();
}
```

View file

@ -0,0 +1,133 @@
# Calling Rust from Ruby
To call a rust function from Ruby first you need to register a function
within Rust using builder pattern.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
// `runtime` is a builder that you can use to register functions
})
.run();
}
```
For example to register a function called `my_rust_func` you can do the following:
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("my_rust_func"), || {
println!("my_rust_func has been called");
});
})
.run();
}
```
After you do that the function will be available to Ruby code in your spawned scripts.
```ruby
my_rust_func
```
Since a registered callback function is a Bevy system, the parameters are passed
to it as `In` struct with tuple, which has to be the first parameter of the closure.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("func_with_params"), |args: In<(String, i64)>| {
println!("my_rust_func has been called with string {} and i64 {}", args.0.0, args.0.1);
});
})
.run();
}
```
To make it look nicer you can destructure the `In` struct.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("func_with_params"), |In((a, b)): In<(String, i64)>| {
println!("my_rust_func has been called with string {} and i64 {}", a, b);
});
})
.run();
}
```
The above function can be called from Ruby
```ruby
func_with_params("abc", 123)
```
## Return value via promise
Any registered rust function that returns a value will retrurn a promise when
called within a script. By calling `:and_then` on the promise you can register
a callback that will receive the value returned from Rust function.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("returns_value"), || {
123
});
})
.run();
}
```
```ruby
returns_value.and_then do |value|
puts(value) # 123
end
```

View file

@ -0,0 +1,66 @@
# Calling Ruby from Rust
To call a function defined in Ruby
```ruby
def on_update
end
```
We need to acquire `RubyRuntime` resource within a bevy system.
Then we will be able to call `call_fn` on it, providing the name
of the function to call, `RubyScriptData` that has been automatically
attached to entity after an entity with script attached has been spawned
and its script evaluated, the entity and optionally some arguments.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn call_ruby_on_update_from_rust(
mut scripted_entities: Query<(Entity, &mut RubyScriptData)>,
scripting_runtime: ResMut<RubyRuntime>,
) {
for (entity, mut script_data) in &mut scripted_entities {
// calling function named `on_update` defined in Ruby script
scripting_runtime
.call_fn("on_update", &mut script_data, entity, ())
.unwrap();
}
}
```
We can also pass some arguments by providing a tuple or `Vec` as the last
`call_fn` argument.
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn call_ruby_on_update_from_rust(
mut scripted_entities: Query<(Entity, &mut RubyScriptData)>,
scripting_runtime: ResMut<RubyRuntime>,
) {
for (entity, mut script_data) in &mut scripted_entities {
scripting_runtime
.call_fn("on_update", &mut script_data, entity, (123, String::from("hello")))
.unwrap();
}
}
```
They will be passed to `on_update` Ruby function
```ruby
def on_update(a, b)
puts(a) # 123
puts(b) # hello
end
```

View file

@ -0,0 +1,72 @@
# Hello World
After you are done installing the required crates, you can start developing
your first game or application using bevy_scriptum.
To start using the library you need to first import some structs and traits
with Rust `use` statements.
For convenience there is a main "prelude" module provided called
`bevy_scriptum::prelude` and a prelude for each runtime you have enabled as
a create feature.
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,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(
String::from("my_print"),
|In((x,)): In<(String,)>| {
println!("my_print: '{}'", x);
},
);
})
.run();
}
```
Then you can create a script file in `assets` directory called `script.rb` that calls this function:
```ruby
my_print("Hello world!")
```
And spawn an entity with attached `Script` component with a handle to a script source file:
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|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::<RubyScript>::new(asset_server.load("script.rb")));
})
.run();
}
```
You should then see `my_print: 'Hello world!'` printed in your console.

View file

@ -0,0 +1,52 @@
# Installation
Ruby is currently only supported on Linux.
## Ruby
To build `bevy_scriptum` with Ruby support a Ruby installation is needed to be
present on your development machine.
The easiest way to produce a compatible Ruby installation is to use [rbenv](https://rbenv.org/).
After installing `rbenv` along with its `ruby-build` plugin you can build and
install a Ruby installation that will work with `bevy_scriptum` by executing:
```sh
CC=clang rbenv install 3.4.4
```
Above assumes that you also have `clang` installed on your system.
For `clang` installation instruction consult your
OS vendor provided documentation or [clang official webiste](https://clang.llvm.org).
If you rather not use `rbenv` you are free to supply your own installation of
Ruby provided the following is true about it:
- it is compiled with `clang`
- it is compiled as a static library
- it is accessible as `ruby` within `PATH` or `RUBY` environment variable is set
to path of desired `ruby` binary.
## Main Library
Add the following to your `Cargo.toml`:
```toml
[dependencies]
bevy = "0.16"
bevy_scriptum = { version = "0.9", features = ["ruby"] }
```
If you need a different version of bevy you need to use a matching bevy_scriptum
version according to the [bevy support matrix](../bevy_support_matrix.md)
Ruby also needs dynamic symbol resolution and since `bevy_scriptum` links Ruby
statically the following `build.rs` file is needed to be present in project
root directory.
```rust
fn main() {
println!("cargo:rustc-link-arg=-rdynamic");
}
```

View file

@ -0,0 +1,83 @@
# Interacting with bevy in callbacks
Every registered function is also just a regular Bevy system.
That allows you to do anything you would do in a Bevy system.
You could for example create a callback system function that prints names
of all entities with `Player` component.
```rust,no_run
# extern crate bevy;
# extern crate bevy_ecs;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
#[derive(Component)]
struct Player;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(
String::from("print_player_names"),
|players: Query<&Name, With<Player>>| {
for player in &players {
println!("player name: {}", player);
}
},
);
})
.run();
}
```
In script:
```ruby
print_player_names
```
You can use functions that interact with Bevy entities and resources and
take arguments at the same time. It could be used for example to mutate a
component.
```rust,no_run
# extern crate bevy;
# extern crate bevy_ecs;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
#[derive(Component)]
struct Player {
health: i32
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(
String::from("hurt_player"),
|In((hit_value,)): In<(i32,)>, mut players: Query<&mut Player>| {
let mut player = players.single_mut().unwrap();
player.health -= hit_value;
},
);
})
.run();
}
```
And it could be called in script like:
```ruby
hurt_player(5)
```

4
book/src/ruby/ruby.md Normal file
View file

@ -0,0 +1,4 @@
# Ruby
This chapter demonstrates how to work with bevy_scriptum when using Ruby language runtime.
Ruby is currently only supported on Linux.

View file

@ -0,0 +1,42 @@
# Spawning scripts
To spawn a Ruby script you will need to get a handle to a script asset using
bevy's `AssetServer`.
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn my_spawner(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("my_script.rb"),
));
}
```
After they scripts have been evaled by bevy_scriptum, the entities that they've
been attached to will get the `Script::<RubyScript>` component stripped and instead
```RubyScriptData``` component will be attached.
So to query scipted entities you could do something like:
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn my_system(
mut scripted_entities: Query<(Entity, &mut RubyScriptData)>,
) {
for (entity, mut script_data) in &mut scripted_entities {
// do something with scripted entities
}
}
```

View file

@ -2,25 +2,25 @@
## Bevy included support
To enable life reload it should be enough to enable `file-watcher` feature
To enable live reload it should be enough to enable `file-watcher` feature
within bevy dependency in `Cargo.toml`
```
bevy = { version = "0.13", features = ["file_watcher"] }
```toml
bevy = { version = "0.16", features = ["file_watcher"] }
```
## Init-teardown pattern for game development
## Init-teardown pattern
It is useful to structure your game in a way that would allow making changes to
the scripting code without restarting the game.
It is useful to structure your application in a way that would allow making changes to
the scripting code without restarting the application.
A useful pattern is to hava three functions "init", "update" and "teardown".
- "init" function will take care of starting the game(spawning the player, the level etc)
- "init" function will take care of starting the application(spawning the player, the level etc)
- "update" function will run the main game logic
- "update" function will run the main application logic
- "teardown" function will despawn all the entities so game starts at fresh state.
- "teardown" function will despawn all the entities so application starts at fresh state.
This pattern is very easy to implement in bevy_scriptum. All you need is to define all needed functions
in script:
@ -35,7 +35,7 @@ local function init()
player.entity = spawn_player()
end
-- game logic here, should be called in a bevy system using call_fn
-- application logic here, should be called in a bevy system using call_fn
local function update()
(...)
end
@ -45,7 +45,7 @@ local function teardown()
despawn(player.entity)
end
-- call init to start the game, this will be called on each file-watcher script
-- call init to start the application, this will be called on each file-watcher script
-- reload
init()
```
@ -53,6 +53,9 @@ init()
The function calls can be implemented on Rust side the following way:
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -92,13 +95,14 @@ fn teardown(
}
}
}
fn main() {}
```
And to tie this all together we do the following:
```rust
```rust,no_run
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
@ -116,24 +120,25 @@ fn main() {
.run();
}
fn init() {} // Implemented elsewhere
fn update() {} // Implemented elsewhere
fn despawn() {} // Implemented elsewhere
fn teardown() {} // Implemented elsewhere
fn spawn_player() {} // Implemented elsewhere
# fn init() {}
# fn update() {}
# fn despawn() {}
# fn teardown() {}
# fn spawn_player() {}
```
`despawn` can be implemented as:
```rust
# extern crate bevy;
# extern crate bevy_scriptum;
use bevy::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
fn despawn(In((entity,)): In<(BevyEntity,)>, mut commands: Commands) {
commands.entity(entity.0).despawn();
}
fn main() {} // Implemented elsewhere
```
Implementation of `spawn_player` has been left out as an exercise for the reader.

8
build.rs Normal file
View file

@ -0,0 +1,8 @@
fn main() {
#[cfg(feature = "ruby")]
{
println!("cargo:rustc-link-arg=-rdynamic");
println!("cargo:rustc-link-arg=-lz");
println!("cargo:rustc-link-lib=z");
}
}

View file

@ -4,22 +4,12 @@ use bevy_scriptum::runtimes::lua::prelude::*;
fn main() {
App::new()
// This is just needed for headless console app, not needed for a regular bevy game
// that uses a winit window
.set_runner(move |mut app: App| {
loop {
app.update();
if let Some(exit) = app.should_exit() {
return exit;
}
}
})
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, call_lua_on_update_from_rust)
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
exit.send(AppExit::Success);
exit.write(AppExit::Success);
});
})
.run();

View file

@ -0,0 +1,42 @@
use bevy::{app::AppExit, prelude::*};
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, call_lua_on_update_from_rust)
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
exit.write(AppExit::Success);
});
})
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<LuaScript>::new(
assets_server.load("examples/lua/function_return_value.lua"),
));
}
fn call_lua_on_update_from_rust(
mut scripted_entities: Query<(Entity, &mut LuaScriptData)>,
scripting_runtime: ResMut<LuaRuntime>,
mut exit: EventWriter<AppExit>,
) {
for (entity, mut script_data) in &mut scripted_entities {
let val = scripting_runtime
.call_fn("get_value", &mut script_data, entity, ())
.unwrap()
.0;
scripting_runtime.with_engine(|engine| {
println!(
"script returned: {}",
engine.registry_value::<mlua::Integer>(&val).unwrap()
);
});
exit.write(AppExit::Success);
}
}

View file

@ -0,0 +1,67 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::lua::prelude::*;
// Plugin A
struct PluginA;
impl Plugin for PluginA {
fn build(&self, app: &mut App) {
app.add_scripting_api::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("hello_from_plugin_a"), || {
info!("Hello from Plugin A");
});
})
.add_systems(Startup, plugin_a_startup);
}
}
fn plugin_a_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<LuaScript>::new(
assets_server.load("examples/lua/multiple_plugins_plugin_a.lua"),
));
}
// Plugin B
struct PluginB;
impl Plugin for PluginB {
fn build(&self, app: &mut App) {
app.add_scripting_api::<LuaRuntime>(|runtime| {
runtime.add_function(
String::from("hello_from_plugin_b_with_parameters"),
hello_from_b,
);
})
.add_systems(Startup, plugin_b_startup);
}
}
fn plugin_b_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<LuaScript>::new(
assets_server.load("examples/lua/multiple_plugins_plugin_b.lua"),
));
}
fn hello_from_b(In((text, x)): In<(String, i32)>) {
info!("{} from Plugin B: {}", text, x);
}
// Main
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<LuaRuntime>(|runtime| {
runtime.add_function(String::from("hello_bevy"), || {
info!("hello bevy, called from script");
});
})
.add_systems(Startup, main_startup)
.add_plugins(PluginA)
.add_plugins(PluginB)
.run();
}
fn main_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<LuaScript>::new(
assets_server.load("examples/lua/hello_world.lua"),
));
}

View file

@ -11,7 +11,7 @@ fn main() {
.add_scripting::<LuaRuntime>(|builder| {
builder.add_function(
String::from("get_player_name"),
|player_names: Query<&Name, With<Player>>| player_names.single().to_string(),
|player_names: Query<&Name, With<Player>>| player_names.single().expect("Missing player_names").to_string(),
);
})
.add_systems(Startup, startup)

View file

@ -4,7 +4,7 @@ use bevy_scriptum::runtimes::lua::prelude::*;
fn main() {
App::new()
// This is just needed for headless console app, not needed for a regular bevy game
// This is just needed for headless console app, not needed for a regular bevy application
// that uses a winit window
.set_runner(move |mut app: App| {
loop {
@ -38,6 +38,6 @@ fn print_entity_names_and_quit(query: Query<&Name>, mut exit: EventWriter<AppExi
for e in &query {
println!("{}", e);
}
exit.send(AppExit::Success);
exit.write(AppExit::Success);
}
}

View file

@ -4,22 +4,12 @@ use bevy_scriptum::runtimes::rhai::prelude::*;
fn main() {
App::new()
// This is just needed for headless console app, not needed for a regular bevy game
// that uses a winit window
.set_runner(move |mut app: App| {
loop {
app.update();
if let Some(exit) = app.should_exit() {
return exit;
}
}
})
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, call_rhai_on_update_from_rust)
.add_scripting::<RhaiRuntime>(|runtime| {
runtime.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
exit.send(AppExit::Success);
exit.write(AppExit::Success);
});
})
.run();

View file

@ -0,0 +1,37 @@
use bevy::{app::AppExit, prelude::*};
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::rhai::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, call_rhai_on_update_from_rust)
.add_scripting::<RhaiRuntime>(|runtime| {
runtime.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
exit.write(AppExit::Success);
});
})
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RhaiScript>::new(
assets_server.load("examples/rhai/function_return_value.rhai"),
));
}
fn call_rhai_on_update_from_rust(
mut scripted_entities: Query<(Entity, &mut RhaiScriptData)>,
scripting_runtime: ResMut<RhaiRuntime>,
mut exit: EventWriter<AppExit>,
) {
for (entity, mut script_data) in &mut scripted_entities {
let val = scripting_runtime
.call_fn("get_value", &mut script_data, entity, ())
.unwrap()
.0;
println!("script returned: {}", val);
exit.write(AppExit::Success);
}
}

View file

@ -0,0 +1,68 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::rhai::prelude::*;
use rhai::ImmutableString;
// Plugin A
struct PluginA;
impl Plugin for PluginA {
fn build(&self, app: &mut App) {
app.add_scripting_api::<RhaiRuntime>(|runtime| {
runtime.add_function(String::from("hello_from_plugin_a"), || {
info!("Hello from Plugin A");
});
})
.add_systems(Startup, plugin_a_startup);
}
}
fn plugin_a_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RhaiScript>::new(
assets_server.load("examples/rhai/multiple_plugins_plugin_a.rhai"),
));
}
// Plugin B
struct PluginB;
impl Plugin for PluginB {
fn build(&self, app: &mut App) {
app.add_scripting_api::<RhaiRuntime>(|runtime| {
runtime.add_function(
String::from("hello_from_plugin_b_with_parameters"),
hello_from_b,
);
})
.add_systems(Startup, plugin_b_startup);
}
}
fn plugin_b_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RhaiScript>::new(
assets_server.load("examples/rhai/multiple_plugins_plugin_b.rhai"),
));
}
fn hello_from_b(In((text, x)): In<(ImmutableString, i64)>) {
info!("{} from Plugin B: {}", text, x);
}
// Main
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RhaiRuntime>(|runtime| {
runtime.add_function(String::from("hello_bevy"), || {
info!("hello bevy, called from script");
});
})
.add_systems(Startup, main_startup)
.add_plugins(PluginA)
.add_plugins(PluginB)
.run();
}
fn main_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RhaiScript>::new(
assets_server.load("examples/rhai/hello_world.rhai"),
));
}

View file

@ -11,7 +11,7 @@ fn main() {
.add_scripting::<RhaiRuntime>(|builder| {
builder.add_function(
String::from("get_player_name"),
|player_names: Query<&Name, With<Player>>| player_names.single().to_string(),
|player_names: Query<&Name, With<Player>>| player_names.single().expect("Missing player_names").to_string(),
);
})
.add_systems(Startup, startup)

View file

@ -4,12 +4,14 @@ use bevy_scriptum::runtimes::rhai::prelude::*;
fn main() {
App::new()
// This is just needed for headless console app, not needed for a regular bevy game
// This is just needed for headless console app, not needed for a regular bevy application
// that uses a winit window
.set_runner(move |mut app: App| loop {
app.update();
if let Some(exit) = app.should_exit() {
return exit;
.set_runner(move |mut app: App| {
loop {
app.update();
if let Some(exit) = app.should_exit() {
return exit;
}
}
})
.add_plugins(DefaultPlugins)
@ -36,6 +38,6 @@ fn print_entity_names_and_quit(query: Query<&Name>, mut exit: EventWriter<AppExi
for e in &query {
println!("{}", e);
}
exit.send(AppExit::Success);
exit.write(AppExit::Success);
}
}

View file

@ -0,0 +1,33 @@
use bevy::{app::AppExit, prelude::*};
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, call_ruby_on_update_from_rust)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
exit.write(AppExit::Success);
});
})
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/call_function_from_rust.rb"),
));
}
fn call_ruby_on_update_from_rust(
mut scripted_entities: Query<(Entity, &mut RubyScriptData)>,
scripting_runtime: ResMut<RubyRuntime>,
) {
for (entity, mut script_data) in &mut scripted_entities {
scripting_runtime
.call_fn("on_update", &mut script_data, entity, ())
.unwrap();
}
}

View file

@ -0,0 +1,25 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(
String::from("get_name"),
|In((BevyEntity(entity),)): In<(BevyEntity,)>, names: Query<&Name>| {
names.get(entity).unwrap().to_string()
},
);
})
.add_systems(Startup, startup)
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn((
Name::from("MyEntityName"),
Script::<RubyScript>::new(assets_server.load("examples/ruby/current_entity.rb")),
));
}

View file

@ -0,0 +1,54 @@
use bevy::prelude::*;
use bevy_scriptum::ScriptingError;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::magnus;
use bevy_scriptum::runtimes::ruby::magnus::Module as _;
use bevy_scriptum::runtimes::ruby::magnus::Object as _;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("hello_bevy"), || {
println!("hello bevy, called from script");
});
})
.add_systems(Startup, startup)
.run();
}
#[magnus::wrap(class = "MyType")]
struct MyType {
my_field: u32,
}
impl MyType {
fn new() -> Self {
Self { my_field: 42 }
}
fn my_method(&self) -> u32 {
self.my_field
}
}
fn startup(
mut commands: Commands,
scripting_runtime: ResMut<RubyRuntime>,
assets_server: Res<AssetServer>,
) {
scripting_runtime
.with_engine_send(|ruby| {
let my_type = ruby.define_class("MyType", ruby.class_object())?;
my_type.define_singleton_method("new", magnus::function!(MyType::new, 0))?;
my_type.define_method("my_method", magnus::method!(MyType::my_method, 0))?;
Ok::<(), ScriptingError>(())
})
.unwrap();
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/custom_type.rb"),
));
}

33
examples/ruby/ecs.rs Normal file
View file

@ -0,0 +1,33 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
#[derive(Component)]
struct Player;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(
String::from("print_player_names"),
|players: Query<&Name, With<Player>>| {
for player in &players {
println!("player name: {}", player);
}
},
);
})
.add_systems(Startup, startup)
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn((Player, Name::new("John")));
commands.spawn((Player, Name::new("Mary")));
commands.spawn((Player, Name::new("Alice")));
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/ecs.rb"),
));
}

View file

@ -0,0 +1,17 @@
use bevy::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
use bevy_scriptum::{prelude::*, BuildScriptingRuntime};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|_| {})
.add_systems(Startup, startup)
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/entity_variable.rb"),
));
}

View file

@ -0,0 +1,52 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::{RArray, prelude::*};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime
.add_function(String::from("fun_without_params"), || {
println!("called without params");
})
.add_function(
String::from("fun_with_string_param"),
|In((x,)): In<(String,)>| {
println!("called with string: '{}'", x);
},
)
.add_function(
String::from("fun_with_i64_param"),
|In((x,)): In<(i64,)>| {
println!("called with i64: {}", x);
},
)
.add_function(
String::from("fun_with_multiple_params"),
|In((x, y)): In<(i64, String)>| {
println!("called with i64: {} and string: '{}'", x, y);
},
)
.add_function(
String::from("fun_with_i64_and_array_param"),
|In((x, y)): In<(i64, RArray)>, runtime: Res<RubyRuntime>| {
runtime.with_engine_send(move |ruby| {
println!(
"called with i64: {} and dynamically typed array: {:?}",
x,
ruby.get_inner(y.0)
);
});
},
);
})
.add_systems(Startup, startup)
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/function_params.rb"),
));
}

View file

@ -0,0 +1,42 @@
use bevy::{app::AppExit, prelude::*};
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
use magnus::TryConvert;
use magnus::value::InnerValue;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, startup)
.add_systems(Update, call_lua_on_update_from_rust)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("quit"), |mut exit: EventWriter<AppExit>| {
exit.write(AppExit::Success);
});
})
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/function_return_value.rb"),
));
}
fn call_lua_on_update_from_rust(
mut scripted_entities: Query<(Entity, &mut RubyScriptData)>,
scripting_runtime: ResMut<RubyRuntime>,
mut exit: EventWriter<AppExit>,
) {
for (entity, mut script_data) in &mut scripted_entities {
let val = scripting_runtime
.call_fn("get_value", &mut script_data, entity, ())
.unwrap()
.0;
scripting_runtime.with_engine(|ruby| {
let val: i32 = TryConvert::try_convert(val.get_inner_with(ruby)).unwrap();
println!("script returned: {}", val);
});
exit.write(AppExit::Success);
}
}

View file

@ -0,0 +1,21 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("hello_bevy"), || {
println!("hello bevy, called from script");
});
})
.add_systems(Startup, startup)
.run();
}
fn startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/hello_world.rb"),
));
}

View file

@ -0,0 +1,67 @@
use bevy::prelude::*;
use bevy_scriptum::prelude::*;
use bevy_scriptum::runtimes::ruby::prelude::*;
// Plugin A
struct PluginA;
impl Plugin for PluginA {
fn build(&self, app: &mut App) {
app.add_scripting_api::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("hello_from_plugin_a"), || {
info!("Hello from Plugin A");
});
})
.add_systems(Startup, plugin_a_startup);
}
}
fn plugin_a_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/multiple_plugins_plugin_a.rb"),
));
}
// Plugin B
struct PluginB;
impl Plugin for PluginB {
fn build(&self, app: &mut App) {
app.add_scripting_api::<RubyRuntime>(|runtime| {
runtime.add_function(
String::from("hello_from_plugin_b_with_parameters"),
hello_from_b,
);
})
.add_systems(Startup, plugin_b_startup);
}
}
fn plugin_b_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/lua/multiple_plugins_plugin_b.lua"),
));
}
fn hello_from_b(In((text, x)): In<(String, i32)>) {
info!("{} from Plugin B: {}", text, x);
}
// Main
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_scripting::<RubyRuntime>(|runtime| {
runtime.add_function(String::from("hello_bevy"), || {
info!("hello bevy, called from script");
});
})
.add_systems(Startup, main_startup)
.add_plugins(PluginA)
.add_plugins(PluginB)
.run();
}
fn main_startup(mut commands: Commands, assets_server: Res<AssetServer>) {
commands.spawn(Script::<RubyScript>::new(
assets_server.load("examples/ruby/hello_world.rb"),
));
}

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