diff --git a/docs/docs/lua/utils.md b/docs/docs/lua/utils.md index 601cbbb..5360880 100644 --- a/docs/docs/lua/utils.md +++ b/docs/docs/lua/utils.md @@ -58,4 +58,17 @@ local clean = string.trim(messy) trace.log('"' .. clean .. '"') -- "Hello World" ``` -This is particularly useful for debugging and logging complex data structures. +### onNextFrame(callback) + +Schedules a function to execute on the next frame render cycle. This helps sync your code with Godot's render pipeline, eliminating stuff like flickering. + +```lua +local canvas = gurt.select("#my-canvas") +local ctx = canvas:withContext("2d") + +onNextFrame(function() + local x = math.random(0, canvas.width - 100) + local y = math.random(0, canvas.height - 100) + ctx:fillRect(x, y, 100, 100, "#ff0000") +end) +``` diff --git a/flumi/Scripts/Tags/canvas.gd b/flumi/Scripts/Tags/canvas.gd index 0952015..6c07eed 100644 --- a/flumi/Scripts/Tags/canvas.gd +++ b/flumi/Scripts/Tags/canvas.gd @@ -9,7 +9,7 @@ var draw_commands: Array = [] var context_2d: CanvasContext2D = null var context_shader: CanvasContextShader = null var pending_redraw: bool = false -var max_draw_commands: int = 1000 +var max_draw_commands: int = 10000 class CanvasContext2D: var canvas: HTMLCanvas diff --git a/flumi/Scripts/Utils/Lua/Canvas.gd b/flumi/Scripts/Utils/Lua/Canvas.gd index e41b0ea..588e7ef 100644 --- a/flumi/Scripts/Utils/Lua/Canvas.gd +++ b/flumi/Scripts/Utils/Lua/Canvas.gd @@ -9,16 +9,32 @@ static var batch_timer: SceneTreeTimer = null static func emit_canvas_operation(lua_api: LuaAPI, operation: Dictionary) -> void: var element_id = operation.get("element_id", "") - if not pending_operations.has(element_id): - pending_operations[element_id] = [] + # SUPER HACKY WAY TO FIX ODD ERRORS DESPITE EXISTING CHECKS + if element_id == "": + return - pending_operations[element_id].append(operation) + var safe_element_id = str(element_id) + var ops_array = pending_operations.get(safe_element_id, null) - if not batch_timer or batch_timer.time_left <= 0: + if ops_array == null or not (ops_array is Array): + ops_array = [] + pending_operations[safe_element_id] = ops_array + + ops_array.append(operation) + + var should_create_timer = false + if batch_timer == null or not is_instance_valid(batch_timer): + should_create_timer = true + else: + if batch_timer.time_left <= 0: + should_create_timer = true + + if should_create_timer: var scene_tree = lua_api.get_tree() if lua_api else Engine.get_main_loop() if scene_tree: batch_timer = scene_tree.create_timer(0.001) # 1ms batch window batch_timer.timeout.connect(_flush_pending_operations.bind(lua_api)) + # END HACKY WAY static func _flush_pending_operations(lua_api: LuaAPI) -> void: if not lua_api or not lua_api.is_inside_tree(): diff --git a/flumi/Scripts/Utils/Lua/ThreadedVM.gd b/flumi/Scripts/Utils/Lua/ThreadedVM.gd index 9724d0a..1e792b0 100644 --- a/flumi/Scripts/Utils/Lua/ThreadedVM.gd +++ b/flumi/Scripts/Utils/Lua/ThreadedVM.gd @@ -406,9 +406,41 @@ func _setup_additional_lua_apis(): LuaDownloadUtils.setup_download_api(lua_vm) LuaCrumbsUtils.setup_crumbs_api(lua_vm) LuaRegexUtils.setup_regex_api(lua_vm) + + lua_vm.lua_pushcallable(_onNextFrame_handler, "onNextFrame") + lua_vm.lua_setglobal("onNextFrame") LuaURLUtils.setup_url_api(lua_vm) Trace.setup_trace_api(lua_vm) +func _onNextFrame_handler(vm: LuauVM) -> int: + vm.luaL_checktype(1, vm.LUA_TFUNCTION) + + vm.lua_pushstring("THREADED_CALLBACKS") + vm.lua_rawget(vm.LUA_REGISTRYINDEX) + if vm.lua_isnil(-1): + vm.lua_pop(1) + vm.lua_newtable() + vm.lua_pushstring("THREADED_CALLBACKS") + vm.lua_pushvalue(-2) + vm.lua_rawset(vm.LUA_REGISTRYINDEX) + + var callback_ref = lua_api.next_callback_ref + lua_api.next_callback_ref += 1 + + vm.lua_pushinteger(callback_ref) + vm.lua_pushvalue(1) + vm.lua_rawset(-3) + vm.lua_pop(1) + + # Schedule callback execution on next frame + var scene_tree = Engine.get_main_loop() as SceneTree + if scene_tree: + var frame_callback = func(): + execute_callback_async(callback_ref, []) + scene_tree.process_frame.connect(frame_callback, CONNECT_ONE_SHOT) + + return 0 + func _table_tostring_handler(vm: LuauVM) -> int: vm.luaL_checktype(1, vm.LUA_TTABLE) var table_string = LuaPrintUtils.table_to_string(vm, 1) diff --git a/flumi/Scripts/Utils/Lua/WebSocket.gd b/flumi/Scripts/Utils/Lua/WebSocket.gd index fef2425..71dfc83 100644 --- a/flumi/Scripts/Utils/Lua/WebSocket.gd +++ b/flumi/Scripts/Utils/Lua/WebSocket.gd @@ -124,6 +124,8 @@ class WebSocketWrapper: # Call the function var result = vm.lua_pcall(1, 0, 0) if result != vm.LUA_OK: + var error_msg = vm.lua_tostring(-1) + print("WebSocket event error: ", error_msg) vm.lua_pop(1) else: vm.lua_pop(1) # Pop the non-function value diff --git a/tests/websocket.html b/tests/websocket.html index 6a1d513..9055a8e 100644 --- a/tests/websocket.html +++ b/tests/websocket.html @@ -114,17 +114,6 @@ addLog('WebSocket API demo ready') end) - messageInput:on('keypress', function(e) - if e.key == 'Enter' and socket and connected then - local message = messageInput.text - if message and message ~= '' then - socket:send(message) - addLog('📤 Sent: ' .. message) - messageInput.text = '' - end - end - end) - updateStatus('Disconnected', 'disconnected') addLog('WebSocket API demo ready') addLog('Enter a WebSocket URL and click Connect to start!')