Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Duckscript for games #89

Open
erlend-sh opened this issue Feb 23, 2020 · 9 comments
Open

Duckscript for games #89

erlend-sh opened this issue Feb 23, 2020 · 9 comments
Assignees

Comments

@erlend-sh
Copy link

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.

@sagiegurari
Copy link
Owner

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 :)

@smokku
Copy link

smokku commented Oct 5, 2021

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.

@sagiegurari
Copy link
Owner

@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
Copy link

smokku commented Oct 6, 2021

@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)

@sagiegurari
Copy link
Owner

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.

@smokku
Copy link

smokku commented Oct 7, 2021

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`

@sagiegurari
Copy link
Owner

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?

@smokku
Copy link

smokku commented Oct 10, 2021

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. :-)

@sagiegurari
Copy link
Owner

cool stuff. good luck :)

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

No branches or pull requests

3 participants