# reflection

Responsible for lua-scripting execution and handling of API access cross-states.
You can use this to also contact other lua_State instances, and create new ones.
Do note you are responsible for spinning up new states.


# Threading

As of currently, the thread isolation for lua_State is quite basic.
As an example, if we create a new lua_State with thread isolation:

local diff = os.clock()
local schedule_response = false

-- this section runs under its own thread thanks to task.*
task.add("test", function()
    if diff + 1 < os.clock() then
        diff = os.clock()
        if schedule_response then
            signal.fire(reflection.get("server"), "test", schedule_response)
            schedule_response = false
        end
    end
end)

-- this section when executed will be in-sync with which ever thread/lua_State called it.
-- therefore **this is not actually threaded here**.
-- so I recommend task deferring into task.* think operation above.
signal.add("test", "incoming", function(...)
    print("from main thread:", ...)
    signal.fire(reflection.get("server"), "test", "got data!")
    schedule_response = "hello world"
end)

Nothing is perfect here, so do expect some problems with this system.
Currently task.* only supports "think" operation, which runs every possible cycle.
Do note that some cfunctions may not work depending on how they are implemented.


# Functions

# reflection.is(name: string): boolean

reflection.is(name: string): boolean
  • Checks if the current execution is of a named lua_State

# reflection.get(name: string): lua_State?

reflection.get(name: string): lua_State?
  • Attempts to locate a lua_State

# reflection.current(): lua_State

reflection.current(): lua_State
  • Gets the current lua_State this is executed in

# reflection.all(): {[index: string]: lua_State}

reflection.all(): {[index: string]: lua_State}
  • Gets all instances of lua_State and their names

# reflection.execute(source: string, name: string, state?: lua_State): string?

reflection.execute(source: string, name: string, state?: lua_State): string?
  • Executes lua on a lua_State or the current active state its in

# reflection.compile(source: string, name: string): function | string

reflection.compile(source: string, name: string): function | string
  • Compiles lua on the the current active state its in

# reflection.open(name: string, threaded: boolean): lua_State

reflection.open(name: string, threaded: boolean): lua_State
  • Spawns a new lua_State instance or returns one if it already exists
  • By enabling threaded, will open the lua_State with its own runtime environment.
  • This can be accessed via task.* which is a signal specific for that state.
-- creates a new lua_State
local state = reflection.open("lua_State.magic")

-- execute lua right onto the stack
reflection.execute([[
    print("new lua_State boys!")
]], "test", state)

-- you can use basic stack manipulation here as well
print("\nprinting this lua_State's stuff")
reflection.stack(function(L)
    L:pushvalue(-10002)
    L:pushnil()
    while L:next(-2) do
        if L:isstring(-2) then
            print(L:getstring(-2))
        end
        L:pop()
    end
end, state)

-- if you really don't need it, you can close it
reflection.close(state)

# reflection.close(state: lua_State)

reflection.close(state: lua_State)
  • Destroys a lua_State instance
  • This won't work on games with built-in lua_State instances

# reflection.stack(func: function(L: lua_State), state?: lua_State): any...

reflection.stack(func: function(L: lua_State), state?: lua_State): any...
  • Creates a stack isolated bridge to a lua_State.
  • Do note that you don't need to do this but using this reduces the risk of overflows.
-- this is in menu-state or another lua-state
local other_state = reflection.get("other_state")

local res = reflection.stack(function(L)
    L:pushnumber(20)
    local num = L:tonumber(-1)
    L:pop()
    return num
end, other_state)

print(res) -- 20

-- you can also do it outside the stack isolation if needed:
L:pushnumber(20)
local res = L:tonumber(-1)
L:pop()

print(res) -- 20

# Events

Events that can be accessed under reflection.listener

# open(name: string, state: lua.state)

open(name: string, state: lua.state)
  • Called when a lua_State has been created.
  • Do note that during the event, lua_State has not been fully initialized.
  • This can result in undefined behavior due to invalid objects, such as no _G being available.
  • We recommend you defer a task before running anything.

# close(name: string, state: lua.state)

