WeaselGames / godot_luaAPI

Godot LuaAPI
https://luaapi.weaselgames.info
Other
371 stars 28 forks source link

Add methods get_running_coroutine,set_hook and yield_state #114

Closed Trey2k closed 1 year ago

Trey2k commented 1 year ago

PRs #110 and #112 introduce some issues.

What they solved

Before there was no way to force a lua coroutine or state to stop executing. Meaning scripts could easily crash the application or etc.

The issue

The intent was that the lua state would be executing on a separate thread when these methods were called. However accessing or modifying the lua state on multiple threads is not safe.

The fix

Instead of having predefined hooks and helper methods as before. This PR adds the ability for the user to create hooks from gdScript. It also adds the ability to yield the lua state from within these hooks. Using yield_state. Aswell as retrieving the current running corouitne from the parent LuaAPI object via get_running_coroutine.

Example

extends Node2D

var lua: LuaAPI
var coroutine: LuaCoroutine

func _lua_hook(lua: LuaAPI, event: int, line: int):
    var co: LuaCoroutine = lua.get_running_coroutine()
    co.yield_state([1])

func _ready():
    lua = LuaAPI.new()
    # Despite the name, this is not like a OS coroutine. It is a coroutine.
    coroutine = lua.new_coroutine()
    coroutine.set_hook(_lua_hook, LuaAPI.HOOK_MASK_COUNT, 4)
    coroutine.load_string("
    while true do
        print('Hello world!')
    end
    ")

var yieldTime = 0
var timeSince = 0
var goodBye = false
func _process(delta):

    timeSince += delta
    # If the coroutine has finished executing or if not enough time has passed, do not resume the coroutine.
    if coroutine.is_done() || timeSince <= yieldTime:
        if !goodBye:
            lua.do_string("""
            for i = 0,10,1 do 
                print('Good Bye World!')
            end
            """)
            goodBye=true
        return
    goodBye=false
    # coroutine.resume will either return a LuaError or an Array.
    var ret = coroutine.resume()
    if ret is LuaError:
        print("ERROR %d: " % ret.type + ret.message)
        return
    # Assumes the user will always pass the number of seconds to pause the coroutine for.
    yieldTime = ret[0]
    timeSince = 0

CC @RadiantUwU