luuuuuuaaaaaaaaah-ah-ah-ah
Available for Purchase at the YoYo Games Marketplace

LuaRousr is a GameMaker Studio 2 extension that integrates Lua with GameMaker Studio 2

Adding to your project

  • Import the LuaRousr Script Group, oLuaRousr Object, and extLuaRousr extension into your project.
  • Drop Lua files into your included folder
  • Optionally Use OutsideTheBox to load from anywhere (included)
    • Import outsideTheBox Script Group and outsideTheBox extension to your project
    • Call var fileIndex = oTB_fileOpen(filePath, oTB.Read) and oTB_fileClose(fileIndex) to open Lua files. You can use file_text_xxx functions.

Quick Start

  • Drop an instance of oLuaRousr into your room. (NOTE: It’s best to keep it persistent)
  • Execute Lua files with luaRousr_executeFile(filename)
  • Execute Lua strings with luaRousr_executeString(scriptstring)
  • Bind GML script with luaRousr_bindScript(lua_name, script_index, [optional]noreturns)
  • Bind/Unbind Instances with luaRousr_bindInstance(lua_name, instance_id)/luaRousr_unbindInstance(instance_id)
Note: LuaRousr uses Lua 5.3

The Demo

boingggggg

The demo was lovingly handcrafted, and provided by @net8floz

Download Here

Messin’ with the Demo

First and foremost: Use the F1-F4 keys so that you can experience the latest in Grid fashion.

The demo uses the 4 Lua files, game.lua, paddle.lua, ball.lua, and math.lua to pit two AI pong paddles against one another, each of these files can be edited WHILE the game is running to completely change the behavior of the game itself. Once you’ve edited the Lua files, press R and the Lua will reload. Hurray!

Working with the Demo Example (included with LuaRousr)

Lua files are expected to be right next to the .exe in this demo - though you don’t have to do it that way. To run the demo from GMS2 you must add the lua files to the executable path first.

This path is something like the following for VM (and something ridiculous on YYC):

1
C:\ProgramData\GMS2\Cache\runtimes\runtime-x-x-x\windows

Alternatively just put the Lua files on C:/lua (or anywhere else) and use that instead of get_executable_path

See: obj_game - Create for more instruction

Makes use of the spline from GMLscripts.com
A note about the included extension: OutsideTheBox is provided to allow loading the Lua files from the executable path, but you can use it in your own projects. (Read More)

API Reference

GML

luaRousr_bindScript(lua_name, script_index, user_data)

lua_name the string name to use in Lua to call the script.
script_index the gml script index of the script
user_data can be any arguments, will be passed along as the first arguments to the function

Call this for all of your GML script functions you’d like Lua to have access to.

Note: This must be done before you load your scripts that use the functions. Otherwise, it’sunreliable that they’ll be able to “see” your functions.

Note About Arguments: In Lua, you’re able to treat most anything like a variable and pass it to a function as an argument. At this time, LuaRousr doesn’t support accepting Lua tables as arguments. You may pass functions, however. If passing a function to GML, you must retain the callbackid in order to call it outside of the scope of the bound script function. See luaRousr_retainCallback and luaRousr_releaseCallback.


luaRousr_executeString(string)

string string of Lua script to execute

returns: returns a script_context_id

These functions execute Lua script, and return an id to associate with any threads that may have been started within this execution. See luaRousr_killThreads


luaRousr_executeFile(filename)

string filename of Lua script to execute

returns: returns a script_context_id

Literally file_text_reads the file into a string, and calls luaRousr_executeString


luaRousr_call(function_name or callbackId, arguments...)

