import { lua, lauxlib, lualib, to_luastring } from "fengari"; const lua_resume = lua.lua_resume; const LUA_OK = lua.LUA_OK; const LUA_YIELD = lua.LUA_YIELD; import { BaseService } from "./BaseService.js"; export class ScriptSchedulerService extends BaseService { constructor() { super("ScriptSchedulerService"); this._scheduled = []; this._waiting = []; this._running = false; } // Add a script (generator function) to the scheduler schedule(scriptGen) { if (typeof scriptGen === "function") { const gen = scriptGen(); this._scheduled.push({ gen, wakeTime: 0 }); } else if (scriptGen && typeof scriptGen.next === "function") { this._scheduled.push({ gen: scriptGen, wakeTime: 0 }); } else { throw new Error("Script must be a generator or generator function"); } } // Add a Fengari coroutine (thread) to the scheduler scheduleFengariCoroutine(luaThread, luaState) { // luaThread: the coroutine (thread) object from Fengari // luaState: the Lua state (L) from Fengari console.debug("Scheduling Fengari coroutine"); this._scheduled.push({ fengari: true, thread: luaThread, state: luaState, wakeTime: 0 }); } // Main update loop, call this every frame with dt (delta time in seconds) step(dt) { const now = Date.now(); // Wake up any waiting scripts whose time has come for (let i = this._waiting.length - 1; i >= 0; i--) { const item = this._waiting[i]; if (now >= item.wakeTime) { this._scheduled.push(item); this._waiting.splice(i, 1); console.log("Woke up routine",i,"from waiting."); } } // Run scheduled scripts for (let i = this._scheduled.length - 1; i >= 0; i--) { const item = this._scheduled[i]; if (item.fengari) { // Fengari coroutine const L = item.state; const thread = item.thread; // Resume the coroutine const status = lua_resume(thread, L, 0); if (status === LUA_OK) { // Coroutine finished this._scheduled.splice(i, 1); console.log("Woke up routine",i,"from fengari, finished."); } else if (status === LUA_YIELD) { // Check for yield value (wait) // You must implement a way for Lua to yield with a wait time, e.g. coroutine.yield({wait=seconds}) // For now, we assume the top of the stack is a table with a 'wait' field const luaTable = lua.lua_tojsstring(L, -1); let waitSeconds = 0; try { // Try to parse the yielded value as JSON (if you yield a JSON string) const val = JSON.parse(luaTable); if (val && val.wait) waitSeconds = val.wait; } catch (e) { } if (waitSeconds > 0) { this._waiting.push({ ...item, wakeTime: now + waitSeconds * 1000 }); this._scheduled.splice(i, 1); } // Otherwise, just continue next frame } else { // Error or unknown status this._scheduled.splice(i, 1); } } else { // JavaScript generator const { value, done } = item.gen.next(); if (done) { this._scheduled.splice(i, 1); console.log("Woke up routine",i,"from JS generator, finished."); } else if (value && value.wait) { // Script yielded with {wait: seconds} const ms = value.wait * 1000; this._waiting.push({ gen: item.gen, wakeTime: now + ms }); this._scheduled.splice(i, 1); } } } } // Utility for scripts to yield for a certain time (in seconds) static wait(seconds) { return { wait: seconds }; } } // Example usage: // function* myScript() { // console.log("Script start"); // yield ScriptSchedulerService.wait(1); // console.log("1 second later"); // yield ScriptSchedulerService.wait(2); // console.log("2 more seconds later"); // } // const scheduler = new ScriptSchedulerService(); // scheduler.schedule(myScript); // setInterval(() => scheduler.step(1/60), 1000/60);