close(name: string, state: lua.state)
  • Called just before a lua_State is destroyed.
  • Typically managed by C itself on when a lua_State should be destroyed.

# Lua State

These are interface & function definitions for how we handle lua_State

# L:execute(source: string, name: string): string?

L:execute(source: string, name: string): string?
  • executes lua on a lua_State.

# L:compile(source: string, name: string): string?

L:compile(source: string, name: string): string?
  • Compiles lua on a lua_State.
  • Do note that doing this will push the function onto the stack, consider seeing more in CAPI.

# L:stack(func: function(L: lua_State)): any?

L:stack(func: function(L: lua_State)): any?
  • Creates a stack isolated bridge to a lua_State.
  • Do note that you don't need to do this but using this reduces the risk of overflows.

# L:name(): string

L:name(): string
  • Converts a lua_State to its named counterpart

# L:threaded(): boolean

L:threaded(): boolean
  • Checks if the lua_State is running under a threaded environment

# L:internal(): boolean

L:internal(): boolean
  • Checks if the lua_State is internally created
  • Internals prevent certain actions from being made, such as destruction

# L:root(): boolean

L:root(): boolean
  • Checks if the lua_State is a root of all other lua_State under it

# L:parent(): lua_State?

L:parent(): lua_State?
  • Gets the parented lua_State

# L:children(): lua_State[]

L:children(): lua_State[]
  • Gets all children associated with the lua_State

# L:api(): API

L:api(): API
  • Pushes the API onto the stack for access

# L:call(inputs: number, outputs: number)

L:call(inputs: number, outputs: number)
  • Calls a function thats pushed onto the stack
  • This will consume the no. inputs first, then execute the functions
L:compile("print(({...})[1])", "example")
L:pushstring("hello world")
L:call(1, 0) -- calls "example" with "hello world"

# L:pop(count?: number)

L:pop(count?: number)
  • Pops a value off of the stack at the top

# L:remove(index: number)

L:remove(index: number)
  • Removes a value at an index off of the stack

# L:length(index: number): number

L:length(index: number): number
  • Used to get the length of a value

# L:next(index: number): boolean

L:next(index: number): boolean
  • A pair-style iterator function for going through table
L:newtable() -- example

L:pushnil() -- push an invalid key to jump-start the iterator
while L:next(-2) do -- target table for key-grabbing
    -- this pushes 2 values onto the stack, -1: value, -2: key
    L:pop(); -- pop the value, keep the key so 'next' can keep going
end

L:pop() -- pop table off stack

# L:pushany(value: any)

L:pushany(value: any)
  • Transfers any datatype from your environment to the stack

# L:getany(index: number): any

L:getany(index: number): any
  • Transfers any datatype from the stack to your environment

# L:gettop(): number

L:gettop(): number
  • Gets the current size of the Lua stack
  • This is useful for vararg style handling

# L:gettype(index: number): number

L:gettype(index: number): number
  • Gets the type ID of a given value

# L:gettypename(index: number): string

L:gettypename(index: number): string
  • Gets the typename of a given value

# L:newtable()

L:newtable()
  • Generates a blank table and pushes it onto the stack

# L:newref(index: number): number

L:newref(index: number): number
  • Creates a reference link to a value
  • This will make the value immune to the garbage collector

# L:pushref(reference: number)

L:pushref(reference: number)
  • Pushes the referenced value back onto the stack

# L:rmref(reference: number)

L:rmref(reference: number)
  • Removes the referenced value from registry

# L:getupvalue(index: number, id: number): string

L:getupvalue(index: number, id: number): string
  • Gets the "upvalue" of a function at the top of the stack
  • Upvalues are values passed to a function not as a parameter, but by reference

# L:setupvalue(index: number, id: number): string

L:setupvalue(index: number, id: number): string
  • Sets the "upvalue" of a function at the top of the stack

# L:getfenv(index: number)

L:getfenv(index: number)
  • Grabs the ENV and pushes it onto the stack
  • You can use this to manipulate the environment

# L:setfenv(index: number)

L:setfenv(index: number)
  • Sets the ENV of a functions at the index
  • This consumes the table you provide it at the top of the stack
  • You can use this to manipulate the environment