function_name: the string name in Lua of the function
callbackId: the `callbackId’ of a function passed from Lua
arguments: any number of arguments to pass to the lua functoin

returns: the value Lua returns.

Call a Lua function with the given name, that simple!
NOTE: Returns are not functioning in the current version! No returns as of yet.


luaRousr_killThreads(contextId)

contextId: (optional, default = -1.0) kill all threads created on the given contextId, or -1.0 for all threads

The contextId is returned by luaRousr_executeString or luaRousr_call. This value is associated with any Lua threads created during the luaRousr_executeString or luaRousr_call call.

When reloading scripts, it’s definitely a good idea to use this function to make sure you have no wild LuaThreads running around.


luaRousr_bindInstance(lua_name, instance_id)

lua_name the string name to use in Lua to represent the instance.
instance_id the instance to bind

Binds an instance to Lua using the given name. All the members can now be accessed i.e,:

1
2
inst.x = 10
print(inst.y)

NOTE: You must unbind an instance in its CleanUp event if it’s still bound at that point.


luaRousr_unbindInstance(instance_id)

instance_id the instance to stop binding to Lua

Removes an instance binding so that Lua no longer updates the GML instance.

Note: Any references to the Lua version of the object are still ‘valid’ in that they will act like a real instance… but they no longer are attached to the GML rendering them useless.


luaRousr_retainCallback(callbackId)

callbackId the id of the callback passed from Lua

Increments the reference count for a callbackId. As long as the reference count is above 0, the callback is kept in memory.

When a callbackId is passed to GML you must retain it during that step, as it begins with a reference count of 0. If you’re going to call it immediately, there’s no need to retain it.


luaRousr_releaseCallback(callbackId)

callbackId the id of the callback passed from Lua

Decrements the reference count for the callbackId. If it’s completely released (refcount = 0), it queues the callbackId for deletion. The next step called by oLuaRousr will release this callback from memory.


luaRousr_bindInstanceVariable(instance, varName, defaultVal, (optional) readOnly, (optional) forceType)

instance the instance to bind the custom member of
varName the instance variable name, also accessible in Lua as this.
defaultVal the default value, mainly to deduce type
readOnly (optional default=false) is it a readonly property
forceType (optional default=undefined) if set, force this as the type to use in Lua

Binds an instance variable to memberName in Lua. Once instance is bound in Lua, the variable is accessible by varName. In LuaRousr’s current version, this feature uses variable_instance_set and variable_instance_get to keep the value in sync with Lua, so varName must match the instance variable name.

Setting readOnly to true will result in a Lua error if the property is written to, and it won’t sync to GML.

The defaultVal is the default value assigned the the variable before the first sync, but it also is used to deduce the “type” memberVariable represents (i.e., string real bool). If you don’t want to pass a default value, you can set this to undefined but use the forceType parameter.


luaRousr_bindInstanceFunction(instance, memberName, script_index, userData)

instance the instance to bind this function to in Lua
memberName the name of the function attached to the instance
script_index the GML script resource to call
userData pass additional arguments, which will get passed to the function when its called (such as an index, or other user data)

Bind a GML script_index to a function to the Lua representation of instance. In Lua, it’s accessible with memeberName.

When this function is called, the first argument is always instance followed by everything passed as user data, followed by any arguments passed in Lua.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/// GML
///@desc test_member_function
var _id = argument0;
var _userDataString = argument1;
var _luaArg1 = argument2;
with (_id) {
show_debug_message(_userDataString + " " + _luaArg1); // "hello from Lua"
}
/// In Tester - Create
luaRousr_bindInstance("tester", id);
luaRousr_bindInstanceFunction(id, "testFunction", test_member_function, "hello");
/// Lua
local tester = GMLInstance("tester")
tester:testFunction("from Lua");
TIP: You can user data to pass another identifier, or perhaps an index intended for a switch statement. This way you can use one GML script resource for multiple calls.

NOT YET IMPLEMENTED luaRousr_bindObjectFunction(object, memberName, script_index, user_data)

object the object to bind this function to in Lua
memberName the name of the function attached to instances of object
script_index the GML script resource to call
user_data pass additional arguments, which will get passed to the function when its called (such as an index, or other user data)

Bind a GML script_index to a function to the Lua representation of all instances of object. In Lua, it’s accessible with memeberName.

When this function is called, the first argument is always the id of the instance followed by everything passed as user data, followed by any arguments passed in Lua.

Note: The object does not have to be bound to Lua.


A note on binding

Existing instances in Lua are not updated with new binds, so it’s never guaranteed you’ll be able to call functions or use member variables that are bound after you’ve already created a Lua instance of the GML resource. To avoid issues and confusion, bind everything as early as possible before any scripts are run.


Lua Functions

thread(function)

callbackId the id of the callback passed from Lua

returns: a thread object to hold on to.

A Lua Thread can be “yielded” to pause execution, resuming from the point of the yield the next time the Lua Thread is resumed. (LuaRousr automatically resumes threads once per step in the oLuaRousr object).

TIP This is useful for scripts that need to wait for things to occur, such as telling an NPC to move to a location, and then checking they’ve finished their move each step. i.e,:
1
2
3
4
5
6
7
local move_thread = thread(function()
local npc = GMLInstance("npc")
npc_move(npc, 200, 200)
while (npc_is_moving(npc)) do
thread.yield()
end
end)
Note: The npc_* functions are just for the example, and not actual API.
Note: We’re storing the returned thread in a local variable. This is to access it with later, but LuaRousr will also keep a Lua reference to the thread while it’s active to prevent it from being garbage collected.

thread.yield()

Yield tells a Lua Thread to stop executing, resuming from this point next step. See the explanation in thread()


toFunction(callbackId)

callbackId the id of the callback passed from Lua to GML and back to Lua

returns: function callbackId represents

If you’ve passed a function to GML, but want to pass it back to Lua and use it, toFunction returns the actual function from the numeric id.


string the text to show_debug_message

a debug, logging, helper function. calls show_debug_message in GML with the given text.


GMLInstance(instanceName)

instanceName the string name given to the instance when it was bound to Lua.

returns: a GMLInstance Lua object representing the named instance, or nil on error.

Retrieves a GML instance resource that was bound using luaRousr_bindInstance. Each GMLInstance Lua object created this way does point to the same GML resource.

Built-ins

All instance built-in GML variables are bound to GMLInstance and accessible as properties:

1
2
3
local inst = GMLInstance("tester")
inst.x = 10
print (inst.x)

Instance functions

Instance functions are not currently implemented, but if they were they’d be accessible:

1
2
3
4
5
local obstacles = GMLObject("obstacle")
local collisionId = tester:place(10, 10, obstacles)
if (collisionId ~= nil) then
local collided = GMLInstance(collisionId)
end


GMLSprite(spriteName)

spriteName the string name given to the sprite when it was bound to Lua.

returns: a GMLSprite Lua object representing the named sprite, or nil on error.

Retrieves a GML sprite resource that was bound using luaRousr_bindSprite. Each GMLSprite Lua object created this way does point to the same GML resource.

Sprite Functions

The following functions are bound to a GMLSprite and are equivalent to the sprite_xxx version in GML, passing the GMLSprite index as the sprite_index where appropriate:

  • get_width
  • get_height
  • exists
  • get_name
  • get_number
  • get_xoffset
  • get_yoffset
  • get_bbox_left
  • get_bbox_right
  • get_bbox_top
  • get_bbox_bottom
  • save
  • save_strip
  • set_cache_size
  • set_cache_size_ext
  • prefetch
  • flush
  • set_speed
  • get_speed_type
  • get_speed

GMLObject(objectName)

objectName the string name given to the object when it was bound to Lua.

returns: a GMLObject Lua object representing the named object, or nil on error.

Retrieves a GML object resource that was bound using luaRousr_bindObject. Each GMLObject Lua object created this way does point to the same GML resource.

Object Functions

The following functions are bound to a GMLObject and are equivalent to the object_xxx version in GML, passing the GMLObject index as the object_index where appropriate:

  • exists
  • get_name
  • get_sprite
  • get_solid
  • get_visible
  • get_persistent
  • get_mask
  • get_parent
  • get_physics
  • is_ancestor
  • set_mask
  • set_persistent
  • set_solid
  • set_sprite
  • set_visible

Credit

Please see LICENSE.md for license information
Extension itself is covered by YoYo Games’s EULA


Changelog

  • v0.8.0

    • API Changes:
      • Renamed luaRousr_killThread to luaRousr_killThreads to more accurately match its function
    • New Features:

      • Added luaRousr_setManualSync and luaRousr_updateInstance, luaRousr_updateObject, and luaRousr_updateSprite to go along with it.
        • Don’t automatically “luaRousr_update” this resource, instead, call for updates manually Note: Still updates if Lua side does
      • luaRousr_init returned, with thorough configuration options (see Doc)
        • Added support for limiting the members and functions bound for GML types for both performance optimization, and mod ‘security’
      • Custom Bindings - bind your instance variables to all instances of an object type, or just specific instances:
        • luaRousr_bindInstanceVariable(object_index or instance_id, memberName, defaultVal, readOnly)
        • luaRousr_bindInstanceFunction(object_index or instance_id, functionName, script_index)
        • luaRousr_bindObjectFunction(object_index, functionName, script_index)
      • Added support for passing functions as arguments to GML, i.e.,:

        1
        2
        3
        4
        -- Lua Code
        set_game_callback(function(str)
        print (str)
        end)
      • Added support for calling those functions, i.e.,:

        1
        2
        3
        4
        // GML code
        ///@func set_game_callback(_callbackId)
        var _callbackId = argument0; // got from the set_game_callback lua function
        luaRousr_callLua(callbackId, "hello");
      • luaRousr_retainCallback and luaRousr_releaseCallback for managing the memory

      • Bound GML Resources:

        • Bound Sprite as GMLSprite

          • All sprite_xxx functions are available as GMLSprite:xxx. i.e.,:
            1
            2
            3
            local spr_ball = GMLSprite("spr_ball")
            print (spr_ball:get_width()) -- prints sprite_get_width(spr_ball)
            sprite_get_width(spr_ball) -- also works
        • Bound Object as GMLObject

          • All ‘object_xxx’ functions are available as GMLObject:xxx see sprite for more information.
        • Passing GMLInstance, GMLSprite, or GMLObject will use their id on GML side
    • Fixes:
      • Fixed calling Lua from Lua called GML functions
        • Nesting Lua->Gml->Lua->Gml should now call functions correctly.
      • Fixed rousr_sync not actually being called after initial instantiation
  • v0.7.0RC
    • Excellent, superb demo by @net8floz
      • Added outsideTheBox (v0.9.3) to the demo
    • Fixed id not being bound to instances.
    • Fixed rebinding to the same named instance.
    • Added luaRousr_unbindInstance
    • Added GMLInstance
      • Integrated sol2 for dynamic member getter/setter
  • v0.6.1 - Fixed YYC compile
  • v0.6.0
    • rousr_sync_val system added
    • Added luaRousr_bindInstance(instance_id)
  • v0.5.1 - Added luaRousr_killThreads
  • v0.5.0
    • Initial Version
      • GML to DLL: Use Async Map to transfer data on a request basis
      • DLL Lua Integration using lua-intf

Upcoming

  • Add a print hook to redirect debug output as you see fit.
  • Lua “Language Features”
    • Ability to pass Instance to function as id (rather than inst.id)
    • Binding Custom Members to Instances
    • Add GMLInstance functions in Lua (i.e., inst:place_meeting(otherInst))
    • Bind Objects as GMLObject
    • Bind Sprites as ‘GMLSprites’
    • More GML API Binding…
  • Support for Multiple Lua States
    • Potentially running on threads independent of each other.
  • Optimizations
    • DLL-Based Peek to sleep the thread, rather than spin on a buffer_peek
    • Remove lua-intf and replace completely with sol2
  • Support for more platforms (MacOs, Linux first)

Known Issues (Expect fixes soon)

  • luaRousr_call does not return values or context IDs.
  • luaRousr_killThread(contextId) OR luaRousr_executeFile are having issues with contextIds.
  • layer = -1 disables rendering, currently syncing it is disabled.
  • If LuaRousr is not persistent, recreating it can sometimes cause issues.