Add /step http server command

This commit is contained in:
offtkp 2023-07-28 13:16:39 +03:00
parent ddf0ca19ee
commit b14564bcb3
2 changed files with 80 additions and 5 deletions

View file

@ -13,7 +13,7 @@
#include "helpers.hpp" #include "helpers.hpp"
enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset, LoadRom }; enum class HttpActionType { None, Screenshot, Key, TogglePause, Reset, LoadRom, Step };
class Emulator; class Emulator;
namespace httplib { 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> createLoadRomAction(DeferredResponseWrapper& response, const std::filesystem::path& path, bool paused);
static std::unique_ptr<HttpAction> createTogglePauseAction(); static std::unique_ptr<HttpAction> createTogglePauseAction();
static std::unique_ptr<HttpAction> createResetAction(); static std::unique_ptr<HttpAction> createResetAction();
static std::unique_ptr<HttpAction> createStepAction(DeferredResponseWrapper& response, int frames);
}; };
struct HttpServer { struct HttpServer {
@ -62,9 +63,11 @@ struct HttpServer {
std::thread httpServerThread; std::thread httpServerThread;
std::queue<std::unique_ptr<HttpAction>> actionQueue; std::queue<std::unique_ptr<HttpAction>> actionQueue;
std::mutex actionQueueMutex; std::mutex actionQueueMutex;
std::unique_ptr<HttpAction> currentStepAction {};
std::map<std::string, u32> keyMap; std::map<std::string, u32> keyMap;
bool paused = false; bool paused = false;
int framesToRun = 0;
void startHttpServer(); void startHttpServer();
void pushAction(std::unique_ptr<HttpAction> action); void pushAction(std::unique_ptr<HttpAction> action);

View file

@ -55,6 +55,18 @@ class HttpActionLoadRom : public HttpAction {
bool getPaused() const { return paused; } 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) { std::unique_ptr<HttpAction> HttpAction::createScreenshotAction(DeferredResponseWrapper& response) {
return std::make_unique<HttpActionScreenshot>(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); 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) HttpServer::HttpServer(Emulator* emulator)
: emulator(emulator), server(std::make_unique<httplib::Server>()), keyMap({ : emulator(emulator), server(std::make_unique<httplib::Server>()), keyMap({
{"A", {HID::Keys::A}}, {"A", {HID::Keys::A}},
@ -99,6 +115,7 @@ void HttpServer::pushAction(std::unique_ptr<HttpAction> action) {
} }
void HttpServer::startHttpServer() { 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("/ping", [](const httplib::Request&, httplib::Response& response) { response.set_content("pong", "text/plain"); });
server->Get("/screen", [this](const httplib::Request&, httplib::Response& response) { 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"); response.set_content(ok ? "ok" : "error", "text/plain");
}); });
server->Get("/step", [this](const httplib::Request&, httplib::Response& response) { server->Get("/step", [this](const httplib::Request& request, httplib::Response& response) {
// TODO: implement /step auto it = request.params.find("frames");
response.set_content("ok", "text/plain"); 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"); }); 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); DeferredResponseWrapper wrapper(response);
std::unique_lock lock(wrapper.mutex); std::unique_lock lock(wrapper.mutex);
pushAction(HttpAction::createLoadRomAction(wrapper, romPath, paused)); pushAction(HttpAction::createLoadRomAction(wrapper, romPath, paused));
response.set_content("ok", "text/plain");
wrapper.cv.wait(lock, [&wrapper] { return wrapper.ready; }); wrapper.cv.wait(lock, [&wrapper] { return wrapper.ready; });
}); });
@ -209,6 +246,31 @@ std::string HttpServer::status() {
void HttpServer::processActions() { void HttpServer::processActions() {
std::scoped_lock lock(actionQueueMutex); 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(); HIDService& hid = emulator->kernel.getServiceManager().getHID();
while (!actionQueue.empty()) { while (!actionQueue.empty()) {
@ -253,6 +315,7 @@ void HttpServer::processActions() {
if (loaded) { if (loaded) {
paused = loadRomAction->getPaused(); paused = loadRomAction->getPaused();
framesToRun = 0;
if (paused) { if (paused) {
emulator->pause(); emulator->pause();
} else { } else {
@ -263,11 +326,20 @@ void HttpServer::processActions() {
} }
case HttpActionType::TogglePause: case HttpActionType::TogglePause:
framesToRun = 0;
emulator->togglePause(); emulator->togglePause();
paused = !paused; paused = !paused;
break; break;
case HttpActionType::Reset: emulator->reset(Emulator::ReloadOption::Reload); 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; default: break;
} }
} }