From b14564bcb33930f183a370fe4bfdb16009613f33 Mon Sep 17 00:00:00 2001 From: offtkp <parisoplop@gmail.com> Date: Fri, 28 Jul 2023 13:16:39 +0300 Subject: [PATCH] Add /step http server command --- include/httpserver.hpp | 5 ++- src/httpserver.cpp | 80 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/include/httpserver.hpp b/include/httpserver.hpp index 08e98bbf..628ad057 100644 --- a/include/httpserver.hpp +++ b/include/httpserver.hpp @@ -13,7 +13,7 @@ #include "helpers.hpp" -enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset, LoadRom }; +enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset, LoadRom, Step }; class Emulator; namespace httplib { @@ -46,6 +46,7 @@ class HttpAction { static std::unique_ptr<HttpAction> createLoadRomAction(DeferredResponseWrapper& response, const std::filesystem::path& path, bool paused); static std::unique_ptr<HttpAction> createTogglePauseAction(); static std::unique_ptr<HttpAction> createResetAction(); + static std::unique_ptr<HttpAction> createStepAction(DeferredResponseWrapper& response, int frames); }; struct HttpServer { @@ -62,9 +63,11 @@ struct HttpServer { std::thread httpServerThread; std::queue<std::unique_ptr<HttpAction>> actionQueue; std::mutex actionQueueMutex; + std::unique_ptr<HttpAction> currentStepAction {}; std::map<std::string, u32> keyMap; bool paused = false; + int framesToRun = 0; void startHttpServer(); void pushAction(std::unique_ptr<HttpAction> action); diff --git a/src/httpserver.cpp b/src/httpserver.cpp index cccd69d9..714b2fee 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -55,6 +55,18 @@ class HttpActionLoadRom : public HttpAction { bool getPaused() const { return paused; } }; +class HttpActionStep : public HttpAction { + DeferredResponseWrapper& response; + int frames; + + public: + HttpActionStep(DeferredResponseWrapper& response, int frames) + : HttpAction(HttpActionType::Step), response(response), frames(frames) {} + + DeferredResponseWrapper& getResponse() { return response; } + int getFrames() const { return frames; } +}; + std::unique_ptr<HttpAction> HttpAction::createScreenshotAction(DeferredResponseWrapper& response) { return std::make_unique<HttpActionScreenshot>(response); } @@ -67,6 +79,10 @@ std::unique_ptr<HttpAction> HttpAction::createLoadRomAction(DeferredResponseWrap return std::make_unique<HttpActionLoadRom>(response, path, paused); } +std::unique_ptr<HttpAction> HttpAction::createStepAction(DeferredResponseWrapper& response, int frames) { + return std::make_unique<HttpActionStep>(response, frames); +} + HttpServer::HttpServer(Emulator* emulator) : emulator(emulator), server(std::make_unique<httplib::Server>()), keyMap({ {"A", {HID::Keys::A}}, @@ -99,6 +115,7 @@ void HttpServer::pushAction(std::unique_ptr<HttpAction> action) { } void HttpServer::startHttpServer() { + server->set_tcp_nodelay(true); server->Get("/ping", [](const httplib::Request&, httplib::Response& response) { response.set_content("pong", "text/plain"); }); server->Get("/screen", [this](const httplib::Request&, httplib::Response& response) { @@ -135,9 +152,30 @@ void HttpServer::startHttpServer() { response.set_content(ok ? "ok" : "error", "text/plain"); }); - server->Get("/step", [this](const httplib::Request&, httplib::Response& response) { - // TODO: implement /step - response.set_content("ok", "text/plain"); + server->Get("/step", [this](const httplib::Request& request, httplib::Response& response) { + auto it = request.params.find("frames"); + if (it == request.params.end()) { + response.set_content("error", "text/plain"); + return; + } + + int frames; + try { + frames = std::stoi(it->second); + } catch (...) { + response.set_content("error", "text/plain"); + return; + } + + if (frames <= 0) { + response.set_content("error", "text/plain"); + return; + } + + DeferredResponseWrapper wrapper(response); + std::unique_lock lock(wrapper.mutex); + pushAction(HttpAction::createStepAction(wrapper, frames)); + wrapper.cv.wait(lock, [&wrapper] { return wrapper.ready; }); }); server->Get("/status", [this](const httplib::Request&, httplib::Response& response) { response.set_content(status(), "text/plain"); }); @@ -171,7 +209,6 @@ void HttpServer::startHttpServer() { DeferredResponseWrapper wrapper(response); std::unique_lock lock(wrapper.mutex); pushAction(HttpAction::createLoadRomAction(wrapper, romPath, paused)); - response.set_content("ok", "text/plain"); wrapper.cv.wait(lock, [&wrapper] { return wrapper.ready; }); }); @@ -209,6 +246,31 @@ std::string HttpServer::status() { void HttpServer::processActions() { std::scoped_lock lock(actionQueueMutex); + if (framesToRun > 0) { + if (!currentStepAction) { + // Should never happen + printf("framesToRun > 0 but no currentStepAction\n"); + return; + } + + emulator->resume(); + framesToRun--; + + if (framesToRun == 0) { + paused = true; + emulator->pause(); + + DeferredResponseWrapper& response = reinterpret_cast<HttpActionStep*>(currentStepAction.get())->getResponse(); + response.inner_response.set_content("ok", "text/plain"); + std::unique_lock<std::mutex> lock(response.mutex); + response.ready = true; + response.cv.notify_one(); + } + + // Don't process more actions until we're done stepping + return; + } + HIDService& hid = emulator->kernel.getServiceManager().getHID(); while (!actionQueue.empty()) { @@ -253,6 +315,7 @@ void HttpServer::processActions() { if (loaded) { paused = loadRomAction->getPaused(); + framesToRun = 0; if (paused) { emulator->pause(); } else { @@ -263,11 +326,20 @@ void HttpServer::processActions() { } case HttpActionType::TogglePause: + framesToRun = 0; emulator->togglePause(); paused = !paused; break; + case HttpActionType::Reset: emulator->reset(Emulator::ReloadOption::Reload); break; + case HttpActionType::Step: { + HttpActionStep* stepAction = static_cast<HttpActionStep*>(action.get()); + framesToRun = stepAction->getFrames(); + currentStepAction = std::move(action); + break; + } + default: break; } }