1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-15 15:02:34 +02:00

Add breakpoints to ScriptMachine, remove from GameWorld.

+ Adds breakpoints on program counter values to the ScriptMachine.
+ Adds breakpoint handler for acting on breakpoints
+ Remove GameWorld::script and make RWGame responsible for script
This commit is contained in:
Daniel Evans 2015-04-04 03:12:28 +01:00
parent a20d170d16
commit 159510cace
7 changed files with 133 additions and 128 deletions

View File

@ -23,8 +23,6 @@ class VehicleObject;
struct WeaponScan; struct WeaponScan;
class ScriptMachine;
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <btBulletDynamicsCommon.h> #include <btBulletDynamicsCommon.h>
@ -70,8 +68,6 @@ public:
*/ */
bool defineItems(const std::string& name); bool defineItems(const std::string& name);
void runScript(const std::string& name);
/** /**
* Loads an IPL into the game. * Loads an IPL into the game.
* @param name The name of the IPL as it appears in the games' gta.dat * @param name The name of the IPL as it appears in the games' gta.dat
@ -241,8 +237,6 @@ public:
*/ */
WorkContext* _work; WorkContext* _work;
ScriptMachine* script;
/** /**
* @brief Loads and starts the named cutscene. * @brief Loads and starts the named cutscene.
* @param name * @param name

View File

@ -6,6 +6,7 @@
#include <iomanip> #include <iomanip>
#include <string> #include <string>
#include <stack> #include <stack>
#include <set>
#define SCM_NEGATE_CONDITIONAL_MASK 0x8000 #define SCM_NEGATE_CONDITIONAL_MASK 0x8000
#define SCM_CONDITIONAL_MASK_PASSED 0xFF #define SCM_CONDITIONAL_MASK_PASSED 0xFF
@ -130,18 +131,20 @@ struct SCMThread
std::stack<pc_t> calls; std::stack<pc_t> calls;
}; };
/**
* Breakpoint callback information
*/
struct SCMBreakpoint
{
SCMThread::pc_t pc;
SCMThread* thread;
ScriptMachine* vm;
ScriptFunctionMeta* function;
ScriptArguments* args;
};
class ScriptMachine class ScriptMachine
{ {
SCMFile* _file;
SCMOpcodes* _ops;
GameWorld* _world;
std::vector<SCMThread> _activeThreads;
void executeThread(SCMThread& t, int msPassed);
SCMByte* _globals;
public: public:
ScriptMachine(GameWorld* world, SCMFile* file, SCMOpcodes* ops); ScriptMachine(GameWorld* world, SCMFile* file, SCMOpcodes* ops);
~ScriptMachine(); ~ScriptMachine();
@ -153,11 +156,32 @@ public:
SCMByte* getGlobals(); SCMByte* getGlobals();
GameWorld* getWorld() const { return _world; } GameWorld* getWorld() const { return _world; }
typedef std::function<void (const SCMBreakpoint&)> BreakpointHandler;
void setBreakpointHandler(const BreakpointHandler& handler);
void addBreakpoint(SCMThread::pc_t pc);
void removeBreakpoint(SCMThread::pc_t pc);
/** /**
* @brief executes threads until they are all in waiting state. * @brief executes threads until they are all in waiting state.
*/ */
void execute(float dt); void execute(float dt);
private:
SCMFile* _file;
SCMOpcodes* _ops;
GameWorld* _world;
std::vector<SCMThread> _activeThreads;
void executeThread(SCMThread& t, int msPassed);
SCMByte* _globals;
BreakpointHandler bpHandler;
std::set<SCMThread::pc_t> breakpoints;
}; };
#endif #endif

View File

