111 lines
4.0 KiB
JavaScript
111 lines
4.0 KiB
JavaScript
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);
|