291 lines
6.9 KiB
C++
291 lines
6.9 KiB
C++
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "ltask.h"
|
|
#include "lauxlib.h"
|
|
#include "lmem.h"
|
|
#include "ldo.h"
|
|
#include "lvm.h"
|
|
|
|
void pause_scripts (void) {
|
|
struct lua_Task *t;
|
|
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if ((L->curr_task != t) && (t->Tstate != DONE))
|
|
t->Tstate = PAUSE;
|
|
}
|
|
}
|
|
|
|
void unpause_scripts (void) {
|
|
struct lua_Task *t;
|
|
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if ((L->curr_task != t) && (t->Tstate != DONE))
|
|
t->Tstate = YIELD;
|
|
}
|
|
}
|
|
|
|
void start_script (void) {
|
|
struct lua_Task *old_task = L->curr_task, *new_task;
|
|
TObject *f = L->stack.stack + L->Cstack.lua2C;
|
|
|
|
f = L->stack.stack + L->Cstack.lua2C;
|
|
if (ttype(f) == LUA_T_CLOSURE)
|
|
f = &clvalue(f)->consts[0];
|
|
// Start nothing? start_script gets called in this fashion
|
|
// by the scene in the scrimshaw parlor, if we just return
|
|
// immediately the game proceeds ok.
|
|
if (ttype(f) == LUA_T_NIL)
|
|
return;
|
|
if (ttype(f) != LUA_T_PROTO)
|
|
lua_error("can only start_script with a Lua function");
|
|
|
|
/* Create a new task with an empty stack */
|
|
new_task = luaI_newtask();
|
|
|
|
/* Put the function and arguments onto the new task's stack */
|
|
for (int32 i = 0; i < old_task->Cstack.num; i++) {
|
|
*(L->stack.top) = *(old_task->stack.stack + old_task->Cstack.lua2C + i);
|
|
incr_top;
|
|
}
|
|
|
|
/* Create a CallInfo frame */
|
|
luaD_precall(L->stack.stack, 1, MULT_RET);
|
|
ttype(L->stack.stack) = (ttype(L->stack.stack) == LUA_T_CLOSURE) ? LUA_T_CLMARK : LUA_T_PMARK;
|
|
|
|
/* Switch back to the old task */
|
|
L->Tstate = YIELD;
|
|
luaI_switchtask(old_task);
|
|
L->curr_task = old_task;
|
|
|
|
/* Insert new task at end of list */
|
|
L->last_task->next = new_task;
|
|
L->last_task = new_task;
|
|
|
|
/* Return task handle */
|
|
ttype(L->stack.top) = LUA_T_TASK;
|
|
nvalue(L->stack.top) = (real)new_task->id;
|
|
incr_top;
|
|
}
|
|
|
|
void stop_script (void) {
|
|
struct lua_Task *prev, *t;
|
|
TObject *f = L->stack.stack + L->Cstack.lua2C;
|
|
int32 match = 0;
|
|
|
|
if ((f == LUA_NOOBJECT) || (ttype(f) != LUA_T_CLOSURE && ttype(f) != LUA_T_PROTO && ttype(f) != LUA_T_TASK))
|
|
lua_error("Bad argument to stop_script");
|
|
|
|
prev = L->root_task;
|
|
while ((t = prev->next) != NULL) {
|
|
switch (ttype(f)) {
|
|
case LUA_T_CLOSURE:
|
|
match = (ttype(t->stack.stack) == LUA_T_CLMARK && clvalue(t->stack.stack) == clvalue(f));
|
|
break;
|
|
case LUA_T_PROTO:
|
|
match = (ttype(t->stack.stack) == LUA_T_PMARK && tfvalue(t->stack.stack) == tfvalue(f));
|
|
break;
|
|
case LUA_T_TASK:
|
|
match = (t->id == (int32)nvalue(f));
|
|
break;
|
|
default: /* Shut up gcc */
|
|
break;
|
|
}
|
|
|
|
if (match) {
|
|
prev->next = t->next; /* Remove from list of active tasks */
|
|
t->next = NULL;
|
|
if (prev->next == NULL) {
|
|
L->last_task = prev;
|
|
if (t == L->curr_task) {
|
|
L->Tstate = DONE;
|
|
}
|
|
} else {
|
|
t->Tstate = DONE;
|
|
}
|
|
} else {
|
|
prev = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
void next_script (void) {
|
|
struct lua_Task *t = NULL, *prev;
|
|
TObject *f = L->stack.stack + L->Cstack.lua2C;
|
|
|
|
if (f == LUA_NOOBJECT)
|
|
lua_error("Bad argument to next_script: no obeject");
|
|
|
|
prev = L->root_task;
|
|
if (ttype(f) == LUA_T_NIL) {
|
|
t = L->root_task;
|
|
} else if (ttype(f) == LUA_T_TASK) {
|
|
int32 taskId = (int32)nvalue(f);
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if (t->id == taskId)
|
|
break;
|
|
}
|
|
} else {
|
|
lua_error("Bad argument to next_script.");
|
|
}
|
|
if (t == NULL) {
|
|
lua_pushnil();
|
|
} else {
|
|
t = t->next;
|
|
if (t == NULL) {
|
|
lua_pushnil();
|
|
} else {
|
|
ttype(L->stack.top) = LUA_T_TASK;
|
|
nvalue(L->stack.top) = (real)t->id;
|
|
incr_top;
|
|
}
|
|
}
|
|
}
|
|
|
|
void identify_script (void) {
|
|
struct lua_Task *t;
|
|
TObject *f = L->stack.stack + L->Cstack.lua2C;
|
|
|
|
if ((f == LUA_NOOBJECT) || ttype(f) != LUA_T_TASK) {
|
|
lua_error("Bad argument to identify_script");
|
|
}
|
|
|
|
int32 taskId = (int32)nvalue(f);
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if (t->id == taskId)
|
|
break;
|
|
}
|
|
|
|
if ((t == NULL) || (t->Tstate == DONE)) {
|
|
ttype(L->stack.top) = LUA_T_NIL;
|
|
} else {
|
|
*L->stack.top = *t->stack.stack;
|
|
}
|
|
incr_top;
|
|
}
|
|
|
|
void find_script (void) {
|
|
struct lua_Task *t = NULL, *foundTask = NULL;
|
|
TObject *f = L->stack.stack + L->Cstack.lua2C;
|
|
int32 countTasks = 0, taskId;
|
|
|
|
switch (ttype(f)) {
|
|
case LUA_T_CLOSURE:
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if ((ttype(t->stack.stack) == LUA_T_CLOSURE || ttype(t->stack.stack) == LUA_T_CMARK) && clvalue(t->stack.stack) == clvalue(f)) {
|
|
foundTask = t;
|
|
countTasks++;
|
|
}
|
|
}
|
|
t = foundTask;
|
|
break;
|
|
case LUA_T_PROTO:
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if ((ttype(t->stack.stack) == LUA_T_PROTO || ttype(t->stack.stack) == LUA_T_PMARK) && tfvalue(t->stack.stack) == tfvalue(f)) {
|
|
foundTask = t;
|
|
countTasks++;
|
|
}
|
|
}
|
|
t = foundTask;
|
|
break;
|
|
case LUA_T_TASK:
|
|
taskId = (int32)nvalue(f);
|
|
for (t = L->root_task->next; t != NULL; t = t->next) {
|
|
if ((t->id == taskId) && (t->Tstate != DONE)) {
|
|
ttype(L->stack.top) = LUA_T_TASK;
|
|
nvalue(L->stack.top) = nvalue(f);
|
|
incr_top;
|
|
lua_pushnumber(1.0f);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
lua_error("Bad argument to find_script");
|
|
}
|
|
|
|
if (t != NULL) {
|
|
ttype(L->stack.top) = LUA_T_TASK;
|
|
nvalue(L->stack.top) = (real)t->id;
|
|
incr_top;
|
|
lua_pushnumber(countTasks);
|
|
} else {
|
|
lua_pushnil();
|
|
lua_pushnumber(0.0f);
|
|
}
|
|
}
|
|
|
|
void break_here (void) {
|
|
struct CallInfo *ci;
|
|
|
|
if (L->curr_task == L->root_task) {
|
|
lua_error("Cannot break in root thread");
|
|
}
|
|
/* Check for any C functions in the call stack */
|
|
for (ci = L->ci-1; ci > L->base_ci; ci--)
|
|
if (ci->tf == NULL)
|
|
lua_error("Cannot yield through C function");
|
|
|
|
L->Tstate = YIELD;
|
|
}
|
|
|
|
void current_script (void) {
|
|
if (L->curr_task == L->root_task) {
|
|
lua_pushnil();
|
|
} else {
|
|
ttype(L->stack.top) = LUA_T_TASK;
|
|
nvalue(L->stack.top) = (real)L->curr_task->id;
|
|
incr_top;
|
|
}
|
|
}
|
|
|
|
void lua_runtasks (void) {
|
|
struct lua_Task *t, *prev;
|
|
struct lua_Task *old_task = L->curr_task;
|
|
jmp_buf myErrorJmp;
|
|
|
|
prev = L->root_task;
|
|
while ((t = prev->next) != NULL) {
|
|
luaI_switchtask(t);
|
|
// Tstate is not available until after switching tasks
|
|
if (t->Tstate == PAUSE) {
|
|
prev = t;
|
|
continue;
|
|
}
|
|
L->errorJmp = &myErrorJmp;
|
|
L->Tstate = RUN;
|
|
if (setjmp(myErrorJmp) == 0) {
|
|
luaV_execute(L->base_ci + 1);
|
|
if (L->Tstate == RUN) { /* Must have run to completion */
|
|
L->Tstate = DONE;
|
|
}
|
|
} else { /* an error occurred */
|
|
L->Tstate = DONE;
|
|
}
|
|
L->errorJmp = NULL;
|
|
prev = t;
|
|
}
|
|
// Free the completed tasks
|
|
// This MUST occur after all the tasks have been run (not during)
|
|
// or else when one task is freed right after another the task
|
|
// execution gets hosed. Test Case: Switching between tw.set and
|
|
// tb.set in Rubacava causes a crash without this.
|
|
prev = L->root_task;
|
|
while ((t = prev->next) != NULL) {
|
|
luaI_switchtask(t);
|
|
if (L->Tstate == DONE) { // Remove from list of active tasks
|
|
luaI_switchtask(old_task);
|
|
prev->next = t->next;
|
|
t->next = NULL;
|
|
if (prev->next == NULL) {
|
|
L->last_task = prev;
|
|
}
|
|
luaM_free(t);
|
|
} else {
|
|
prev = t;
|
|
}
|
|
}
|
|
if (L->curr_task != old_task) {
|
|
luaI_switchtask(old_task);
|
|
}
|
|
}
|