scummvm/engines/grim/lua/ltask.cpp
2022-01-14 09:01:44 +01:00

381 lines
9.3 KiB
C++

#define FORBIDDEN_SYMBOL_EXCEPTION_setjmp
#define FORBIDDEN_SYMBOL_EXCEPTION_longjmp
#include "engines/grim/lua/ltask.h"
#include "engines/grim/lua/lapi.h"
#include "engines/grim/lua/lauxlib.h"
#include "engines/grim/lua/lmem.h"
#include "engines/grim/lua/ldo.h"
#include "engines/grim/lua/lvm.h"
#include "engines/grim/grim.h"
#include "common/textconsole.h"
namespace Grim {
void lua_taskinit(lua_Task *task, lua_Task *next, StkId tbase, int results) {
task->executed = false;
task->next = next;
task->initBase = tbase;
task->initResults = results;
}
void lua_taskresume(lua_Task *task, Closure *closure, TProtoFunc *protofunc, StkId tbase) {
task->cl = closure;
task->tf = protofunc;
task->base = tbase;
task->pc = task->tf->code;
task->consts = task->tf->consts;
task->S = &lua_state->stack;
}
void start_script() {
lua_Object paramObj = lua_getparam(1);
lua_Type type = paramObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(paramObj));
if (paramObj == LUA_NOOBJECT || (type != LUA_T_CPROTO && type != LUA_T_PROTO)) {
return;
}
LState *state = luaM_new(LState);
lua_stateinit(state);
state->next = lua_state->next;
state->prev = lua_state;
if (state->next)
state->next->prev = state;
lua_state->next = state;
state->taskFunc.ttype = type;
state->taskFunc.value = Address(paramObj)->value;
int l = 2;
for (lua_Object object = lua_getparam(l++); object != LUA_NOOBJECT; object = lua_getparam(l++)) {
TObject ptr;
ptr.ttype = Address(object)->ttype;
ptr.value = Address(object)->value;
LState *tmpState = lua_state;
lua_state = state;
luaA_pushobject(&ptr);
lua_state = tmpState;
}
ttype(lua_state->stack.top) = LUA_T_TASK;
nvalue(lua_state->stack.top) = (float)state->id;
incr_top;
}
void next_script() {
lua_Object paramObj = lua_getparam(1);
lua_Type type = paramObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(paramObj));
if (paramObj == LUA_NOOBJECT || (type != LUA_T_TASK && type != LUA_T_NIL))
lua_error("Bad argument to next_script");
if (type == LUA_T_TASK) {
uint32 task = (uint32)nvalue(Address(paramObj));
LState *state;
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (state->id == task) {
if (state->next) {
ttype(lua_state->stack.top) = LUA_T_TASK;
nvalue(lua_state->stack.top) = (float)state->next->id;
incr_top;
} else
lua_pushnil();
return;
}
}
}
if (lua_rootState->next) {
ttype(lua_state->stack.top) = LUA_T_TASK;
nvalue(lua_state->stack.top) = (float)lua_rootState->next->id;
incr_top;
} else
lua_pushnil();
}
void stop_script() {
lua_Object paramObj = lua_getparam(1);
lua_Type type = paramObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(paramObj));
LState *state;
if (paramObj == LUA_NOOBJECT || (type != LUA_T_CPROTO && type != LUA_T_PROTO && type != LUA_T_TASK))
lua_error("Bad argument to stop_script");
if (type == LUA_T_TASK) {
uint32 task = (uint32)nvalue(Address(paramObj));
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (state->id == task)
break;
}
if (state) {
if (state != lua_state) {
lua_statedeinit(state);
luaM_free(state);
}
}
} else if (type == LUA_T_PROTO || type == LUA_T_CPROTO) {
for (state = lua_rootState->next; state != nullptr;) {
bool match;
if (type == LUA_T_PROTO) {
match = (state->taskFunc.ttype == type && tfvalue(&state->taskFunc) == tfvalue(Address(paramObj)));
} else {
match = (state->taskFunc.ttype == type && fvalue(&state->taskFunc) == fvalue(Address(paramObj)));
}
if (match && state != lua_state) {
LState *tmp = state->next;
lua_statedeinit(state);
luaM_free(state);
state = tmp;
} else {
state = state->next;
}
}
}
}
void identify_script() {
lua_Object paramObj = lua_getparam(1);
lua_Type type = paramObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(paramObj));
if (paramObj == LUA_NOOBJECT || type != LUA_T_TASK)
lua_error("Bad argument to identify_script");
uint32 task = (uint32)nvalue(Address(paramObj));
LState *state;
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (state->id == task) {
luaA_pushobject(&state->taskFunc);
return;
}
}
lua_pushnil();
}
void find_script() {
lua_Object paramObj = lua_getparam(1);
lua_Type type = paramObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(paramObj));
if (type != LUA_T_CPROTO && type != LUA_T_PROTO && type != LUA_T_TASK && type != LUA_T_NIL)
lua_error("Bad argument to find_script");
if (type == LUA_T_NIL) {
ttype(lua_state->stack.top) = LUA_T_TASK;
nvalue(lua_state->stack.top) = lua_state->id;
incr_top;
lua_pushnumber(1.0f);
return;
}
if (type == LUA_T_TASK) {
uint32 task = (uint32)nvalue(Address(paramObj));
LState *state;
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (state->id == task) {
lua_pushobject(paramObj);
lua_pushnumber(1.0f);
return;
}
}
} else if (type == LUA_T_PROTO || type == LUA_T_CPROTO) {
int task = -1, countTasks = 0;
bool match;
LState *state;
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (type == LUA_T_PROTO) {
match = (state->taskFunc.ttype == type && tfvalue(&state->taskFunc) == tfvalue(Address(paramObj)));
} else {
match = (state->taskFunc.ttype == type && fvalue(&state->taskFunc) == fvalue(Address(paramObj)));
}
if (match) {
task = state->id;
countTasks++;
}
}
if (countTasks) {
assert(task != -1);
ttype(lua_state->stack.top) = LUA_T_TASK;
nvalue(lua_state->stack.top) = (float)task;
incr_top;
lua_pushnumber((float)countTasks);
return;
}
}
lua_pushnil();
lua_pushnumber(0.0f);
}
void pause_script() {
lua_Object taskObj = lua_getparam(1);
lua_Type type = taskObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(taskObj));
if (type != LUA_T_TASK) {
lua_error("Bad argument to pause_script");
return;
}
uint32 task = (uint32)nvalue(Address(taskObj));
LState *state;
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (state->id == task) {
state->paused = true;
return;
}
}
}
void pause_scripts() {
LState *t;
lua_Object boolObj = lua_getparam(1);
bool p = false;
if (!lua_isnil(boolObj))
p = true;
for (t = lua_rootState->next; t != nullptr; t = t->next) {
if (lua_state != t) {
if (p) {
t->all_paused++;
} else {
t->all_paused = 1;
}
}
}
}
void unpause_script() {
lua_Object taskObj = lua_getparam(1);
lua_Type type = taskObj == LUA_NOOBJECT ? LUA_T_NIL : ttype(Address(taskObj));
if (type != LUA_T_TASK) {
lua_error("Bad argument to unpause_script");
return;
}
uint32 task = (uint32)nvalue(Address(taskObj));
LState *state;
for (state = lua_rootState->next; state != nullptr; state = state->next) {
if (state->id == task) {
state->paused = false;
return;
}
}
}
void unpause_scripts() {
LState *t;
lua_Object boolObj = lua_getparam(1);
bool p = false;
if (!lua_isnil(boolObj))
p = true;
for (t = lua_rootState->next; t != nullptr; t = t->next) {
if (lua_state != t) {
if (p) {
if (t->all_paused > 0)
t->all_paused--;
} else {
t->all_paused = 0;
}
}
}
}
void current_script() {
ttype(lua_state->stack.top) = LUA_T_TASK;
nvalue(lua_state->stack.top) = (float)lua_state->id;
incr_top;
}
void break_here() {}
void sleep_for() {
lua_Object msObj = lua_getparam(1);
if (lua_isnumber(msObj)) {
int ms = (int)lua_getnumber(msObj);
lua_state->sleepFor = ms;
}
}
void lua_runtasks() {
if (!lua_state || !lua_state->next) {
return;
}
// Mark all the states to be updated
LState *state = lua_state->next;
do {
if (state->sleepFor > 0) {
state->sleepFor -= g_grim->getFrameTime();
} else {
state->updated = false;
}
state = state->next;
} while (state);
// And run them
runtasks(lua_state);
}
void runtasks(LState *const rootState) {
lua_state = lua_state->next;
while (lua_state) {
LState *nextState = nullptr;
bool stillRunning;
if (!lua_state->all_paused && !lua_state->updated && !lua_state->paused) {
jmp_buf errorJmp;
lua_state->errorJmp = &errorJmp;
if (setjmp(errorJmp)) {
lua_Task *t, *m;
for (t = lua_state->task; t != nullptr;) {
m = t->next;
luaM_free(t);
t = m;
}
stillRunning = false;
lua_state->task = nullptr;
} else {
if (lua_state->task) {
stillRunning = luaD_call(lua_state->task->initBase, lua_state->task->initResults);
} else {
StkId base = lua_state->Cstack.base;
luaD_openstack((lua_state->stack.top - lua_state->stack.stack) - base);
set_normalized(lua_state->stack.stack + lua_state->Cstack.base, &lua_state->taskFunc);
stillRunning = luaD_call(base + 1, 255);
}
}
nextState = lua_state->next;
// The state returned. Delete it
if (!stillRunning) {
lua_statedeinit(lua_state);
luaM_free(lua_state);
} else {
lua_state->updated = true;
}
} else {
nextState = lua_state->next;
}
lua_state = nextState;
}
// Restore the value of lua_state to the main script
lua_state = rootState;
// Check for states that may have been created in this run.
LState *state = lua_state->next;
while (state) {
if (!state->all_paused && !state->paused && !state->updated) {
// New state! Run a new pass.
runtasks(rootState);
return;
}
state = state->next;
}
}
} // end of namespace Grim