Open erlend-sh opened 4 years ago
i never tried game dev before so got no experience there. currently I'm embedding this runtime into cargo-make but I'm sure it can be used anywhere that a single threaded (for now) scripting runtime can help.
I can try to look at the tutorial link you sent, but might take me a while.
thanks a lot for the advice :)
I am trying to embed duckscript in my game and the thing I find most difficult is passing external context to my custom commands.
It's great that a command is a struct and it could contain fields that the command can work on. So i could create a multiple-access Rc<RefCell<>>
data structure and store it in command.
The issue is if I do not control the form of data I want to change and all i have is &mut
I cannot pass it to the Command
struct I want to implement.
@smokku you are setting the value as a member on the command struct itself? did i understand correctly? i actually never did something like that and didn't think about that path. instead, i use the context data itself so all commands can access it from there and the commands are also reusable (as in, using in other script context).
i embed duckscript inside cargo-make which provides additional command with flow info. this is how i insert that object to the data context: https://github.com/sagiegurari/cargo-make/blob/ee4e8b40319532079750d26c7d2415c0c6fbc306/src/lib/scriptengine/duck_script/mod.rs#L52
and this is how i use that data inside the command: https://github.com/sagiegurari/cargo-make/blob/ee4e8b40319532079750d26c7d2415c0c6fbc306/src/lib/scriptengine/duck_script/sdk/cm_run_task.rs#L50 and https://github.com/sagiegurari/cargo-make/blob/ee4e8b40319532079750d26c7d2415c0c6fbc306/src/lib/scriptengine/duck_script/sdk/mod.rs#L22
its a bit complex as its a struct and not some primitive, but thats why i split it to util and command using the util. so later on i can write (if needed) more commands that access that flowinfo struct.
in general, duckscript is more of a shell like scripting env, so never thought about using it for games and i'm not sure what requirements games have.
@smokku you are setting the value as a member on the command struct itself? did i understand correctly? i actually never did something like that and didn't think about that path.
Yes, I am doing exactly that: https://github.com/smokku/soldank/blob/ac20249/client/src/engine/script.rs#L54
instead, i use the context data itself so all commands can access it from there and the commands are also reusable (as in, using in other script context).
My use-case is I want to have commands interacting with outside state (the game state) - change configuration, spawn game objects etc..
Both my approach (Command field) and your approach (putting data in context) require taking ownership of provided data by the engine. In the above linked case I was able to wrap part of my game (the config data structure) in Rc
/RefCell
which rc
I can clone and pass into Duckscript internals.
The thing is I am not able to do this with every data structure I want to interact. Most of the time I have just a temporary &mut
reference provided, which I cannot store for later use (like in the duckscript context or inside Command).
For example, GameShell solves this by having a user provided command execution context which is then passed to executed commands. This way commands can interact with outside object without taking ownership.
https://docs.rs/gameshell/0.4.0/gameshell/#example (here the user provided data is just u8
but it can be any complex struct)
ok got it. so you want the ability to pass &mut into the context. that might require some changes in the runtime internally.... i'll have to investigate it a bit.
a really simple test case code would really help me to see that we can reach a good solution, if possible.
I've extracted my code to a bare example: https://github.com/smokku/duckscript4games/tree/master
As you can see i can use moveright
command to drive my player position 10 units of x:
> cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/duckscript4games`
[src/main.rs:27] state = RefCell {
value: GameState {
x: 10.0,
y: 0.0,
},
}
Now, let's suppose that we are not at liberty to wrap GameState
in Rc/RefCell
: https://github.com/smokku/duckscript4games/blob/ref_mut/src/main.rs
> cargo run
Compiling duckscript4games v0.1.0
error[E0621]: explicit lifetime required in the type of `state`
--> src/main.rs:32:26
|
32 | context.commands.set(Box::new(MoveRightCommand { state }))?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'static` required
error[E0599]: no method named `clone` found for struct `MoveRightCommand` in the current scope
--> src/main.rs:48:26
|
16 | struct MoveRightCommand<'a> {
| --------------------------- method `clone` not found for this
...
48 | Box::new((*self).clone())
| ^^^^^ method not found in `MoveRightCommand<'_>`
|
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
sorry was a bit busy. correct me if I'm wrong but you can have only one mut borrow, so I'm not sure how you plan to have 2 command structs having the same mut borrow value.
why can't you give them rc with refcell which holds the value and clone that for eqch command? meaning, in what scenario this won't work?
I don't want to wrap the value in RefCell as I would need to .borrow_mut()
the value all around my codebase, which is annoying.
Inspired both by duckscript
and GameShell
I was able to roll my own scripting engine, which accomplishes this goal.
https://github.com/smokku/soldank/blob/3702ddb/client/src/engine/script.rs#L21
with example script: https://github.com/smokku/soldank/blob/3702ddb/client/resources/config.cfg
Thanks for your code and help. :-)
cool stuff. good luck :)
Duckscript seems like it could be very useful as a lightweight scripting language for game development. Quite a few games (Blizzard being a notable fan of this practice) have created their own scripting languages to be tailored perfectly to their game’s unique requirements. Duckscript’s extensibility accommodates this pursuit of niche functionality.
Are there any examples of Duckscript being used as a scripting language for a game?
(If wanted, I can point you to some examples of Rust games with scripting implementations like Lua, Python and JavaScript.)
As for what might make a good example, demonstrating Ducktype scripting in bracket-lib (formerly rltk_rs) would surely get added to the Rusty Roguelike Tutorial.
There’s a rough spec and POC available for ECS-friendly scripting, though Duckscript might have an easier way in with its embeddable qualities.