Files
bo3-js/js/instances/ScriptSchedulerService.js
usernames122 d9d5460856 Add launchable
2025-10-23 20:58:19 +02:00

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);