mirror of
https://github.com/dhewm/dhewm3-sdk.git
synced 2024-11-23 04:52:20 +00:00
Port script debugger support from dhewm3
all that work in dhewm3 was done by https://github.com/HarrievG/
This commit is contained in:
parent
0942905731
commit
ced3b8f74e
10 changed files with 654 additions and 61 deletions
|
@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) {
|
|||
===============================================================================
|
||||
*/
|
||||
|
||||
idGameEdit gameEditLocal;
|
||||
idGameEditExt gameEditLocal;
|
||||
idGameEdit * gameEdit = &gameEditLocal;
|
||||
|
||||
|
||||
|
@ -1146,3 +1146,63 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
Debugger
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
bool idGameEditExt::IsLineCode( const char *filename, int linenumber ) const {
|
||||
idStr fileStr;
|
||||
idProgram *program = &gameLocal.program;
|
||||
for ( int i = 0; i < program->NumStatements( ); i++ ) {
|
||||
fileStr = program->GetFilename( program->GetStatement( i ).file );
|
||||
fileStr.BackSlashesToSlashes( );
|
||||
|
||||
if ( strcmp( filename, fileStr.c_str( ) ) == 0
|
||||
&& program->GetStatement( i ).linenumber == linenumber
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void idGameEditExt::GetLoadedScripts(idStrList** result)
|
||||
{
|
||||
(*result)->Clear();
|
||||
idProgram* program = &gameLocal.program;
|
||||
|
||||
for (int i = 0; i < program->NumFilenames(); i++)
|
||||
{
|
||||
(*result)->AddUnique(idStr(program->GetFilename(i)));
|
||||
}
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteScriptList(idBitMsg* msg)
|
||||
{
|
||||
idProgram* program = &gameLocal.program;
|
||||
|
||||
msg->WriteInt(program->NumFilenames());
|
||||
for (int i = 0; i < program->NumFilenames(); i++)
|
||||
{
|
||||
idStr file = program->GetFilename(i);
|
||||
//fix this. it seams that scripts triggered by the runtime are stored with a wrong path
|
||||
//the use // instead of '\'
|
||||
file.BackSlashesToSlashes();
|
||||
msg->WriteString(file);
|
||||
}
|
||||
}
|
||||
|
||||
const char* idGameEditExt::GetFilenameForStatement(idProgram* program, int index) const
|
||||
{
|
||||
return program->GetFilenameForStatement(index);
|
||||
}
|
||||
|
||||
int idGameEditExt::GetLineNumberForStatement(idProgram* program, int index) const
|
||||
{
|
||||
return program->GetLineNumberForStatement(index);
|
||||
}
|
||||
|
||||
|
|
|
@ -300,6 +300,17 @@ void idGameLocal::Clear( void ) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// DG: for script debugger
|
||||
static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL;
|
||||
bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {
|
||||
bool ret = false;
|
||||
if ( interpreter != NULL && program != NULL ) {
|
||||
ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter , program, instructionPointer ) : false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===========
|
||||
idGameLocal::Init
|
||||
|
@ -408,6 +419,11 @@ void idGameLocal::Init( void ) {
|
|||
gamestate = GAMESTATE_NOMAP;
|
||||
|
||||
Printf( "...%d aas types\n", aasList.Num() );
|
||||
|
||||
// debugger support - Note: a side effect of requesting this function pointer
|
||||
// is that now the engine knows that this mod supports script debugging.
|
||||
common->GetAdditionalFunction( idCommon::FT_UpdateDebugger,( idCommon::FunctionPointer * ) &updateDebuggerFnPtr,NULL);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
#include "script/Script_Interpreter.h"
|
||||
|
||||
#include "framework/FileSystem.h"
|
||||
|
||||
// HvG: Debugger support
|
||||
extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer );
|
||||
|
||||
/*
|
||||
================
|
||||
idInterpreter::idInterpreter()
|
||||
|
@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue
|
|||
Returns a string representation of the value of the register. This is
|
||||
used primarily for the debugger and debugging
|
||||
|
||||
//FIXME: This is pretty much wrong. won't access data in most situations.
|
||||
================
|
||||
*/
|
||||
bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDepth ) {
|
||||
|
@ -191,17 +195,17 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
idVarDef *d;
|
||||
char funcObject[ 1024 ];
|
||||
char *funcName;
|
||||
const idVarDef *scope;
|
||||
const idVarDef *scope = NULL;
|
||||
const idVarDef *scopeObj;
|
||||
const idTypeDef *field;
|
||||
const idScriptObject *obj;
|
||||
const function_t *func;
|
||||
|
||||
out.Empty();
|
||||
|
||||
|
||||
if ( scopeDepth == -1 ) {
|
||||
scopeDepth = callStackDepth;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( scopeDepth == callStackDepth ) {
|
||||
func = currentFunction;
|
||||
} else {
|
||||
|
@ -215,35 +219,44 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
funcName = strstr( funcObject, "::" );
|
||||
if ( funcName ) {
|
||||
*funcName = '\0';
|
||||
scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
|
||||
funcName += 2;
|
||||
scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
|
||||
funcName += 2;
|
||||
if ( scopeObj )
|
||||
{
|
||||
scope = gameLocal.program.GetDef( NULL, funcName, scopeObj );
|
||||
}
|
||||
} else {
|
||||
funcName = funcObject;
|
||||
scope = &def_namespace;
|
||||
scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace );
|
||||
scopeObj = NULL;
|
||||
}
|
||||
|
||||
// Get the function from the object
|
||||
d = gameLocal.program.GetDef( NULL, funcName, scope );
|
||||
if ( !d ) {
|
||||
if ( !scope )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the variable itself and check various namespaces
|
||||
d = gameLocal.program.GetDef( NULL, name, d );
|
||||
if ( !d ) {
|
||||
if ( scope == &def_namespace ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
d = gameLocal.program.GetDef( NULL, name, scope );
|
||||
if ( !d ) {
|
||||
d = gameLocal.program.GetDef( NULL, name, &def_namespace );
|
||||
if ( !d ) {
|
||||
return false;
|
||||
d = gameLocal.program.GetDef( NULL, name, scope );
|
||||
|
||||
// Check the objects for it if it wasnt local to the function
|
||||
if ( !d )
|
||||
{
|
||||
for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def )
|
||||
{
|
||||
d = gameLocal.program.GetDef( NULL, name, scopeObj );
|
||||
if ( d )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !d )
|
||||
{
|
||||
out = "???";
|
||||
return false;
|
||||
}
|
||||
|
||||
reg = GetVariable( d );
|
||||
switch( d->Type() ) {
|
||||
case ev_float:
|
||||
|
@ -256,7 +269,7 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
break;
|
||||
|
||||
case ev_vector:
|
||||
if ( reg.vectorPtr ) {
|
||||
if ( reg.vectorPtr ) {
|
||||
out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z );
|
||||
} else {
|
||||
out = "0,0,0";
|
||||
|
@ -274,30 +287,55 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
break;
|
||||
|
||||
case ev_field:
|
||||
{
|
||||
idEntity* entity;
|
||||
idScriptObject* obj;
|
||||
|
||||
if ( scope == &def_namespace ) {
|
||||
// should never happen, but handle it safely anyway
|
||||
return false;
|
||||
}
|
||||
|
||||
field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType();
|
||||
obj = *reinterpret_cast<const idScriptObject **>( &localstack[ callStack[ callStackDepth ].stackbase ] );
|
||||
if ( !field || !obj ) {
|
||||
field = d->TypeDef()->FieldType();
|
||||
entity = GetEntity ( *((int*)&localstack[ localstackBase ]) );
|
||||
if ( !entity || !field )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = &entity->scriptObject;
|
||||
if ( !obj ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ( field->Type() ) {
|
||||
case ev_boolean:
|
||||
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
|
||||
return true;
|
||||
case ev_boolean:
|
||||
out = va( "%d", *( reinterpret_cast<int *>( &obj->data[ reg.ptrOffset ] ) ) );
|
||||
return true;
|
||||
|
||||
case ev_float:
|
||||
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
|
||||
return true;
|
||||
case ev_float:
|
||||
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
|
||||
return true;
|
||||
|
||||
case ev_string: {
|
||||
const char* str;
|
||||
str = reinterpret_cast<const char*>( &obj->data[ reg.ptrOffset ] );
|
||||
if ( !str ) {
|
||||
out = "\"\"";
|
||||
} else {
|
||||
out = "\"";
|
||||
out += str;
|
||||
out += "\"";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ev_string:
|
||||
if ( reg.stringPtr ) {
|
||||
|
@ -969,6 +1007,19 @@ bool idInterpreter::Execute( void ) {
|
|||
// next statement
|
||||
st = &gameLocal.program.GetStatement( instructionPointer );
|
||||
|
||||
if ( !updateGameDebugger( this, &gameLocal.program, instructionPointer )
|
||||
&& g_debugScript.GetBool( ) )
|
||||
{
|
||||
static int lastLineNumber = -1;
|
||||
if (lastLineNumber != gameLocal.program.GetStatement(instructionPointer).linenumber) {
|
||||
gameLocal.Printf("%s (%d)\n",
|
||||
gameLocal.program.GetFilename(gameLocal.program.GetStatement(instructionPointer).file),
|
||||
gameLocal.program.GetStatement(instructionPointer).linenumber
|
||||
);
|
||||
lastLineNumber = gameLocal.program.GetStatement(instructionPointer).linenumber;
|
||||
}
|
||||
}
|
||||
|
||||
switch( st->op ) {
|
||||
case OP_RETURN:
|
||||
LeaveFunction( st->a );
|
||||
|
@ -1833,3 +1884,100 @@ bool idInterpreter::Execute( void ) {
|
|||
|
||||
return threadDying;
|
||||
}
|
||||
|
||||
|
||||
bool idGameEditExt::CheckForBreakPointHit(const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth) const
|
||||
{
|
||||
return ((interpreter->GetCurrentFunction() == function1 ||
|
||||
interpreter->GetCurrentFunction() == function2) &&
|
||||
(interpreter->GetCallstackDepth() <= depth));
|
||||
}
|
||||
|
||||
bool idGameEditExt::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const
|
||||
{
|
||||
|
||||
return (const_cast<idProgram*>(program)->GetStatement(index).op == OP_RETURN && interpreter->GetCallstackDepth() <= 1);
|
||||
}
|
||||
|
||||
bool idGameEditExt::GetRegisterValue(const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth) const
|
||||
{
|
||||
return const_cast<idInterpreter*>(interpreter)->GetRegisterValue(name, out, scopeDepth);
|
||||
}
|
||||
|
||||
const idThread* idGameEditExt::GetThread(const idInterpreter* interpreter) const
|
||||
{
|
||||
return interpreter->GetThread();
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteCallstackFunc(idBitMsg* msg, const prstack_t* stack, const idProgram* program, int instructionPtr)
|
||||
{
|
||||
const statement_t* st;
|
||||
const function_t* func;
|
||||
|
||||
func = stack->f;
|
||||
|
||||
// If the function is unknown then just fill in with default data.
|
||||
if (!func)
|
||||
{
|
||||
msg->WriteString("<UNKNOWN>");
|
||||
msg->WriteString("<UNKNOWN>");
|
||||
msg->WriteInt(0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->WriteString(va("%s( )", func->Name()));
|
||||
}
|
||||
|
||||
if (stack->s == -1) //this is a fake stack created by debugger, use intruction pointer for retrieval.
|
||||
st = &const_cast<idProgram*>(program)->GetStatement(instructionPtr);
|
||||
else // Use the calling statement as the filename and linenumber where the call was made from
|
||||
st = &const_cast<idProgram*>(program)->GetStatement(stack->s);
|
||||
|
||||
if (st)
|
||||
{
|
||||
idStr qpath = const_cast<idProgram*>(program)->GetFilename(st->file);
|
||||
if (idStr::FindChar(qpath, ':') != -1)
|
||||
qpath = fileSystem->OSPathToRelativePath(qpath.c_str());
|
||||
qpath.BackSlashesToSlashes();
|
||||
msg->WriteString(qpath);
|
||||
msg->WriteInt(st->linenumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->WriteString("<UNKNOWN>");
|
||||
msg->WriteInt(0);
|
||||
}
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteInterpreterInfo(idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr)
|
||||
{
|
||||
int i;
|
||||
prstack_s temp;
|
||||
|
||||
msg->WriteShort((int)interpreter->GetCallstackDepth());
|
||||
|
||||
// write out the current function
|
||||
temp.f = interpreter->GetCurrentFunction();
|
||||
temp.s = -1;
|
||||
temp.stackbase = 0;
|
||||
MSG_WriteCallstackFunc(msg, &temp, program, instructionPtr);
|
||||
|
||||
// Run through all of the callstack and write each to the msg
|
||||
for (i = interpreter->GetCallstackDepth() - 1; i > 0; i--)
|
||||
{
|
||||
MSG_WriteCallstackFunc(msg, interpreter->GetCallstack() + i, program, instructionPtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int idGameEditExt::GetInterpreterCallStackDepth(const idInterpreter* interpreter)
|
||||
{
|
||||
return interpreter->GetCallstackDepth();
|
||||
}
|
||||
|
||||
const function_t* idGameEditExt::GetInterpreterCallStackFunction(const idInterpreter* interpreter, int stackDepth/* = -1*/)
|
||||
{
|
||||
return interpreter->GetCallstack()[stackDepth > -1 ? stackDepth : interpreter->GetCallstackDepth()].f;
|
||||
}
|
||||
|
||||
|
|
|
@ -1921,3 +1921,50 @@ void idThread::Event_InfluenceActive( void ) {
|
|||
idThread::ReturnInt( false );
|
||||
}
|
||||
}
|
||||
|
||||
int idGameEditExt::ThreadGetNum(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->GetThreadNum();
|
||||
}
|
||||
|
||||
const char* idGameEditExt::ThreadGetName(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->GetThreadName();
|
||||
}
|
||||
|
||||
int idGameEditExt::GetTotalScriptThreads() const
|
||||
{
|
||||
return idThread::GetThreads().Num();
|
||||
}
|
||||
|
||||
const idThread* idGameEditExt::GetThreadByIndex(int index) const
|
||||
{
|
||||
return idThread::GetThreads()[index];
|
||||
}
|
||||
|
||||
bool idGameEditExt::ThreadIsDoneProcessing(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->IsDoneProcessing();
|
||||
}
|
||||
|
||||
bool idGameEditExt::ThreadIsWaiting(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->IsWaiting();
|
||||
}
|
||||
|
||||
bool idGameEditExt::ThreadIsDying(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->IsDying();
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter)
|
||||
{
|
||||
msg->WriteString(const_cast<idThread*>(thread)->GetThreadName());
|
||||
msg->WriteInt(const_cast<idThread*>(thread)->GetThreadNum());
|
||||
|
||||
msg->WriteBits((int)(thread == interpreter->GetThread()), 1);
|
||||
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDoneProcessing(), 1);
|
||||
msg->WriteBits((int)const_cast<idThread*>(thread)->IsWaiting(), 1);
|
||||
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDying(), 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,6 +73,9 @@ extern idCVar com_showAsyncStats;
|
|||
extern idCVar com_showSoundDecoders;
|
||||
extern idCVar com_makingBuild;
|
||||
extern idCVar com_updateLoadSize;
|
||||
extern idCVar com_enableDebuggerServer;
|
||||
extern idCVar com_dbgClientAdr;
|
||||
extern idCVar com_dbgServerAdr;
|
||||
|
||||
extern int time_gameFrame; // game logic time
|
||||
extern int time_gameDraw; // game present time
|
||||
|
@ -84,6 +87,8 @@ extern volatile int com_ticNumber; // 60 hz tics, incremented by async functio
|
|||
extern int com_editors; // current active editor(s)
|
||||
extern bool com_editorActive; // true if an editor has focus
|
||||
|
||||
extern bool com_debuggerSupported; // only set to true when the updateDebugger function is set. see GetAdditionalFunction()
|
||||
|
||||
#ifdef _WIN32
|
||||
const char DMAP_MSGID[] = "DMAPOutput";
|
||||
const char DMAP_DONE[] = "DMAPDone";
|
||||
|
@ -266,6 +271,11 @@ public:
|
|||
// it returns true if we're currently running the doom3 demo
|
||||
// not relevant for mods, only for game/ aka base.dll/base.so/...
|
||||
FT_IsDemo = 1,
|
||||
// the function's signature is bool fn(idInterpreter,idProgram,int) with arguments:
|
||||
// idInterpreter *interpreter, idProgram *program, int instructionPointer
|
||||
// it returns true if the game debugger is active.
|
||||
// relevant for mods.
|
||||
FT_UpdateDebugger,
|
||||
};
|
||||
|
||||
// returns true if that function is available in this version of dhewm3
|
||||
|
|
|
@ -31,6 +31,7 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
#include "idlib/BitMsg.h"
|
||||
#include "idlib/Dict.h"
|
||||
#include "idlib/containers/StrList.h"
|
||||
#include "framework/UsercmdGen.h"
|
||||
#include "renderer/RenderWorld.h"
|
||||
#include "sound/sound.h"
|
||||
|
@ -232,6 +233,12 @@ enum {
|
|||
|
||||
class idEntity;
|
||||
class idMD5Anim;
|
||||
// DG: for idGameEditExt (for script debugger)
|
||||
class idThread;
|
||||
class function_t;
|
||||
class idProgram;
|
||||
class idInterpreter;
|
||||
typedef struct prstack_s prstack_t;
|
||||
|
||||
// FIXME: this interface needs to be reworked but it properly separates code for the time being
|
||||
class idGameEdit {
|
||||
|
@ -315,6 +322,40 @@ public:
|
|||
|
||||
extern idGameEdit * gameEdit;
|
||||
|
||||
// In game script Debugging Support
|
||||
class idGameEditExt : public idGameEdit {
|
||||
public:
|
||||
virtual ~idGameEditExt( void ) { }
|
||||
// IdProgram
|
||||
virtual void GetLoadedScripts( idStrList ** result );
|
||||
virtual bool IsLineCode( const char* filename, int linenumber) const;
|
||||
virtual const char * GetFilenameForStatement( idProgram* program, int index ) const;
|
||||
virtual int GetLineNumberForStatement( idProgram* program, int index ) const;
|
||||
|
||||
// idInterpreter
|
||||
virtual bool CheckForBreakPointHit( const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth ) const;
|
||||
virtual bool ReturnedFromFunction( const idProgram* program, const idInterpreter* interpreter, int index ) const;
|
||||
virtual bool GetRegisterValue( const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth ) const;
|
||||
virtual const idThread* GetThread( const idInterpreter* interpreter ) const;
|
||||
virtual int GetInterpreterCallStackDepth( const idInterpreter* interpreter );
|
||||
virtual const function_t* GetInterpreterCallStackFunction( const idInterpreter* interpreter, int stackDepth = -1 );
|
||||
|
||||
// IdThread
|
||||
virtual const char * ThreadGetName( const idThread* thread ) const;
|
||||
virtual int ThreadGetNum( const idThread* thread ) const;
|
||||
virtual bool ThreadIsDoneProcessing( const idThread* thread ) const;
|
||||
virtual bool ThreadIsWaiting( const idThread* thread ) const;
|
||||
virtual bool ThreadIsDying( const idThread* thread ) const;
|
||||
virtual int GetTotalScriptThreads( ) const;
|
||||
virtual const idThread* GetThreadByIndex( int index ) const;
|
||||
|
||||
// MSG helpers
|
||||
virtual void MSG_WriteThreadInfo( idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter );
|
||||
virtual void MSG_WriteCallstackFunc( idBitMsg* msg, const prstack_t* stack, const idProgram* program, int instructionPtr );
|
||||
virtual void MSG_WriteInterpreterInfo( idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr );
|
||||
virtual void MSG_WriteScriptList( idBitMsg* msg );
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
|
|
@ -670,7 +670,7 @@ void idEditEntities::DisplayEntities( void ) {
|
|||
===============================================================================
|
||||
*/
|
||||
|
||||
idGameEdit gameEditLocal;
|
||||
idGameEditExt gameEditLocal;
|
||||
idGameEdit * gameEdit = &gameEditLocal;
|
||||
|
||||
|
||||
|
@ -1146,3 +1146,65 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
Debugger
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
bool idGameEditExt::IsLineCode(const char* filename, int linenumber) const
|
||||
{
|
||||
idStr fileStr;
|
||||
idProgram* program = &gameLocal.program;
|
||||
for (int i = 0; i < program->NumStatements(); i++)
|
||||
{
|
||||
fileStr = program->GetFilename(program->GetStatement(i).file);
|
||||
fileStr.BackSlashesToSlashes();
|
||||
|
||||
if (strcmp(filename, fileStr.c_str()) == 0
|
||||
&& program->GetStatement(i).linenumber == linenumber
|
||||
)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void idGameEditExt::GetLoadedScripts( idStrList** result )
|
||||
{
|
||||
(*result)->Clear();
|
||||
idProgram* program = &gameLocal.program;
|
||||
|
||||
for (int i = 0; i < program->NumFilenames(); i++)
|
||||
{
|
||||
(*result)->AddUnique( idStr(program->GetFilename( i )) );
|
||||
}
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteScriptList( idBitMsg* msg)
|
||||
{
|
||||
idProgram* program = &gameLocal.program;
|
||||
|
||||
msg->WriteInt( program->NumFilenames() );
|
||||
for (int i = 0; i < program->NumFilenames(); i++)
|
||||
{
|
||||
idStr file = program->GetFilename(i);
|
||||
//fix this. it seams that scripts triggered by the runtime are stored with a wrong path
|
||||
//the use // instead of '\'
|
||||
file.BackSlashesToSlashes();
|
||||
msg->WriteString(file);
|
||||
}
|
||||
}
|
||||
|
||||
const char*idGameEditExt::GetFilenameForStatement(idProgram* program, int index) const
|
||||
{
|
||||
return program->GetFilenameForStatement(index);
|
||||
}
|
||||
|
||||
int idGameEditExt::GetLineNumberForStatement(idProgram* program, int index) const
|
||||
{
|
||||
return program->GetLineNumberForStatement(index);
|
||||
}
|
||||
|
||||
|
|
|
@ -256,6 +256,18 @@ void idGameLocal::Clear( void ) {
|
|||
memset( lagometer, 0, sizeof( lagometer ) );
|
||||
}
|
||||
|
||||
|
||||
// DG: for script debugger support
|
||||
static bool ( *updateDebuggerFnPtr )( idInterpreter *interpreter, idProgram *program, int instructionPointer ) = NULL;
|
||||
bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer ) {
|
||||
bool ret = false;
|
||||
if ( interpreter != NULL && program != NULL ) {
|
||||
ret = updateDebuggerFnPtr ? updateDebuggerFnPtr( interpreter, program, instructionPointer ) : false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
===========
|
||||
idGameLocal::Init
|
||||
|
@ -332,6 +344,10 @@ void idGameLocal::Init( void ) {
|
|||
gamestate = GAMESTATE_NOMAP;
|
||||
|
||||
Printf( "...%d aas types\n", aasList.Num() );
|
||||
|
||||
// debugger support - Note: a side effect of requesting this function pointer
|
||||
// is that now the engine knows that this mod supports script debugging.
|
||||
common->GetAdditionalFunction(idCommon::FT_UpdateDebugger,(idCommon::FunctionPointer*) &updateDebuggerFnPtr,NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -33,6 +33,11 @@ If you have questions concerning this license or the applicable additional terms
|
|||
|
||||
#include "script/Script_Interpreter.h"
|
||||
|
||||
#include "framework/FileSystem.h"
|
||||
|
||||
// HvG: Debugger support
|
||||
extern bool updateGameDebugger( idInterpreter *interpreter, idProgram *program, int instructionPointer );
|
||||
|
||||
/*
|
||||
================
|
||||
idInterpreter::idInterpreter()
|
||||
|
@ -183,7 +188,6 @@ idInterpreter::GetRegisterValue
|
|||
Returns a string representation of the value of the register. This is
|
||||
used primarily for the debugger and debugging
|
||||
|
||||
//FIXME: This is pretty much wrong. won't access data in most situations.
|
||||
================
|
||||
*/
|
||||
bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDepth ) {
|
||||
|
@ -191,9 +195,9 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
idVarDef *d;
|
||||
char funcObject[ 1024 ];
|
||||
char *funcName;
|
||||
const idVarDef *scope;
|
||||
const idVarDef *scope = NULL;
|
||||
const idVarDef *scopeObj;
|
||||
const idTypeDef *field;
|
||||
const idScriptObject *obj;
|
||||
const function_t *func;
|
||||
|
||||
out.Empty();
|
||||
|
@ -215,33 +219,42 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
funcName = strstr( funcObject, "::" );
|
||||
if ( funcName ) {
|
||||
*funcName = '\0';
|
||||
scope = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
|
||||
scopeObj = gameLocal.program.GetDef( NULL, funcObject, &def_namespace );
|
||||
funcName += 2;
|
||||
if ( scopeObj )
|
||||
{
|
||||
scope = gameLocal.program.GetDef( NULL, funcName, scopeObj );
|
||||
}
|
||||
} else {
|
||||
funcName = funcObject;
|
||||
scope = &def_namespace;
|
||||
scope = gameLocal.program.GetDef( NULL, func->Name(), &def_namespace );
|
||||
scopeObj = NULL;
|
||||
}
|
||||
|
||||
// Get the function from the object
|
||||
d = gameLocal.program.GetDef( NULL, funcName, scope );
|
||||
if ( !d ) {
|
||||
if ( !scope )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the variable itself and check various namespaces
|
||||
d = gameLocal.program.GetDef( NULL, name, d );
|
||||
if ( !d ) {
|
||||
if ( scope == &def_namespace ) {
|
||||
return false;
|
||||
}
|
||||
d = gameLocal.program.GetDef( NULL, name, scope );
|
||||
|
||||
d = gameLocal.program.GetDef( NULL, name, scope );
|
||||
if ( !d ) {
|
||||
d = gameLocal.program.GetDef( NULL, name, &def_namespace );
|
||||
if ( !d ) {
|
||||
return false;
|
||||
// Check the objects for it if it wasnt local to the function
|
||||
if ( !d )
|
||||
{
|
||||
for ( ; scopeObj && scopeObj->TypeDef()->SuperClass(); scopeObj = scopeObj->TypeDef()->SuperClass()->def )
|
||||
{
|
||||
d = gameLocal.program.GetDef( NULL, name, scopeObj );
|
||||
if ( d )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !d )
|
||||
{
|
||||
out = "???";
|
||||
return false;
|
||||
}
|
||||
|
||||
reg = GetVariable( d );
|
||||
|
@ -274,14 +287,24 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
break;
|
||||
|
||||
case ev_field:
|
||||
{
|
||||
idEntity* entity;
|
||||
idScriptObject* obj;
|
||||
|
||||
if ( scope == &def_namespace ) {
|
||||
// should never happen, but handle it safely anyway
|
||||
return false;
|
||||
}
|
||||
|
||||
field = scope->TypeDef()->GetParmType( reg.ptrOffset )->FieldType();
|
||||
obj = *reinterpret_cast<const idScriptObject **>( &localstack[ callStack[ callStackDepth ].stackbase ] );
|
||||
if ( !field || !obj ) {
|
||||
field = d->TypeDef()->FieldType();
|
||||
entity = GetEntity ( *((int*)&localstack[ localstackBase ]) );
|
||||
if ( !entity || !field )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
obj = &entity->scriptObject;
|
||||
if ( !obj ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -293,11 +316,26 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep
|
|||
case ev_float:
|
||||
out = va( "%g", *( reinterpret_cast<float *>( &obj->data[ reg.ptrOffset ] ) ) );
|
||||
return true;
|
||||
|
||||
case ev_string: {
|
||||
const char* str;
|
||||
str = reinterpret_cast<const char*>( &obj->data[ reg.ptrOffset ] );
|
||||
if ( !str ) {
|
||||
out = "\"\"";
|
||||
} else {
|
||||
out = "\"";
|
||||
out += str;
|
||||
out += "\"";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ev_string:
|
||||
if ( reg.stringPtr ) {
|
||||
|
@ -969,6 +1007,19 @@ bool idInterpreter::Execute( void ) {
|
|||
// next statement
|
||||
st = &gameLocal.program.GetStatement( instructionPointer );
|
||||
|
||||
if ( !updateGameDebugger( this, &gameLocal.program, instructionPointer )
|
||||
&& g_debugScript.GetBool( ) )
|
||||
{
|
||||
static int lastLineNumber = -1;
|
||||
if ( lastLineNumber != gameLocal.program.GetStatement ( instructionPointer ).linenumber ) {
|
||||
gameLocal.Printf ( "%s (%d)\n",
|
||||
gameLocal.program.GetFilename ( gameLocal.program.GetStatement ( instructionPointer ).file ),
|
||||
gameLocal.program.GetStatement ( instructionPointer ).linenumber
|
||||
);
|
||||
lastLineNumber = gameLocal.program.GetStatement ( instructionPointer ).linenumber;
|
||||
}
|
||||
}
|
||||
|
||||
switch( st->op ) {
|
||||
case OP_RETURN:
|
||||
LeaveFunction( st->a );
|
||||
|
@ -1833,3 +1884,98 @@ bool idInterpreter::Execute( void ) {
|
|||
|
||||
return threadDying;
|
||||
}
|
||||
|
||||
bool idGameEditExt::CheckForBreakPointHit(const idInterpreter* interpreter, const function_t* function1, const function_t* function2, int depth) const
|
||||
{
|
||||
return ( ( interpreter->GetCurrentFunction ( ) == function1 ||
|
||||
interpreter->GetCurrentFunction ( ) == function2)&&
|
||||
( interpreter->GetCallstackDepth ( ) <= depth) );
|
||||
}
|
||||
|
||||
bool idGameEditExt::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const
|
||||
{
|
||||
|
||||
return ( const_cast<idProgram*>(program)->GetStatement(index).op == OP_RETURN && interpreter->GetCallstackDepth ( ) <= 1 );
|
||||
}
|
||||
|
||||
bool idGameEditExt::GetRegisterValue(const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth) const
|
||||
{
|
||||
return const_cast<idInterpreter*>(interpreter)->GetRegisterValue(name, out, scopeDepth);
|
||||
}
|
||||
|
||||
const idThread*idGameEditExt::GetThread(const idInterpreter* interpreter) const
|
||||
{
|
||||
return interpreter->GetThread();
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteCallstackFunc(idBitMsg* msg, const prstack_t* stack, const idProgram * program, int instructionPtr)
|
||||
{
|
||||
const statement_t* st;
|
||||
const function_t* func;
|
||||
|
||||
func = stack->f;
|
||||
|
||||
// If the function is unknown then just fill in with default data.
|
||||
if ( !func )
|
||||
{
|
||||
msg->WriteString ( "<UNKNOWN>" );
|
||||
msg->WriteString ( "<UNKNOWN>" );
|
||||
msg->WriteInt ( 0 );
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->WriteString ( va("%s( )", func->Name() ) );
|
||||
}
|
||||
|
||||
if (stack->s == -1) //this is a fake stack created by debugger, use intruction pointer for retrieval.
|
||||
st = &const_cast<idProgram*>( program )->GetStatement( instructionPtr );
|
||||
else // Use the calling statement as the filename and linenumber where the call was made from
|
||||
st = &const_cast<idProgram*>( program )->GetStatement ( stack->s );
|
||||
|
||||
if ( st )
|
||||
{
|
||||
idStr qpath = const_cast<idProgram*>( program )->GetFilename( st->file );
|
||||
if (idStr::FindChar( qpath, ':' ) != -1)
|
||||
qpath = fileSystem->OSPathToRelativePath( qpath.c_str() );
|
||||
qpath.BackSlashesToSlashes ( );
|
||||
msg->WriteString( qpath );
|
||||
msg->WriteInt( st->linenumber );
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->WriteString ( "<UNKNOWN>" );
|
||||
msg->WriteInt ( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteInterpreterInfo(idBitMsg* msg, const idInterpreter* interpreter, const idProgram* program, int instructionPtr)
|
||||
{
|
||||
int i;
|
||||
prstack_s temp;
|
||||
|
||||
msg->WriteShort((int)interpreter->GetCallstackDepth( ) );
|
||||
|
||||
// write out the current function
|
||||
temp.f = interpreter->GetCurrentFunction( );
|
||||
temp.s = -1;
|
||||
temp.stackbase = 0;
|
||||
MSG_WriteCallstackFunc( msg, &temp, program, instructionPtr);
|
||||
|
||||
// Run through all of the callstack and write each to the msg
|
||||
for (i = interpreter->GetCallstackDepth() - 1; i > 0; i--)
|
||||
{
|
||||
MSG_WriteCallstackFunc( msg, interpreter->GetCallstack( ) + i, program, instructionPtr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int idGameEditExt::GetInterpreterCallStackDepth(const idInterpreter* interpreter)
|
||||
{
|
||||
return interpreter->GetCallstackDepth();
|
||||
}
|
||||
|
||||
const function_t*idGameEditExt::GetInterpreterCallStackFunction( const idInterpreter* interpreter, int stackDepth/* = -1*/)
|
||||
{
|
||||
return interpreter->GetCallstack( )[ stackDepth > -1 ? stackDepth :interpreter->GetCallstackDepth( ) ].f;
|
||||
}
|
||||
|
|
|
@ -1841,3 +1841,50 @@ void idThread::Event_InfluenceActive( void ) {
|
|||
idThread::ReturnInt( false );
|
||||
}
|
||||
}
|
||||
|
||||
int idGameEditExt::ThreadGetNum(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->GetThreadNum();
|
||||
}
|
||||
|
||||
const char*idGameEditExt::ThreadGetName(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->GetThreadName();
|
||||
}
|
||||
|
||||
int idGameEditExt::GetTotalScriptThreads() const
|
||||
{
|
||||
return idThread::GetThreads().Num();
|
||||
}
|
||||
|
||||
const idThread*idGameEditExt::GetThreadByIndex(int index) const
|
||||
{
|
||||
return idThread::GetThreads()[index];
|
||||
}
|
||||
|
||||
bool idGameEditExt::ThreadIsDoneProcessing(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->IsDoneProcessing();
|
||||
}
|
||||
|
||||
bool idGameEditExt::ThreadIsWaiting(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->IsWaiting();
|
||||
}
|
||||
|
||||
bool idGameEditExt::ThreadIsDying(const idThread* thread) const
|
||||
{
|
||||
return const_cast<idThread*>(thread)->IsDying();
|
||||
}
|
||||
|
||||
void idGameEditExt::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter)
|
||||
{
|
||||
msg->WriteString(const_cast<idThread*>(thread)->GetThreadName());
|
||||
msg->WriteInt(const_cast<idThread*>(thread)->GetThreadNum());
|
||||
|
||||
msg->WriteBits((int)(thread == interpreter->GetThread()), 1);
|
||||
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDoneProcessing(), 1);
|
||||
msg->WriteBits((int)const_cast<idThread*>(thread)->IsWaiting(), 1);
|
||||
msg->WriteBits((int)const_cast<idThread*>(thread)->IsDying(), 1);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue