Files
leonwww/flumi/Scripts/Utils/Lua/Timeout.gd
2025-08-04 17:04:35 +03:00

132 lines
3.7 KiB
GDScript

class_name LuaTimeoutManager
extends RefCounted
var active_timeouts: Dictionary = {}
var next_timeout_id: int = 1
class TimeoutInfo:
var id: int
var callback_ref: int
var vm: LuauVM
var timer: Timer
var timeout_manager: LuaTimeoutManager
func _init(timeout_id: int, cb_ref: int, lua_vm: LuauVM, manager: LuaTimeoutManager):
id = timeout_id
callback_ref = cb_ref
vm = lua_vm
timeout_manager = manager
func set_timeout_handler(vm: LuauVM, parent_node: Node) -> int:
vm.luaL_checktype(1, vm.LUA_TFUNCTION)
var delay_ms: int = vm.luaL_checkint(2)
var timeout_id = next_timeout_id
next_timeout_id += 1
# Store callback in isolated registry table
vm.lua_pushstring("GURT_TIMEOUTS")
vm.lua_rawget(vm.LUA_REGISTRYINDEX)
if vm.lua_isnil(-1):
vm.lua_pop(1)
vm.lua_newtable()
vm.lua_pushstring("GURT_TIMEOUTS")
vm.lua_pushvalue(-2)
vm.lua_rawset(vm.LUA_REGISTRYINDEX)
vm.lua_pushinteger(timeout_id)
vm.lua_pushvalue(1)
vm.lua_rawset(-3)
vm.lua_pop(1)
# Create timeout info
var timeout_info = TimeoutInfo.new(timeout_id, timeout_id, vm, self)
# Create and configure timer
var timer = Timer.new()
timer.wait_time = delay_ms / 1000.0
timer.one_shot = true
timer.timeout.connect(_on_timeout_triggered.bind(timeout_info))
timeout_info.timer = timer
active_timeouts[timeout_id] = timeout_info
# Add timer to scene tree
parent_node.add_child(timer)
timer.start()
vm.lua_pushinteger(timeout_id)
return 1
func clear_timeout_handler(vm: LuauVM) -> int:
var timeout_id: int = vm.luaL_checkint(1)
var timeout_info = active_timeouts.get(timeout_id, null)
if timeout_info:
# Stop and remove timer
if timeout_info.timer:
timeout_info.timer.stop()
timeout_info.timer.queue_free()
# Clean up callback reference
vm.lua_pushstring("GURT_TIMEOUTS")
vm.lua_rawget(vm.LUA_REGISTRYINDEX)
if not vm.lua_isnil(-1):
vm.lua_pushinteger(timeout_info.callback_ref)
vm.lua_pushnil()
vm.lua_rawset(-3)
vm.lua_pop(1)
# Remove from active timeouts
active_timeouts.erase(timeout_id)
return 0
func _on_timeout_triggered(timeout_info: TimeoutInfo) -> void:
if not active_timeouts.has(timeout_info.id):
return
# Execute the callback
timeout_info.vm.lua_pushstring("GURT_TIMEOUTS")
timeout_info.vm.lua_rawget(timeout_info.vm.LUA_REGISTRYINDEX)
timeout_info.vm.lua_pushinteger(timeout_info.callback_ref)
timeout_info.vm.lua_rawget(-2)
timeout_info.vm.lua_remove(-2)
if timeout_info.vm.lua_isfunction(-1):
if timeout_info.vm.lua_pcall(0, 0, 0) != timeout_info.vm.LUA_OK:
print("GURT ERROR in timeout callback: ", timeout_info.vm.lua_tostring(-1))
timeout_info.vm.lua_pop(1)
else:
timeout_info.vm.lua_pop(1)
# Clean up timeout
timeout_info.timer.queue_free()
timeout_info.vm.lua_pushstring("GURT_TIMEOUTS")
timeout_info.vm.lua_rawget(timeout_info.vm.LUA_REGISTRYINDEX)
if not timeout_info.vm.lua_isnil(-1):
timeout_info.vm.lua_pushinteger(timeout_info.callback_ref)
timeout_info.vm.lua_pushnil()
timeout_info.vm.lua_rawset(-3)
timeout_info.vm.lua_pop(1)
active_timeouts.erase(timeout_info.id)
func cleanup_all_timeouts():
# Clean up all active timeouts
for timeout_id in active_timeouts:
var timeout_info = active_timeouts[timeout_id]
if timeout_info.timer:
timeout_info.timer.stop()
timeout_info.timer.queue_free()
# Release Lua callback reference
if timeout_info.vm and timeout_info.callback_ref:
timeout_info.vm.lua_pushstring("GURT_TIMEOUTS")
timeout_info.vm.lua_rawget(timeout_info.vm.LUA_REGISTRYINDEX)
if not timeout_info.vm.lua_isnil(-1):
timeout_info.vm.lua_pushinteger(timeout_info.callback_ref)
timeout_info.vm.lua_pushnil()
timeout_info.vm.lua_rawset(-3)
timeout_info.vm.lua_pop(1)
active_timeouts.clear()