Skip to content
Ali Hussain (Coolsonickirby) edited this page Oct 2, 2023 · 5 revisions

Lua Managers

Lua Managers allow plugin developers to easily communicate back and forth between their plugins and lua scripts. You can use Lua Managers to add more functionality to menus or do something specific when in an item/assist/boss.

The Lua Stack

Before getting into creating functions and registering managers, we'll first need to talk about the Lua Stack.

The Lua Stack is used to transfer arguments from Lua to Rust and vice versa. It's important to understand how the stack works to properly parse and push arguments, as doing it improperly will lead to incorrect results (or possibly even crashing)

Pushing onto the stack (Returning stuff from Rust -> Lua)

Let's take a look at pushing onto the stack. Let's say I have a rust function which returns a string and an integer to the lua script. I push the string ("my custom string") and integer (12345) by using the respective push funtions associated with the lua state (push_string & push_integer), so the stack will look something like this:

- 12345
- "my custom string"

Every time you push an argument, the lua stack adds your argument, then goes up by one. This is important to note for pulling, but for pushing, the lua script can easily just store the arguments the same way it was passed. So if we sent the string then the integer, we can easily store them like this in the lua script:

local my_string, my_integer = MyManager.GetFunction()

Our Rust function will also have to return the amount of arguments pushed onto the stack (in this case, it'll return 2)

Pulling from the stack (Getting stuff from Lua -> Rust)

Pulling from the stack is a bit different than pushing. Let's say my lua script sends back an integer, string, and float. It could look something like this:

MyManager.SendFunction(1218, "I'm in the lua script!", 123.45)

The lua stack will look like this in our function:

- 123.45
- "I'm in the lua script!"
- 1218

To properly retrieve these values, we have to use the right "get" functions in the correct order starting from top to bottom. So our function will get the arguments like this:

let float = state.get_number_arg();
let text  = state.get_string_arg();
let int   = state.get_integer_arg();

We can then do whatever we want with the arguments in our rust function!

Creating Functions

To create a function that'll be used by the lua script, we just need to export a function in rust with the following signature:

extern "C" fn my_function(state: &mut lua_state) -> i32 {
    0 // The amount of arguments that'll be returned to the lua script
}

We just need to export a C function that takes in a lua_state and returns a signed 32-bit integer (that corresponds to the amount of arguments being returned to the lua script).

With that, we have the function completely set up. Onwards to registering them for actual use!

Registering functions

Registering functions with the ARCropolis API is very simple. There are two different functions that can be used for registering, these being the following:

  • add_lua_menu_manager -> only registers our functions for use on the menus (Main Menu, SSS, etc...)
  • add_lua_ingame_manager -> only registers our functions for use on the in-game stuff (Smash Mode (in the actual match), Training Mode, etc...)

The arguments for both functions are as follows:

  • name -> String (name of the manager)
  • functions -> Vec<LuaCfunction> (all functions assigned to that manager)

An example of adding a new menu function that does stuff would look something like this:

use arcropolis_api::{add_lua_menu_manager, lua_state, luaL_Reg};

extern "C" fn test_function(state: &mut lua_state) -> i32 {
    // ...
    0
}

#[skyline::main(name = "my_lua_plugin")]
pub fn main() {
    // This function adds the manager specifically for the menus
    add_lua_menu_manager("TestManager" /* Name of the manager */,
        vec![
            luaL_Reg {
                name: "test_function".to_string(), // Name used to call function
                func: Some(test_function), // Function that will be called
            }
        ]
    );
}

in your menu lua script, you can now call your function simply by doing:

TestManager.test_function()

To register functions for the in-game modes, you can just replace add_lua_menu_manager with add_lua_ingame_manager.

  • Notes
    • You can register the same manager name on both (menu and in-game) with no issues (from my limited testing)
    • Normally for menus, the Vector needs a std::ptr::null and None at the end, but ARCropolis automatically appends it
Clone this wiki locally