@ -11,11 +11,6 @@
#include <data/WeaponData.hpp> #include <data/WeaponData.hpp>
#include <WorkContext.hpp> #include <WorkContext.hpp>
#include <script/ScriptMachine.hpp>
#include <script/modules/VMModule.hpp>
#include <script/modules/GameModule.hpp>
#include <script/modules/ObjectModule.hpp>
// 3 isn't enough to cause a factory. // 3 isn't enough to cause a factory.
#include <objects/CharacterObject.hpp> #include <objects/CharacterObject.hpp>
#include <objects/InstanceObject.hpp> #include <objects/InstanceObject.hpp>
@ -80,7 +75,7 @@ public:
GameWorld::GameWorld(Logger* log, const std::string& path) GameWorld::GameWorld(Logger* log, const std::string& path)
: logger(log), gameTime(0.f), gameData(log, path), randomEngine(rand()), : logger(log), gameTime(0.f), gameData(log, path), randomEngine(rand()),
_work( new WorkContext( this ) ), script(nullptr), cutsceneAudio(nullptr), missionAudio(nullptr), _work( new WorkContext( this ) ), cutsceneAudio(nullptr), missionAudio(nullptr),
paused(false) paused(false)
{ {
gameData.engine = this; gameData.engine = this;
@ -144,24 +139,6 @@ bool GameWorld::defineItems(const std::string& name)
return false; return false;
} }
void GameWorld::runScript(const std::string &name)
{
SCMFile* f = gameData.loadSCM(name);
if( f ) {
if( script ) delete script;
SCMOpcodes* opcodes = new SCMOpcodes;
opcodes->modules.push_back(new VMModule);
opcodes->modules.push_back(new GameModule);
opcodes->modules.push_back(new ObjectModule);
script = new ScriptMachine(this, f, opcodes);
}
else {
logger->error("World", "Failed to load SCM: " + name);
}
}
bool GameWorld::placeItems(const std::string& name) bool GameWorld::placeItems(const std::string& name)
{ {
auto i = gameData.iplLocations.find(name); auto i = gameData.iplLocations.find(name);

View File

@ -29,26 +29,13 @@ bool SCMOpcodes::findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out)
void ScriptMachine::executeThread(SCMThread &t, int msPassed) void ScriptMachine::executeThread(SCMThread &t, int msPassed)
{ {
#if SCM_DEBUG_INSTRUCTIONS
std::string threadfilter;
if(getenv("SCM_DEBUG_THREAD"))
{
threadfilter = getenv("SCM_DEBUG_THREAD");
}
bool debug_op = threadfilter.empty() || threadfilter.find(t.name) != std::string::npos || threadfilter.find(std::to_string(t.baseAddress)) != std::string::npos;
if ( debug_op )
{
_world->logger.verbose("SCM", "Thread " + t.name + " woke at " + std::to_string(t.wakeCounter) );
}
#endif
if( t.wakeCounter > 0 ) { if( t.wakeCounter > 0 ) {
t.wakeCounter = std::max( t.wakeCounter - msPassed, 0 ); t.wakeCounter = std::max( t.wakeCounter - msPassed, 0 );
} }
if( t.wakeCounter > 0 ) return; if( t.wakeCounter > 0 ) return;
bool hasDebugging = !! bpHandler;
while( t.wakeCounter == 0 ) { while( t.wakeCounter == 0 ) {
auto opcode = _file->read<SCMOpcode>(t.programCounter); auto opcode = _file->read<SCMOpcode>(t.programCounter);
auto opcorg = opcode; auto opcorg = opcode;
@ -63,6 +50,8 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
} }
ScriptFunctionMeta& code = *foundcode; ScriptFunctionMeta& code = *foundcode;
// Keep the pc for the debugger
auto pc = t.programCounter;
t.programCounter += sizeof(SCMOpcode); t.programCounter += sizeof(SCMOpcode);
SCMParams parameters; SCMParams parameters;
@ -126,81 +115,25 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
}; };
} }
if(! code.function) if(code.function)
{ {
#if SCM_DEBUG_INSTRUCTIONS
std::cout << std::setw(7) << std::setfill(' ') << t.name <<
" " << std::dec << std::setw(8) << std::setfill(' ') << t.programCounter <<
" " << std::hex << std::setw(4) << std::setfill('0') << opcorg <<
" " << std::dec;
for(SCMOpcodeParameter& p : parameters) {
std::cout << p.type << ":";
switch(p.type) {
case TGlobal:
case TLocal:
std::cout << *p.globalInteger;
break;
case TInt8:
case TInt16:
case TInt32:
std::cout << p.integer;
break;
case TFloat16:
std::cout << p.real;
break;
case TString:
std::cout << p.string;
break;
}
std::cout << " ";
}
std::cout << code.signature << " unimplemented"<< std::endl;
#endif
}
else
{
#if SCM_DEBUG_INSTRUCTIONS
if( debug_op )
{
std::cout << std::setw(7) << std::setfill(' ') << t.name <<
" " << std::dec << std::setw(8) << std::setfill(' ') << t.programCounter <<
" " << std::hex << std::setw(4) << std::setfill('0') << opcorg <<
" " << std::dec;
for(SCMOpcodeParameter& p : parameters) {
std::cout << p.type << ":";
switch(p.type) {
case TGlobal:
case TLocal:
std::cout << *p.globalInteger << "(" << p.globalInteger << ")";
break;
case TInt8:
case TInt16:
case TInt32:
std::cout << p.integer;
break;
case TFloat16:
std::cout << p.real;
break;
case TString:
std::cout << p.string;
break;
}
std::cout << " ";
}
std::cout << code.signature << std::endl;
}
#endif
ScriptArguments sca(&parameters, &t, this); ScriptArguments sca(&parameters, &t, this);
code.function(sca);
#if SCM_DEBUG_INSTRUCTIONS if( hasDebugging )
if( debug_op )
{ {
if( code.conditional ) if( breakpoints.find(pc) != breakpoints.end() )
{ {
std::cout << " => " << t.conditionResult << std::endl; SCMBreakpoint bp;
bp.pc = pc;
bp.thread = &t;
bp.vm = this;
bp.function = &code;
bp.args = &sca;
bpHandler(bp);
} }
} }
#endif
code.function(sca);
} }
if(isNegatedConditional) { if(isNegatedConditional) {
@ -299,3 +232,20 @@ void ScriptMachine::execute(float dt)
} }
} }
} }
void ScriptMachine::setBreakpointHandler(const ScriptMachine::BreakpointHandler& handler)
{
bpHandler = handler;
}
void ScriptMachine::addBreakpoint(SCMThread::pc_t pc)
{
breakpoints.insert(pc);
}
void ScriptMachine::removeBreakpoint(SCMThread::pc_t pc)
{
breakpoints.erase(pc);
}

