This is a function "cheat sheet" for Apollo extension by YellowAfterlife. The extension can be acquired from GM:Marketplace
or itch.io. For questions/support, use forums (itch.io,
GM forums),
or send me an email. A most up-to-date version of the manual is always available online. The extension is currently available for Windows, Mac (GMS2 only), and Linux.
This also causes newly made states to have IDs start at 0 again.
lua_state_reuse_indexes()➜count
Here's the deal: As you might know, reusing indexes can cause some really mysterious bugs
if your code accidentally continues to use an index (which is now taken up by something else).
So, by default, Apollo will not do that. Which, in turn, means that after states are destroyed,
4 bytes worth of data (which were onece the state's address) will continue to be reserved per state.
While it would take about 500 000 states to run out of memory this way,
you might prefer not to have that anyway.
So, calling this function will mark that memory as available again, causing Apollo to reuse the
destroyed indexes for newly created states. This will only affect indexes of states that are
destroyed as of calling the function.
In other words, while developing your project, you would not call this at all
(so that if you use a destroyed state, you get an obvious error), and for final release
version you would call it once in a while (such as during room transitions - to further
reduce the odds of anything going strange).
The function returns the number of indexes that will be reused as result.
If chdir is left at true, the function will automatically call lua_chdir when given a relative path so that the Lua code would work with files in that directory.
Returns the type of a state's global variable as a constant.
Possible values are as following:
lua_type_none: placeholder for type ID 0
lua_type_nil: an equivalent of GML's undefined. Not-yet-set values are nil.
lua_type_bool: a boolean value (true or false).
lua_type_number: a numeric type, same as GML's real.
lua_type_string: same as GML's string type.
lua_type_table: a Lua table (array or dictionary).
lua_type_function: a Lua function (that can be called via lua_call group).
lua_type_thread: a Lua "thread"/coroutine.
lua_type_userdata: an external reference (see Lua doc).
lua_type_lightuserdata: an external struct (see Lua doc).
lua_type_unknown: unrecognized type (never returns with normal Lua library)
If the state does not exist, an error is thrown.
if(lua_global_type(state,"test")==lua_type_function){lua_call(state,"test");}elseshow_debug_message("The state does not have a function called `test`!");
The last line with an empty lua_return_add is needed to return 0 values if loop matches no instances (as runtime would otherwise assume that you are going to return something with a regular return).
Sends an error message to the currently executing Lua state.
This should only be used inside scripts exposed via lua_add_function.
/// scr_variable_global_get(name)if(is_string(argument0)){returnvariable_global_get(argument0);}elselua_show_error("Expected a variable name to be a string.");
If you are building a medium-scale scripting API, you may find yourself needing to expose a large number of scripts (and/or built-in functions), as well as introducing argument type checking to prevent GML-side errors.
To save you from having to deal with that, Apollo includes a small utility that generates wrapper and loader scripts.
It accepts function definitions in funcname(arg1:type1, arg2:type2, ...):rtype,
arg1, arg2, ...: argument names. Will be shown in errors returned to Lua.
type1, type2, ...: argument types. Optional.
If defined, code will be added to ensure that each argument matches it's type.
Known types are real, bool, string;
color, int, index, id can also be used, but are treated same as real.
rtype: returned type, if the function returns a specific one.
If set to bool, return-statement will be wrapped in lua_bool call.
If prefixed with :, function will be marked as "instance function" and will accept an instance ID as first argument, also allowing to call it as inst.func(...) if instance access scripts are set up.
Constants can be defined either as name# (uses the value of same-named constant/variable) or name = value (computes the given GML value at inclusion time).
The tool is included with the extension as ApolloGen.exe;
A web-based version is available below:
Whenever the contents of above field are changed, updated loader script will be output into the field below:
You can then save them into a .gml file and import it to your project.
table ➜ array or struct Copied by-value recursively. If length (lua_rawlen) is >0,
the value becomes an array,
else it becomes a struct (2.3+)
or a 2d array of keys and values (pre-2.3)
As of Apollo V2, this function is equivalent to calling bool directly
and is only here for backwards compatibility.
A thing to remember: unlike GML, Lua is stricter about use of boolean types
(e.g. 1 + true is forbidden),
therefore you may want to cast your values to appropriate types before passing them to Lua.
lua_true or a lua_false read-only variables hold values
for boolean true and false accordingly - unlike the built-in true and false
constants, which currently remain to be equal to 1 and 0 accordingly.
Primarily intended for use with pre-2.3 versions of GM,
this function allows to hint a value so that upon handing it over to Lua
it would become a function reference (rather than staying a script index).
Returns a tiny array with metadata including the given script index.
This allows to pack up an array of functions, for example.
This function hints a GML array/struct to be passed to Lua by reference!
Passing by reference means that when the source array/struct is modified,
the changes will be reflected in any references to it automatically,
and that modifying the contents from Lua will reflect changes on GML side.
If the optional recursive argument is set to true, any sub-items pulled
from the array/struct will be passed to Lua by reference automatically
(good for nested arrays/structs).
Arrays
There are a few things to this:
To match Lua "array" behaviour more closely,
the array reference will be 1-indexed on Lua side.
So, if you wrote something into index 2 on GML side,
it would be accessible at index 3 on Lua side, and vice versa.
ipairs doesn't work on these due to __ipairs meta-function having been
removed in Lua 5.3+, but you can use pairs on them
(which will sequentially iterate from 1 to #arr),
or doing for i = 1, #arr directly.
Example:
lua_add_code(state,@'
function test(arr)
print(arr[1]) -- "one"
arr[2] = "hi!"
end
');arr[0]="one";arr[1]="two";lua_call(state,"test",lua_byref(arr));show_debug_message(arr[1]);// "hi!"
Structs (GMS≥2.3)
These behave pretty alike to Lua tables, but keys can only be strings.
Also accessing a non-existent struct variable from Lua will give you a nil
instead of throwing an error on GML side.
Example:
lua_add_code(state,@'
function test(o)
print(o.one) -- 1
print(o.miss) -- nil
o.two = "hi!"
end
');st={one:1,two:2};lua_call(state,"test",lua_byref(st));show_debug_message(st.two);// "hi!"
If you are using GameMaker Studio 2 or an non-ancient version of GameMaker: Studio,
you can have Lua directly read and write variables on GameMaker instances.
To do so, you would add three scripts to your project:
/// ref_variable_instance_get(context, name)varq=argument0,s=argument1;with(q)returnvariable_instance_get(id,s);if(q<100000){lua_show_error("Couldn't find any instances of "+string(q)+" ("+object_get_name(q)+")");}elselua_show_error("Couldn't find instance "+string(q));returnundefined;
(reads a variable from an instance),
/// ref_variable_instance_set(context, name, value)varq=argument0,s=argument1,v=argument2,n=0;with(q){variable_instance_set(id,s,v);n++;}if(n)exit;if(q<100000){lua_show_error("Couldn't find any instances of "+string(q)+" ("+object_get_name(q)+")");}elselua_show_error("Couldn't find instance "+string(q));
(writes a variable to an instance(s)),
/// ref_variable_instance_init(lua_state)varq=argument0;lua_add_function(q,"variable_instance_get",ref_variable_instance_get);lua_add_function(q,"variable_instance_set",ref_variable_instance_set);lua_add_code(q,@'-- ref_variable_instance_init()
__idfields = __idfields or { };
debug.setmetatable(0, {
__index = function(self, name)
if (__idfields[name]) then
return _G[name];
else
return variable_instance_get(self, name);
end
end,
__newindex = variable_instance_set,
})
');
(exposes the above scripts to a Lua state and sets it up to use them when trying to read/write a field on a numeric value (id)).
Then you can use them as following:
// create a Lua state:state=lua_state_create();// allow the state to work with GM instances:ref_variable_instance_init(state);// add a test function to the state -// function takes an instance and modifies it's `result` variable.lua_add_code(state,"function test(q) q.result = 'Hello!' end");// call the test-function for the current instance and display the result:result="";lua_call(state,"test",id);show_debug_message(result);
A coroutine, in short, is a function that can pause/resume execution at arbitrary points.
These can be used for iterators, cutscenes (pausing/resuming allows to write timing in an intuitive way),
tweening, AI, or anything else that benefits from maintaining the state across multi-call execution.
This set of functions enables you to use Lua coroutines in Apollo.
Creates a "thread" state for the given Lua state and returns it's ID.
Such "threads" share the global context (variables, functions, etc.) with their parent state, but have their own clal stack, meaning that they can do their own thing (namely, executing coroutines) while the parent state does something else.
Does not free resources of the parent state, only what was owned by the thread itself.
Is a convenience function and is interchangeable with lua_state_destroy.
Starts a coroutine call on the given sate, returns whether the operation succeeded.
Note that some functions will work oddly (or not work at all) on a state that is currently amidst the coroutine call, which is why you should generally create a thread for the coroutine call.
Executes the next iteration on the given state and returns whether the coroutine call is ongoing (as opposed to finishing or encountering a runtime error).
The general scheme of performing coroutine calls is thus as following:
If you have pre-existing GML code that you'd like to quickly tweak for use with Apollo,
I have also developed an online GML->Lua converter.
While automatic conversion won't make extensive use of Lua-specific language features, it produces functional code in vast majority of cases and the output is clean enough to tweak it manually if needed.
In case you'd like a custom version (such as to use LuaJIT, or to use a Lua fork with
different syntax), C++ source code is included - it should work with any Lua build
down to 5.1 or so.
The extension runs on Windows, Mac, and Linux - linked dynamically in all cases.
Apollo v2 beta currently only comes with a Windows (GMS1,GMS2) binary,
but you can compile it yourself (code is portable).
Mac may require additional tinkering (via install_name_tool - example), as library inclusion paths may vary depending on whether the game is running from IDE, whether YYC is enabled, and GMS version. If you are familiar with Mac extension development yourself, feel free to get in touch about better ways of handling this.
Internally, Apollo V2 uses a Lua C API function lua_yieldk
to switch back and forth between GML and Lua code.
So, when you ask to call a GML script, the current Lua state pauses,
writes arguments to a buffer, and returns execution to GML code.
GML code then reads the arguments from a buffer, calls the script in question,
writes the result(s) back into the buffer, and calls an extension function,
which reads them from a buffer, hands them to the Lua state, and resumes execution.
There is a little caveat to this: in rare cases
(most notably, inside __tostring, and ipairs)
Lua does not support pausing the state, and you will get the aforementioned error.
When this happens, you can tweak the code to either not access GML side of things
in the function itself (e.g. cache the result elsewhere)
or change the code so that it does not rely on those features
(e.g. add a separate toString function to your metatable or make a custom iterator).
Lua supports several additional reference types (such as Lua function references),
but these cannot be safely sent to GML as pointers as they are garbage-collected,
and thus may get recycled while still referenced on the GML side of things
(resulting in hard crash when trying to use the passed back value).
A good way to deal with this is to make a pair of lookup tables - since Lua allows table indexes to be of any type, you can do something like the following:
Which allow you to use ref.toid(some_reference) to return/create a numeric ID for a reference, ref.fromid(index) to convert one of those back to a reference, and ref.free(index_or_reference) to remove the lookup pairs (allowing Lua to safely recycle the reference when it is no longer used).