diff --git a/rwengine/src/script/ScriptMachine.cpp b/rwengine/src/script/ScriptMachine.cpp index e48bbd4f..6d43ae63 100644 --- a/rwengine/src/script/ScriptMachine.cpp +++ b/rwengine/src/script/ScriptMachine.cpp @@ -46,9 +46,9 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed) bool hasDebugging = !! bpHandler; - while( t.wakeCounter == 0 ) { - auto opcode = _file->read(t.programCounter); - auto opcorg = opcode; + while( t.wakeCounter == 0 ) { + auto pc = t.programCounter; + auto opcode = _file->read(pc); bool isNegatedConditional = ((opcode & SCM_NEGATE_CONDITIONAL_MASK) == SCM_NEGATE_CONDITIONAL_MASK); opcode = opcode & ~SCM_NEGATE_CONDITIONAL_MASK; @@ -56,13 +56,11 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed) ScriptFunctionMeta* foundcode; if( ! _ops->findOpcode(opcode, &foundcode) ) { - throw IllegalInstruction(opcode, t.programCounter, t.name); + throw IllegalInstruction(opcode, pc, t.name); } ScriptFunctionMeta& code = *foundcode; - // Keep the pc for the debugger - auto pc = t.programCounter; - t.programCounter += sizeof(SCMOpcode); + pc += sizeof(SCMOpcode); SCMParams parameters; @@ -70,7 +68,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed) auto requiredParams = std::abs(code.arguments); for( int p = 0; p < requiredParams || hasExtraParameters; ++p ) { - auto type_r = _file->read(t.programCounter); + auto type_r = _file->read(pc); auto type = static_cast(type_r); if( type_r > 42 ) { @@ -78,7 +76,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed) type = TString; } else { - t.programCounter += sizeof(SCMByte); + pc += sizeof(SCMByte); } parameters.push_back(SCMOpcodeParameter { type, { 0 } }); @@ -87,71 +85,74 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed) hasExtraParameters = false; break; case TInt8: - parameters.back().integer = _file->read(t.programCounter); - t.programCounter += sizeof(SCMByte); + parameters.back().integer = _file->read(pc); + pc += sizeof(SCMByte); break; case TInt16: - parameters.back().integer = _file->read(t.programCounter); - t.programCounter += sizeof(SCMByte) * 2; + parameters.back().integer = _file->read(pc); + pc += sizeof(SCMByte) * 2; break; case TGlobal: { - auto v = _file->read(t.programCounter); + auto v = _file->read(pc); parameters.back().globalPtr = globalData.data() + v; //* SCM_VARIABLE_SIZE; if( v >= _file->getGlobalsSize() ) { state->world->logger->error("SCM", "Global Out of bounds! "+ std::to_string(v) + " " + std::to_string(_file->getGlobalsSize())); } - t.programCounter += sizeof(SCMByte) * 2; + pc += sizeof(SCMByte) * 2; } break; case TLocal: { - auto v = _file->read(t.programCounter); + auto v = _file->read(pc); parameters.back().globalPtr = t.locals.data() + v * SCM_VARIABLE_SIZE; if( v >= SCM_THREAD_LOCAL_SIZE ) { state->world->logger->error("SCM", "Local Out of bounds!"); } - t.programCounter += sizeof(SCMByte) * 2; + pc += sizeof(SCMByte) * 2; } break; case TInt32: - parameters.back().integer = _file->read(t.programCounter); - t.programCounter += sizeof(SCMByte) * 4; + parameters.back().integer = _file->read(pc); + pc += sizeof(SCMByte) * 4; break; case TString: - std::copy(_file->data()+t.programCounter, _file->data()+t.programCounter+8, + std::copy(_file->data()+pc, _file->data()+pc+8, parameters.back().string); - t.programCounter += sizeof(SCMByte) * 8; + pc += sizeof(SCMByte) * 8; break; case TFloat16: - parameters.back().real = _file->read(t.programCounter) / 16.f; - t.programCounter += sizeof(SCMByte) * 2; + parameters.back().real = _file->read(pc) / 16.f; + pc += sizeof(SCMByte) * 2; break; default: - throw UnknownType(type, t.programCounter, t.name); + throw UnknownType(type, pc, t.name); break; }; } + ScriptArguments sca(¶meters, &t, this); + + if( hasDebugging ) + { + if( breakpoints.find(pc) != breakpoints.end() || interupt ) + { + interupt = false; + SCMBreakpoint bp; + bp.pc = t.programCounter; + bp.thread = &t; + bp.vm = this; + bp.function = &code; + bp.args = &sca; + bpHandler(bp); + } + } + + // After debugging has been completed, update the program counter + t.programCounter = pc; + if(code.function) - { - ScriptArguments sca(¶meters, &t, this); - - if( hasDebugging ) - { - if( breakpoints.find(pc) != breakpoints.end() || interupt ) - { - interupt = false; - SCMBreakpoint bp; - bp.pc = pc; - bp.thread = &t; - bp.vm = this; - bp.function = &code; - bp.args = &sca; - bpHandler(bp); - } - } - + { code.function(sca); } @@ -181,7 +182,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed) t.conditionResult = (t.conditionMask != 0); } - } + } SCMOpcodeParameter p; p.globalPtr = (t.locals.data() + 16 * sizeof ( SCMByte ) * 4); @@ -239,15 +240,12 @@ void ScriptMachine::execute(float dt) { int ms = dt * 1000.f; for(size_t ti = 0; ti < _activeThreads.size(); ++ti) { - auto thread = _activeThreads[ti]; + auto& thread = _activeThreads[ti]; executeThread( thread, ms ); if( thread.finished ) { _activeThreads.erase( _activeThreads.begin() + ti ); - } - else { - _activeThreads[ti] = thread; - } + } } } diff --git a/rwengine/src/script/modules/GameModule.cpp b/rwengine/src/script/modules/GameModule.cpp index 19e7931d..cc2e1aac 100644 --- a/rwengine/src/script/modules/GameModule.cpp +++ b/rwengine/src/script/modules/GameModule.cpp @@ -903,6 +903,7 @@ GameModule::GameModule() bindFunction(0x015A, game_reset_camera, 0, "Reset Camera" ); bindFunction(0x015C, game_set_zone_ped_info, 11, "Set zone ped info" ); + bindUnimplemented( 0x015D, game_set_timescale;, 1, "Set Game Timescale" ); bindFunction(0x015F, game_camera_fixed_position, 6, "Set Fixed Camera Position" ); bindFunction(0x0160, game_camera_lookat_position, 4, "Point Camera at Point" ); diff --git a/rwengine/src/script/modules/ObjectModule.cpp b/rwengine/src/script/modules/ObjectModule.cpp index b4a03c9b..d615910b 100644 --- a/rwengine/src/script/modules/ObjectModule.cpp +++ b/rwengine/src/script/modules/ObjectModule.cpp @@ -293,6 +293,23 @@ bool game_player_near_point_on_foot_3D(const ScriptArguments& args) return false; } +bool game_player_near_point_in_vehicle_3D(const ScriptArguments& args) +{ + auto character = static_cast(args.getPlayerCharacter(0)); + glm::vec3 center(args[1].real, args[2].real, args[3].real); + glm::vec3 size(args[4].real, args[5].real, args[6].real); + bool unkown = !!args[7].integer; + + auto vehicle = character->getCurrentVehicle(); + if( vehicle ) { + auto distance = center - character->getPosition(); + distance /= size; + if( glm::length( distance ) < 1.f ) return true; + } + + return false; +} + bool game_character_near_point_in_vehicle(const ScriptArguments& args) { auto character = static_cast(args.getObject(0)); @@ -432,6 +449,21 @@ void game_create_character_in_vehicle(const ScriptArguments& args) *args[3].globalInteger = character->getGameObjectID(); } +bool game_is_vehicle_model(const ScriptArguments& args) +{ + auto vdata = args.getWorld()->data->findObjectType(args[1].integer); + if( vdata ) + { + auto vehicle = args.getObject(0); + if ( vehicle ) { + + return vehicle->model && vdata->modelName == vehicle->model->name; + } + } + return false; +} + + void game_set_player_heading(const ScriptArguments& args) { auto object = args.getPlayerCharacter(0); @@ -1016,6 +1048,7 @@ ObjectModule::ObjectModule() bindUnimplemented( 0x00F5, game_locate_character_in_sphere, 8, "Locate Player In Sphere" ); bindFunction(0x00F6, game_player_near_point_on_foot_3D, 8, "Is Player near point on foot" ); + bindFunction(0x00F7, game_player_near_point_in_vehicle_3D, 8, "Is Player near point in car" ); bindFunction(0x0100, game_character_near_point_in_vehicle, 8, "Is Character near point in car" ); @@ -1031,6 +1064,8 @@ ObjectModule::ObjectModule() bindFunction(0x0129, game_create_character_in_vehicle, 4, "Create Character In Car" ); bindUnimplemented(0x0135, game_set_vehicle_locked, 2, "Set Vehicle locked state"); + + bindFunction(0x0137, game_is_vehicle_model, 2, "Is Vehicle Model" ); bindFunction(0x0171, game_set_player_heading, 2, "Set Player Heading" ); diff --git a/rwgame/debug/HttpServer.cpp b/rwgame/debug/HttpServer.cpp index 46b0f191..af38be14 100644 --- a/rwgame/debug/HttpServer.cpp +++ b/rwgame/debug/HttpServer.cpp @@ -11,11 +11,13 @@ var app = angular.module('debugApp', []); app.controller('DebugCtrl', function($scope,$http) { $scope.threads = []; $scope.running = true; + $scope.breakpoint = {}; $scope.refresh = function() { var promise = $http.get('/state') .success(function(data, status, headers, config) { $scope.running = data.status == 'running'; $scope.threads = data.threads; + $scope.breakpoint = data.breakpoint; }); }; $scope.interrupt = function() { @@ -73,10 +75,11 @@ html,body { font-family: sans-serif; } Game is running
+ {{ breakpoint.program_counter }} {{ breakpoint.thread }}

Threads

  • -

    {{thread.name}}

    +

    {{thread.name}} ({{thread.address}})

    program counter: {{thread.program_counter}}
    wake counter: {{thread.wake_counter}} ms

    Return Stack

    @@ -87,7 +90,8 @@ html,body { font-family: sans-serif; }

    Disassembly

    -
    +
    {{ call.address }} @@ -117,12 +121,11 @@ html,body { font-family: sans-serif; } )"; HttpServer::HttpServer(RWGame* game, GameWorld* world) - : game(game), world(world), paused(false) + : game(game), world(world), paused(false), lastBreakpoint(nullptr) {} void HttpServer::run() { - listener.create(); listener.listen(8091); std::cout << "STARTING HTTP SERVER" << std::endl; @@ -161,6 +164,7 @@ void HttpServer::run() void HttpServer::handleBreakpoint(const SCMBreakpoint &bp) { + lastBreakpoint = &bp; paused = true; while( paused ) { @@ -237,7 +241,7 @@ std::string thread_disassembly(ScriptMachine* script, SCMThread& th) ss << "{"; if( script->getOpcodes()->findOpcode((uint16_t)opcode, &meta) ) { - ss << "\"address\": " << it->first << "," + ss << "\"address\": \"" << it->first << "\"," << "\"function\": \"" << meta->signature << "\"," << "\"arguments\": ["; SCMParams& parameters = it->second.parameters; @@ -267,6 +271,7 @@ std::string thread(ScriptMachine* script, SCMThread& th) { std::stringstream ss; ss << "{" + << "\"address\": \"" << &th << "\"," << "\"program_counter\": " << th.programCounter << "," << "\"name\": \"" << th.name << "\"," << "\"wake_counter\": " << th.wakeCounter << "," @@ -276,6 +281,20 @@ std::string thread(ScriptMachine* script, SCMThread& th) return ss.str(); } +std::string breakpoint(const SCMBreakpoint* breakpoint) +{ + std::stringstream ss; + ss << "{"; + if( breakpoint != nullptr ) + { + ss << "\"program_counter\": " << breakpoint->pc << "," + << "\"thread\": \"" << breakpoint->thread << "\""; + } + ss << "}"; + + return ss.str(); +} + std::string HttpServer::getState() const { if( !paused ) { @@ -285,12 +304,14 @@ std::string HttpServer::getState() const std::stringstream ss; ss << "{"; ss << R"("status":"interrupted",)"; + ss << R"("breakpoint": )" << breakpoint(lastBreakpoint) << ","; ss << R"("threads": [)"; for(unsigned int i = 0; i < game->getScript()->getThreads().size(); ++i) { - SCMThread& th = game->getScript()->getThreads()[i]; - bool last = (game->getScript()->getThreads().size() == i+1); - ss << thread(game->getScript(), th) << (last ? "" : ","); + if( i != 0 ) + ss << ","; + SCMThread& th = game->getScript()->getThreads().at(i); + ss << thread(game->getScript(), th); } ss << R"(])"; ss << "}"; @@ -320,12 +341,14 @@ std::string HttpServer::dispatch(std::string method, std::string path) else if(path == "/step") { if( paused ) { game->getScript()->interuptNext(); + lastBreakpoint = nullptr; paused = false; } ss << getState(); mime = "application/json"; } else if(path == "/continue") { + lastBreakpoint = nullptr; paused = false; ss << getState(); mime = "application/json"; diff --git a/rwgame/debug/HttpServer.hpp b/rwgame/debug/HttpServer.hpp index 242504de..93def89c 100644 --- a/rwgame/debug/HttpServer.hpp +++ b/rwgame/debug/HttpServer.hpp @@ -10,20 +10,6 @@ #include #endif -class ReuseableListener : public sf::TcpListener -{ - public: - void create() { - char reuse = 1; - sf::SocketHandle handle = socket(PF_INET, SOCK_STREAM, 0); - if( setsockopt(handle, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0 ) - { - std::cerr << "Failed to set socket SO_REUSEADDR: errno = " << errno << std::endl; - } - Socket::create(handle); - } -}; - class HttpServer { public: @@ -32,10 +18,11 @@ public: void handleBreakpoint(const SCMBreakpoint& bp); private: - ReuseableListener listener; + sf::TcpListener listener; RWGame* game; GameWorld* world; bool paused; + const SCMBreakpoint* lastBreakpoint; std::string dispatch(std::string method, std::string path); std::string getState() const;