From 99ca9f054328abca4f67a323afd72b5f114139ef Mon Sep 17 00:00:00 2001 From: HarrievG Date: Thu, 13 May 2021 10:23:36 +0200 Subject: [PATCH] - D3XP debugger fixes --- neo/d3xp/GameEdit.cpp | 62 ++++++ neo/d3xp/Game_local.cpp | 26 ++- neo/d3xp/Game_local.h | 7 +- neo/d3xp/script/Script_Interpreter.cpp | 270 +++++++++++++++++++------ neo/d3xp/script/Script_Thread.cpp | 46 +++++ 5 files changed, 334 insertions(+), 77 deletions(-) diff --git a/neo/d3xp/GameEdit.cpp b/neo/d3xp/GameEdit.cpp index 0c637bdb..71907bb4 100644 --- a/neo/d3xp/GameEdit.cpp +++ b/neo/d3xp/GameEdit.cpp @@ -1146,3 +1146,65 @@ void idGameEdit::MapEntityTranslate( const char *name, const idVec3 &v ) const { } } } + + +/*********************************************************************** + + Debugger + +***********************************************************************/ + +bool idGameEdit::IsLineCode(const char* filename, int linenumber) const +{ + static idStr fileStr = idStr(MAX_PATH); + 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 idGameEdit::GetLoadedScripts(idStrList** result) +{ + (*result)->Clear(); + idProgram* program = &gameLocal.program; + + for (int i = 0; i < program->NumFilenames(); i++) + { + (*result)->AddUnique(idStr(program->GetFilename(i))); + } +} + +void idGameEdit::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* idGameEdit::GetFilenameForStatement(idProgram* program, int index) const +{ + return program->GetFilenameForStatement(index); +} + +int idGameEdit::GetLineNumberForStatement(idProgram* program, int index) const +{ + return program->GetLineNumberForStatement(index); +} diff --git a/neo/d3xp/Game_local.cpp b/neo/d3xp/Game_local.cpp index 08aaf9bf..fccc4947 100644 --- a/neo/d3xp/Game_local.cpp +++ b/neo/d3xp/Game_local.cpp @@ -1312,7 +1312,7 @@ void idGameLocal::MapPopulate( void ) { idGameLocal::InitFromNewMap =================== */ -void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) { +void idGameLocal::InitFromNewMap(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, bool isServer, bool isClient, int randseed, int activeEditors) { this->isServer = isServer; this->isClient = isClient; @@ -1323,6 +1323,9 @@ void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorl } Printf( "----- Game Map Init -----\n" ); + + //exposing editor flag so debugger does not miss any script calls during load/startup + editors = activeEditors; gamestate = GAMESTATE_STARTUP; @@ -1350,7 +1353,7 @@ void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorl idGameLocal::InitFromSaveGame ================= */ -bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile ) { +bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile, int activeEditors) { int i; int num; idEntity *ent; @@ -2436,14 +2439,17 @@ void idGameLocal::RunTimeGroup2() { idGameLocal::RunFrame ================ */ -gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) { - idEntity * ent; - int num; - float ms; - idTimer timer_think, timer_events, timer_singlethink; - gameReturn_t ret; - idPlayer *player; - const renderView_t *view; +gameReturn_t idGameLocal::RunFrame(const usercmd_t* clientCmds, int activeEditors) { + idEntity* ent; + int num; + float ms; + idTimer timer_think, timer_events, timer_singlethink; + gameReturn_t ret; + idPlayer* player; + const renderView_t* view; + + //exposing editor flag so debugger does not miss any script calls during load/startup + editors = activeEditors; #ifdef _DEBUG if ( isMultiplayer ) { diff --git a/neo/d3xp/Game_local.h b/neo/d3xp/Game_local.h index acd4fff0..4451b559 100644 --- a/neo/d3xp/Game_local.h +++ b/neo/d3xp/Game_local.h @@ -319,6 +319,7 @@ public: idEntityPtr lastGUIEnt; // last entity with a GUI, used by Cmd_NextGUI_f int lastGUI; // last GUI on the lastGUIEnt + int editors; #ifdef _D3XP idEntityPtr portalSkyEnt; @@ -365,13 +366,13 @@ public: virtual const idDict & GetPersistentPlayerInfo( int clientNum ); virtual void SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo ); - virtual void InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randSeed ); - virtual bool InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile ); + virtual void InitFromNewMap(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, bool isServer, bool isClient, int randSeed, int activeEditors); + virtual bool InitFromSaveGame(const char* mapName, idRenderWorld* renderWorld, idSoundWorld* soundWorld, idFile* saveGameFile, int activeEditors); virtual void SaveGame( idFile *saveGameFile ); virtual void MapShutdown( void ); virtual void CacheDictionaryMedia( const idDict *dict ); virtual void SpawnPlayer( int clientNum ); - virtual gameReturn_t RunFrame( const usercmd_t *clientCmds ); + virtual gameReturn_t RunFrame(const usercmd_t* clientCmds, int activeEditors); virtual bool Draw( int clientNum ); virtual escReply_t HandleESC( idUserInterface **gui ); virtual idUserInterface *StartMenu( void ); diff --git a/neo/d3xp/script/Script_Interpreter.cpp b/neo/d3xp/script/Script_Interpreter.cpp index c8e0cc45..58fc69ee 100644 --- a/neo/d3xp/script/Script_Interpreter.cpp +++ b/neo/d3xp/script/Script_Interpreter.cpp @@ -33,6 +33,8 @@ If you have questions concerning this license or the applicable additional terms #include "script/Script_Interpreter.h" +#include "framework/FileSystem.h" + /* ================ idInterpreter::idInterpreter() @@ -176,135 +178,166 @@ void idInterpreter::Reset( void ) { doneProcessing = true; } -/* -================ -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 ) { +bool idInterpreter::GetRegisterValue(const char* name, idStr& out, int scopeDepth) { varEval_t reg; - idVarDef *d; - char funcObject[ 1024 ]; - char *funcName; - const idVarDef *scope; - const idTypeDef *field; - const idScriptObject *obj; - const function_t *func; + idVarDef* d; + char funcObject[1024]; + char* funcName; + const idVarDef* scope = NULL; + const idVarDef* scopeObj; + const idTypeDef* field; + const function_t* func; out.Empty(); - if ( scopeDepth == -1 ) { + if (scopeDepth == -1) { scopeDepth = callStackDepth; } - if ( scopeDepth == callStackDepth ) { + if (scopeDepth == callStackDepth) { func = currentFunction; - } else { - func = callStack[ scopeDepth ].f; } - if ( !func ) { + else { + func = callStack[scopeDepth].f; + } + if (!func) { return false; } - idStr::Copynz( funcObject, func->Name(), sizeof( funcObject ) ); - funcName = strstr( funcObject, "::" ); - if ( funcName ) { + idStr::Copynz(funcObject, func->Name(), sizeof(funcObject)); + 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; - } else { + 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; } } } - reg = GetVariable( d ); - switch( d->Type() ) { + if (!d) + { + out = "???"; + return false; + } + + reg = GetVariable(d); + switch (d->Type()) { case ev_float: - if ( reg.floatPtr ) { - out = va("%g", *reg.floatPtr ); - } else { + if (reg.floatPtr) { + out = va("%g", *reg.floatPtr); + } + else { out = "0"; } return true; break; case ev_vector: - if ( reg.vectorPtr ) { - out = va( "%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z ); - } else { + if (reg.vectorPtr) { + out = va("%g,%g,%g", reg.vectorPtr->x, reg.vectorPtr->y, reg.vectorPtr->z); + } + else { out = "0,0,0"; } return true; break; case ev_boolean: - if ( reg.intPtr ) { - out = va( "%d", *reg.intPtr ); - } else { + if (reg.intPtr) { + out = va("%d", *reg.intPtr); + } + else { out = "0"; } return true; break; case ev_field: - if ( scope == &def_namespace ) { + { + 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( &localstack[ callStack[ callStackDepth ].stackbase ] ); - if ( !field || !obj ) { + field = d->TypeDef()->FieldType(); + entity = GetEntity(*((int*)&localstack[localstackBase])); + if (!entity || !field) + { return false; } - switch ( field->Type() ) { + obj = &entity->scriptObject; + if (!obj) { + return false; + } + + switch (field->Type()) { case ev_boolean: - out = va( "%d", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); + out = va("%d", *(reinterpret_cast(&obj->data[reg.ptrOffset]))); return true; case ev_float: - out = va( "%g", *( reinterpret_cast( &obj->data[ reg.ptrOffset ] ) ) ); + out = va("%g", *(reinterpret_cast(&obj->data[reg.ptrOffset]))); return true; + case ev_string: { + const char* str; + str = reinterpret_cast(&obj->data[reg.ptrOffset]); + if (!str) { + out = "\"\""; + } + else { + out = "\""; + out += str; + out += "\""; + } + return true; + } + default: return false; } + break; + } case ev_string: - if ( reg.stringPtr ) { + if (reg.stringPtr) { out = "\""; out += reg.stringPtr; out += "\""; - } else { + } + else { out = "\"\""; } return true; @@ -313,7 +346,6 @@ bool idInterpreter::GetRegisterValue( const char *name, idStr &out, int scopeDep return false; } } - /* ================ idInterpreter::GetCallstackDepth @@ -969,6 +1001,20 @@ bool idInterpreter::Execute( void ) { // next statement st = &gameLocal.program.GetStatement( instructionPointer ); + if (gameLocal.editors & EDITOR_DEBUGGER) { + common->DebuggerCheckBreakpoint(this, &gameLocal.program, instructionPointer); + } + else if (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 +1879,99 @@ bool idInterpreter::Execute( void ) { return threadDying; } + + +bool idGameEdit::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 idGameEdit::ReturnedFromFunction(const idProgram* program, const idInterpreter* interpreter, int index) const +{ + + return (const_cast(program)->GetStatement(index).op == OP_RETURN && interpreter->GetCallstackDepth() <= 1); +} + +bool idGameEdit::GetRegisterValue(const idInterpreter* interpreter, const char* name, idStr& out, int scopeDepth) const +{ + return const_cast(interpreter)->GetRegisterValue(name, out, scopeDepth); +} + +const idThread* idGameEdit::GetThread(const idInterpreter* interpreter) const +{ + return interpreter->GetThread(); +} + +void idGameEdit::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(""); + msg->WriteString(""); + 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(program)->GetStatement(instructionPtr); + else // Use the calling statement as the filename and linenumber where the call was made from + st = &const_cast(program)->GetStatement(stack->s); + + if (st) + { + idStr qpath = const_cast(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(""); + msg->WriteInt(0); + } +} + +void idGameEdit::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 idGameEdit::GetInterpreterCallStackDepth(const idInterpreter* interpreter) +{ + return interpreter->GetCallstackDepth(); +} + +const function_t* idGameEdit::GetInterpreterCallStackFunction(const idInterpreter* interpreter, int stackDepth/* = -1*/) +{ + return interpreter->GetCallstack()[stackDepth > -1 ? stackDepth : interpreter->GetCallstackDepth()].f; +} \ No newline at end of file diff --git a/neo/d3xp/script/Script_Thread.cpp b/neo/d3xp/script/Script_Thread.cpp index cb826331..e320ec69 100644 --- a/neo/d3xp/script/Script_Thread.cpp +++ b/neo/d3xp/script/Script_Thread.cpp @@ -1921,3 +1921,49 @@ void idThread::Event_InfluenceActive( void ) { idThread::ReturnInt( false ); } } + +int idGameEdit::ThreadGetNum(const idThread* thread) const +{ + return const_cast(thread)->GetThreadNum(); +} + +const char* idGameEdit::ThreadGetName(const idThread* thread) const +{ + return const_cast(thread)->GetThreadName(); +} + +int idGameEdit::GetTotalScriptThreads() const +{ + return idThread::GetThreads().Num(); +} + +const idThread* idGameEdit::GetThreadByIndex(int index) const +{ + return idThread::GetThreads()[index]; +} + +bool idGameEdit::ThreadIsDoneProcessing(const idThread* thread) const +{ + return const_cast(thread)->IsDoneProcessing(); +} + +bool idGameEdit::ThreadIsWaiting(const idThread* thread) const +{ + return const_cast(thread)->IsWaiting(); +} + +bool idGameEdit::ThreadIsDying(const idThread* thread) const +{ + return const_cast(thread)->IsDying(); +} + +void idGameEdit::MSG_WriteThreadInfo(idBitMsg* msg, const idThread* thread, const idInterpreter* interpreter) +{ + msg->WriteString(const_cast(thread)->GetThreadName()); + msg->WriteInt(const_cast(thread)->GetThreadNum()); + + msg->WriteBits((int)(thread == interpreter->GetThread()), 1); + msg->WriteBits((int)const_cast(thread)->IsDoneProcessing(), 1); + msg->WriteBits((int)const_cast(thread)->IsWaiting(), 1); + msg->WriteBits((int)const_cast(thread)->IsDying(), 1); +} \ No newline at end of file