View File

@ -10,7 +10,11 @@
#include <engine/GameWorld.hpp> #include <engine/GameWorld.hpp>
#include <render/GameRenderer.hpp> #include <render/GameRenderer.hpp>
#include <render/DebugDraw.hpp> #include <render/DebugDraw.hpp>
#include <script/ScriptMachine.hpp> #include <script/ScriptMachine.hpp>
#include <script/modules/VMModule.hpp>
#include <script/modules/GameModule.hpp>
#include <script/modules/ObjectModule.hpp>
#include <data/CutsceneData.hpp> #include <data/CutsceneData.hpp>
#include <ai/PlayerController.hpp> #include <ai/PlayerController.hpp>
@ -22,12 +26,13 @@ DebugDraw* debug;
StdOutReciever logPrinter; StdOutReciever logPrinter;
RWGame::RWGame(const std::string& gamepath, int argc, char* argv[]) RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
: engine(nullptr), renderer(nullptr), inFocus(true), showDebugStats(false), : engine(nullptr), renderer(nullptr), script(nullptr), inFocus(true), showDebugStats(false),
accum(0.f), timescale(1.f) accum(0.f), timescale(1.f)
{ {
size_t w = GAME_WINDOW_WIDTH, h = GAME_WINDOW_HEIGHT; size_t w = GAME_WINDOW_WIDTH, h = GAME_WINDOW_HEIGHT;
bool fullscreen = false; bool fullscreen = false;
bool newgame = false; bool newgame = false;
bool debugscript = false;
for( int i = 1; i < argc; ++i ) for( int i = 1; i < argc; ++i )
{ {
@ -47,6 +52,10 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
{ {
newgame = true; newgame = true;
} }
if( strcmp( "--debug", argv[i] ) == 0 )
{
debugscript = true;
}
} }
@ -128,10 +137,51 @@ RWGame::RWGame(const std::string& gamepath, int argc, char* argv[])
RWGame::~RWGame() RWGame::~RWGame()
{ {
delete script;
delete renderer; delete renderer;
delete engine; delete engine;
} }
void RWGame::startScript(const std::string& name)
{
SCMFile* f = engine->gameData.loadSCM(name);
if( f ) {
if( script ) delete script;
SCMOpcodes* opcodes = new SCMOpcodes;
opcodes->modules.push_back(new VMModule);
opcodes->modules.push_back(new GameModule);
opcodes->modules.push_back(new ObjectModule);
script = new ScriptMachine(engine, f, opcodes);
// Set up breakpoint handler
script->setBreakpointHandler(
[&](const SCMBreakpoint& bp)
{
log.info("Script", "Breakpoint hit!");
std::stringstream ss;
ss << " " << bp.function->description << ".";
ss << " Args:";
for(int a = 0; a < bp.args->getParameters().size(); a++)
{
auto& arg = bp.args->getParameters()[a];
ss << " " << arg.integerValue();
if( a != bp.args->getParameters().size()-1 )
{
ss << ",";
}
}
log.info("Script", ss.str());
});
script->addBreakpoint(0);
}
else {
log.error("Game", "Failed to load SCM: " + name);
}
}
int RWGame::run() int RWGame::run()
{ {
clock.restart(); clock.restart();
@ -250,9 +300,9 @@ void RWGame::tick(float dt)
engine->dynamicsWorld->stepSimulation(dt, 2, dt); engine->dynamicsWorld->stepSimulation(dt, 2, dt);
if( engine->script ) { if( script ) {
try { try {
engine->script->execute(dt); script->execute(dt);
} }
catch( SCMException& ex ) { catch( SCMException& ex ) {
std::cerr << ex.what() << std::endl; std::cerr << ex.what() << std::endl;

View File

@ -4,6 +4,7 @@
#include <core/Logger.hpp> #include <core/Logger.hpp>
#include <engine/GameWorld.hpp> #include <engine/GameWorld.hpp>
#include <render/GameRenderer.hpp> #include <render/GameRenderer.hpp>
#include <script/ScriptMachine.hpp>
#include "game.hpp" #include "game.hpp"
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
@ -14,6 +15,7 @@ class RWGame
GameWorld* engine; GameWorld* engine;
// must be allocated after Logger setup. // must be allocated after Logger setup.
GameRenderer* renderer; GameRenderer* renderer;
ScriptMachine* script;
sf::RenderWindow window; sf::RenderWindow window;
sf::Clock clock; sf::Clock clock;
bool inFocus; bool inFocus;
@ -45,6 +47,11 @@ public:
return window; return window;
} }
ScriptMachine* getScript() const
{
return script;
}
bool hitWorldRay(glm::vec3 &hit, glm::vec3 &normal, GameObject** object = nullptr) bool hitWorldRay(glm::vec3 &hit, glm::vec3 &normal, GameObject** object = nullptr)
{ {
auto vc = nextCam; auto vc = nextCam;
@ -74,6 +81,8 @@ public:
} }
return false; return false;
} }
void startScript(const std::string& name);
private: private:
void tick(float dt); void tick(float dt);

View File

@ -11,6 +11,7 @@
#include <render/Model.hpp> #include <render/Model.hpp>
#include <items/WeaponItem.hpp> #include <items/WeaponItem.hpp>
#include <engine/GameWorld.hpp> #include <engine/GameWorld.hpp>
#include <script/ScriptMachine.hpp>
#define AUTOLOOK_TIME 2.f #define AUTOLOOK_TIME 2.f
@ -68,7 +69,7 @@ void IngameState::startTest()
void IngameState::startGame() void IngameState::startGame()
{ {
getWorld()->runScript("data/main.scm"); game->startScript("data/main.scm");
getWorld()->sound.playBackground( getWorld()->gameData.getDataPath() + "/audio/City.wav" ); getWorld()->sound.playBackground( getWorld()->gameData.getDataPath() + "/audio/City.wav" );
} }