cobthread.cpp
来自「这是整套横扫千军3D版游戏的源码」· C++ 代码 · 共 968 行 · 第 1/2 页
CPP
968 行
#include "StdAfx.h"
#include <sstream>
#include "CobThread.h"
#include "CobFile.h"
#include "CobInstance.h"
#include "CobEngine.h"
#include "Lua/LuaCob.h"
#include "LogOutput.h"
#include "mmgr.h"
#ifdef _CONSOLE
#include "GlobalStuff.h"
#else
#include "GlobalStuff.h"
#endif
CCobThread::CCobThread(CCobFile &script, CCobInstance *owner)
: owner(owner), script(script)
{
for (int i = 0; i < MAX_LUA_COB_ARGS; i++) {
luaArgs[i] = 0;
}
state = Init;
owner->threads.push_back(this);
AddDeathDependence(owner);
signalMask = 42;
}
//Inform the vultures that we finally croaked
CCobThread::~CCobThread(void)
{
if (callback != NULL) {
//logOutput.Print("%s callback with %d", script.scriptNames[callStack.back().functionId].c_str(), retCode);
(*callback)(retCode, cbParam1, cbParam2);
}
if(owner)
owner->threads.remove(this);
}
//Sets a callback that will be called when the thread dies. There can be only one.
void CCobThread::SetCallback(CBCobThreadFinish cb, void *p1, void *p2)
{
callback = cb;
cbParam1 = p1;
cbParam2 = p2;
}
//This function sets the thread in motion. Should only be called once.
//If schedule is false the thread is not added to the scheduler, and thus
//it is expected that the starter is responsible for ticking it.
void CCobThread::Start(int functionId, const vector<int> &args, bool schedule)
{
state = Run;
PC = script.scriptOffsets[functionId];
struct callInfo ci;
ci.functionId = functionId;
ci.returnAddr = -1;
ci.stackTop = 0;
callStack.push_back(ci);
paramCount = 0;
signalMask = 0;
callback = NULL;
retCode = -1;
for(vector<int>::const_iterator i = args.begin(); i != args.end(); ++i) {
stack.push_back(*i);
paramCount++;
}
//Add to scheduler
if (schedule)
GCobEngine.AddThread(this);
}
const string &CCobThread::GetName()
{
return script.scriptNames[callStack[0].functionId];
}
/** @brief Checks whether the stack has at least size items.
@returns min(size, stack.size()) */
int CCobThread::CheckStack(int size)
{
if ((unsigned)size > stack.size()) {
std::stringstream s;
s << "stack not large enough, need: " << size << ", have: "
<< stack.size() << " (too few arguments passed to function?)";
ShowError(s.str());
return stack.size();
}
return size;
}
/** @brief Returns the value at pos in the stack. Neater than exposing the actual stack */
int CCobThread::GetStackVal(int pos)
{
return stack[pos];
}
int CCobThread::GetWakeTime() const
{
return wakeTime;
}
//Command documentation from http://visualta.tauniverse.com/Downloads/cob-commands.txt
//And some information from basm0.8 source (basm ops.txt)
//Model interaction
const int MOVE = 0x10001000;
const int TURN = 0x10002000;
const int SPIN = 0x10003000;
const int STOP_SPIN = 0x10004000;
const int SHOW = 0x10005000;
const int HIDE = 0x10006000;
const int CACHE = 0x10007000;
const int DONT_CACHE = 0x10008000;
const int MOVE_NOW = 0x1000B000;
const int TURN_NOW = 0x1000C000;
const int SHADE = 0x1000D000;
const int DONT_SHADE = 0x1000E000;
const int EMIT_SFX = 0x1000F000;
//Blocking operations
const int WAIT_TURN = 0x10011000;
const int WAIT_MOVE = 0x10012000;
const int SLEEP = 0x10013000;
//Stack manipulation
const int PUSH_CONSTANT = 0x10021001;
const int PUSH_LOCAL_VAR = 0x10021002;
const int PUSH_STATIC = 0x10021004;
const int CREATE_LOCAL_VAR = 0x10022000;
const int POP_LOCAL_VAR = 0x10023002;
const int POP_STATIC = 0x10023004;
const int POP_STACK = 0x10024000; // Not sure what this is supposed to do
//Arithmetic operations
const int ADD = 0x10031000;
const int SUB = 0x10032000;
const int MUL = 0x10033000;
const int DIV = 0x10034000;
const int MOD = 0x10034001; // spring specific
const int BITWISE_AND = 0x10035000;
const int BITWISE_OR = 0x10036000;
const int BITWISE_XOR = 0x10037000;
const int BITWISE_NOT = 0x10038000;
//Native function calls
const int RAND = 0x10041000;
const int GET_UNIT_VALUE = 0x10042000;
const int GET = 0x10043000;
//Comparison
const int SET_LESS = 0x10051000;
const int SET_LESS_OR_EQUAL = 0x10052000;
const int SET_GREATER = 0x10053000;
const int SET_GREATER_OR_EQUAL = 0x10054000;
const int SET_EQUAL = 0x10055000;
const int SET_NOT_EQUAL = 0x10056000;
const int LOGICAL_AND = 0x10057000;
const int LOGICAL_OR = 0x10058000;
const int LOGICAL_XOR = 0x10059000;
const int LOGICAL_NOT = 0x1005A000;
//Flow control
const int START = 0x10061000;
const int CALL = 0x10062000; // converted when executed
const int REAL_CALL = 0x10062001; // spring custom
const int LUA_CALL = 0x10062002; // spring custom
const int JUMP = 0x10064000;
const int RETURN = 0x10065000;
const int JUMP_NOT_EQUAL = 0x10066000;
const int SIGNAL = 0x10067000;
const int SET_SIGNAL_MASK = 0x10068000;
//Piece destruction
const int EXPLODE = 0x10071000;
const int PLAY_SOUND = 0x10072000;
//Special functions
const int SET = 0x10082000;
const int ATTACH = 0x10083000;
const int DROP = 0x10084000;
// Indices for SET, GET, and GET_UNIT_VALUE for LUA return values
#define LUA0 110 // (LUA0 returns the lua call status, 0 or 1)
#define LUA1 111
#define LUA2 112
#define LUA3 113
#define LUA4 114
#define LUA5 115
#define LUA6 116
#define LUA7 117
#define LUA8 118
#define LUA9 119
//Handy macros
#define GET_LONG_PC() (script.code[PC++])
//#define POP() (stack.size() > 0) ? stack.back(), stack.pop_back(); : 0
int CCobThread::POP(void)
{
if (stack.size() > 0) {
int r = stack.back();
stack.pop_back();
return r;
}
else
return 0;
}
//Returns -1 if this thread is dead and needs to be killed
int CCobThread::Tick(int deltaTime)
{
if (state == Sleep) {
logOutput.Print("CobError: sleeping thread ticked!");
}
if (state == Dead || !owner) {
return -1;
}
state = Run;
GCobEngine.SetCurThread(this);
int r1, r2, r3, r4, r5, r6;
vector<int> args;
CCobThread *thread;
execTrace.clear();
delayedAnims.clear();
//list<int>::iterator ei;
vector<int>::iterator ei;
#if COB_DEBUG > 0
if (COB_DEBUG_FILTER)
logOutput.Print("Executing in %s (from %s)", script.scriptNames[callStack.back().functionId].c_str(), GetName().c_str());
#endif
while (state == Run) {
//int opcode = *(int *)&script.code[PC];
//Disabling exec trace gives about a 50% speedup on vm-intensive code
//execTrace.push_back(PC);
int opcode = GET_LONG_PC();
#if COB_DEBUG > 1
if (COB_DEBUG_FILTER)
logOutput.Print("PC: %x opcode: %x (%s)", PC - 1, opcode, GetOpcodeName(opcode).c_str());
#endif
switch(opcode) {
case PUSH_CONSTANT:
r1 = GET_LONG_PC();
stack.push_back(r1);
break;
case SLEEP:
r1 = POP();
wakeTime = GCurrentTime + r1;
state = Sleep;
GCobEngine.AddThread(this);
GCobEngine.SetCurThread(NULL);
#if COB_DEBUG > 0
if (COB_DEBUG_FILTER)
logOutput.Print("%s sleeping for %d ms", script.scriptNames[callStack.back().functionId].c_str(), r1);
#endif
return 0;
case SPIN:
r1 = GET_LONG_PC();
r2 = GET_LONG_PC();
r3 = POP(); //speed
r4 = POP(); //accel
owner->Spin(r1, r2, r3, r4);
break;
case STOP_SPIN:
r1 = GET_LONG_PC();
r2 = GET_LONG_PC();
r3 = POP(); //decel
//logOutput.Print("Stop spin of %s around %d", script.pieceNames[r1].c_str(), r2);
owner->StopSpin(r1, r2, r3);
break;
case RETURN:
retCode = POP();
if (callStack.back().returnAddr == -1) {
#if COB_DEBUG > 0
if (COB_DEBUG_FILTER)
logOutput.Print("%s returned %d", script.scriptNames[callStack.back().functionId].c_str(), retCode);
#endif
state = Dead;
GCobEngine.SetCurThread(NULL);
//callStack.pop_back();
//Leave values intact on stack in case caller wants to check them
return -1;
}
PC = callStack.back().returnAddr;
while (stack.size() > callStack.back().stackTop) {
stack.pop_back();
}
callStack.pop_back();
#if COB_DEBUG > 0
if (COB_DEBUG_FILTER)
logOutput.Print("Returning to %s", script.scriptNames[callStack.back().functionId].c_str());
#endif
break;
case SHADE:
r1 = GET_LONG_PC();
break;
case DONT_SHADE:
r1 = GET_LONG_PC();
break;
case CACHE:
r1 = GET_LONG_PC();
break;
case DONT_CACHE:
r1 = GET_LONG_PC();
break;
case CALL: {
r1 = GET_LONG_PC();
PC--;
const string& name = script.scriptNames[r1];
if (name.find("lua_") == 0) {
script.code[PC - 1] = LUA_CALL;
LuaCall();
break;
}
script.code[PC - 1] = REAL_CALL;
// fall through //
}
case REAL_CALL:
r1 = GET_LONG_PC();
r2 = GET_LONG_PC();
if (script.scriptLengths[r1] == 0) {
//logOutput.Print("Preventing call to zero-len script %s", script.scriptNames[r1].c_str());
break;
}
struct callInfo ci;
ci.functionId = r1;
ci.returnAddr = PC;
ci.stackTop = stack.size() - r2;
callStack.push_back(ci);
paramCount = r2;
PC = script.scriptOffsets[r1];
#if COB_DEBUG > 0
if (COB_DEBUG_FILTER)
logOutput.Print("Calling %s", script.scriptNames[r1].c_str());
#endif
break;
case LUA_CALL:
LuaCall();
break;
case POP_STATIC:
r1 = GET_LONG_PC();
r2 = POP();
owner->staticVars[r1] = r2;
//logOutput.Print("Pop static var %d val %d", r1, r2);
break;
case POP_STACK:
POP();
break;
case START:
r1 = GET_LONG_PC();
r2 = GET_LONG_PC();
if (script.scriptLengths[r1] == 0) {
//logOutput.Print("Preventing start of zero-len script %s", script.scriptNames[r1].c_str());
break;
}
args.clear();
for (r3 = 0; r3 < r2; ++r3) {
r4 = POP();
args.push_back(r4);
}
thread = SAFE_NEW CCobThread(script, owner);
thread->Start(r1, args, true);
//Seems that threads should inherit signal mask from creator
thread->signalMask = signalMask;
#if COB_DEBUG > 0
if (COB_DEBUG_FILTER)
logOutput.Print("Starting %s %d", script.scriptNames[r1].c_str(), signalMask);
#endif
break;
case CREATE_LOCAL_VAR:
if (paramCount == 0) {
stack.push_back(0);
}
else {
paramCount--;
}
break;
case GET_UNIT_VALUE:
r1 = POP();
if ((r1 >= LUA0) && (r1 <= LUA9)) {
stack.push_back(luaArgs[r1 - LUA0]);
break;
}
ForceCommitAllAnims(); // getunitval could possibly read piece locations
r1 = owner->GetUnitVal(r1, 0, 0, 0, 0);
stack.push_back(r1);
break;
case JUMP_NOT_EQUAL:
r1 = GET_LONG_PC();
r2 = POP();
if (r2 == 0) {
PC = r1;
}
break;
case JUMP:
r1 = GET_LONG_PC();
//this seem to be an error in the docs..
//r2 = script.scriptOffsets[callStack.back().functionId] + r1;
PC = r1;
break;
case POP_LOCAL_VAR:
r1 = GET_LONG_PC();
r2 = POP();
stack[callStack.back().stackTop + r1] = r2;
break;
case PUSH_LOCAL_VAR:
r1 = GET_LONG_PC();
r2 = stack[callStack.back().stackTop + r1];
stack.push_back(r2);
break;
case SET_LESS_OR_EQUAL:
r2 = POP();
r1 = POP();
if (r1 <= r2)
stack.push_back(1);
else
stack.push_back(0);
break;
case BITWISE_AND:
r1 = POP();
r2 = POP();
stack.push_back(r1 & r2);
break;
case BITWISE_OR: //seems to want stack contents or'd, result places on stack
r1 = POP();
r2 = POP();
stack.push_back(r1 | r2);
break;
case BITWISE_XOR:
r1 = POP();
r2 = POP();
stack.push_back(r1 ^ r2);
break;
case BITWISE_NOT:
r1 = POP();
stack.push_back(~r1);
break;
case EXPLODE:
r1 = GET_LONG_PC();
r2 = POP();
owner->Explode(r1, r2);
break;
case PLAY_SOUND:
r1 = GET_LONG_PC();
r2 = POP();
owner->PlayUnitSound(r1, r2);
break;
case PUSH_STATIC:
r1 = GET_LONG_PC();
stack.push_back(owner->staticVars[r1]);
//logOutput.Print("Push static %d val %d", r1, owner->staticVars[r1]);
break;
case SET_NOT_EQUAL:
r1 = POP();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?