# L:getmetatable(index: number): boolean

L:getmetatable(index: number): boolean
  • Grabs the metatable of a table or userdata, and pushes it onto the stack
  • The boolean is provided for if it fails to grab the metatable

# L:setmetatable(index: number)

L:setmetatable(index: number)
  • Sets the metatable of a table or userdata at the index
  • This consumes the table you provide it at the top of the stack

# L:getfield(index: number, key: string)

L:getfield(index: number, key: string)
  • Gets the value from a table and pushes it onto the stack

# L:setfield(index: number, key: string)

L:setfield(index: number, key: string)
  • Sets a value in a table by key
  • Index corresponds to where the table is on the stack

# L:gettable(index: number)

L:gettable(index: number)
  • Gets the value from a table and pushes it onto the stack
  • This will consume a value just before it to determine the key

# L:settable(index: number)

L:settable(index: number)
  • Sets a value in a table by the key at the top of the stack
  • The top of the stack is the key, and top-1 is the value

# L:rawget(index: number)

L:rawget(index: number)
  • Gets the value from a table and pushes it onto the stack
  • This will consume a value just before it to determine the key
  • Unlike gettable this will not invoke metatable callbacks

# L:rawset(index: number)

L:rawset(index: number)
  • Sets a value in a table by the key at the top of the stack
  • The top of the stack is the key, and top-1 is the value
  • Unlike settable this will not invoke metatable callbacks

# L:getboolean(index: number): boolean

L:getboolean(index: number): boolean
  • Attempts to get a boolean value from the Lua stack
  • If the type is invalid, an error will be thrown

# L:getnumber(index: number)

L:getnumber(index: number)
  • Attempts to get a number value from the Lua stack
  • If the type is invalid, an error will be thrown

# L:getstring(index: number)

L:getstring(index: number)
  • Attempts to get a string value from the Lua stack
  • If the type is invalid, an error will be thrown

# L:pushboolean(value: boolean)

L:pushboolean(value: boolean)
  • Pushes a boolean onto the stack

# L:pushnil()

L:pushnil()
  • Pushes a nil value onto the stack

# L:pushnumber(value: number)

L:pushnumber(value: number)
  • Pushes a number onto the stack

# L:pushstring(value: string)

L:pushstring(value: string)
  • Pushes a string onto the stack

# L:pushfunction(value: function)

L:pushfunction(value: function)
  • Pushes a function onto the stack

# L:pushtable(value: table)

L:pushtable(value: table)
  • Pushes a table onto the stack

# L:pushvalue(index: number)

L:pushvalue(index: number)
  • Makes a copy of a value at the index and pushes it to the top of the stack

# L:isboolean(index: number): boolean

L:isboolean(index: number): boolean
  • Checks if the value at index on the stack is a boolean

# L:iscfunction(index: number): boolean

L:iscfunction(index: number): boolean
  • Checks if the value at index on the stack is a cfunction

# L:isfunction(index: number): boolean

L:isfunction(index: number): boolean
  • Checks if the value at index on the stack is a function

# L:islightuserdata(index: number): boolean

L:islightuserdata(index: number): boolean
  • Checks if the value at index on the stack is a lightuserdata

# L:isnil(index: number): boolean

L:isnil(index: number): boolean
  • Checks if the value at index on the stack is nil

# L:isnumber(index: number): boolean

L:isnumber(index: number): boolean
  • Checks if the value at index on the stack is a number

# L:isstring(index: number): boolean

L:isstring(index: number): boolean
  • Checks if the value at index on the stack is a string

# L:istable(index: number): boolean

L:istable(index: number): boolean
  • Checks if the value at index on the stack is a table

# L:isthread(index: number): boolean

L:isthread(index: number): boolean
  • Checks if the value at index on the stack is a thread

# L:isuserdata(index: number): boolean

L:isuserdata(index: number): boolean
  • Checks if the value at index on the stack is a userdata

# L:istype(index: number, type: number): boolean

L:istype(index: number, type: number): boolean
  • Checks if the value matches a specific type ID