diff --git a/src/devtools/DebuggingServer.cpp b/src/devtools/DebuggingServer.cpp index 9cdb6427..86e2bdae 100644 --- a/src/devtools/DebuggingServer.cpp +++ b/src/devtools/DebuggingServer.cpp @@ -3,11 +3,62 @@ #include "engine/Engine.hpp" #include "network/Network.hpp" #include "debug/Logger.hpp" +#include "coders/json.hpp" using namespace devtools; static debug::Logger logger("debug-server"); +ClientConnection::~ClientConnection() { + if (auto connection = dynamic_cast( + network.getConnection(this->connection, true) + )) { + connection->close(); + } +} + +std::string ClientConnection::read() { + auto connection = dynamic_cast( + network.getConnection(this->connection, true) + ); + if (connection == nullptr) { + return ""; + } + if (messageLength == 0) { + if (connection->available() >= sizeof(int32_t)) { + int32_t length = 0; + connection->recv(reinterpret_cast(&length), sizeof(int32_t)); + if (length <= 0) { + logger.error() << "invalid message length " << length; + } else { + messageLength = length; + } + } + } else if (connection->available() >= messageLength) { + std::string string(messageLength, 0); + connection->recv(string.data(), messageLength); + return string; + } + return ""; +} + +void ClientConnection::send(const dv::value& object) { + auto connection = dynamic_cast( + network.getConnection(this->connection, true) + ); + if (connection == nullptr) { + return; + } + auto message = json::stringify(object, false); + int32_t length = message.length(); + connection->send(reinterpret_cast(&length), sizeof(int32_t)); + connection->send(message.data(), length); +} + +void ClientConnection::sendResponse(const std::string& type) { + send(dv::object({{"type", type}})); +} + static network::Server& create_tcp_server( DebuggingServer& dbgServer, Engine& engine, int port ) { @@ -15,12 +66,14 @@ static network::Server& create_tcp_server( u64id_t serverId = network.openTcpServer( port, [&network, &dbgServer](u64id_t sid, u64id_t id) { - auto& connection = *network.getConnection(id, true); + auto& connection = dynamic_cast( + *network.getConnection(id, true) + ); connection.setPrivate(true); logger.info() << "connected client " << id << ": " << connection.getAddress() << ":" << connection.getPort(); - dbgServer.setClient(connection); + dbgServer.setClient(id); } ); auto& server = *network.getServer(serverId, true); @@ -64,14 +117,55 @@ DebuggingServer::DebuggingServer( ) : engine(engine), server(create_server(*this, engine, serverString)), - client(nullptr) { + connection(nullptr) { } DebuggingServer::~DebuggingServer() { logger.info() << "stopping debugging server"; + server.close(); } bool DebuggingServer::update() { - return false; + if (connection == nullptr) { + return true; + } + std::string message = connection->read(); + if (message.empty()) { + return true; + } + logger.debug() << "received: " << message; + try { + auto obj = json::parse(message); + if (!obj.has("type")) { + logger.error() << "missing message type"; + return true; + } + const auto& type = obj["type"].asString(); + return performCommand(type, obj); + } catch (const std::runtime_error& err) { + logger.error() << "could not to parse message: " << err.what(); + } + return true; +} + +bool DebuggingServer::performCommand( + const std::string& type, const dv::value& map +) { + if (type == "terminate") { + engine.quit(); + connection->sendResponse("success"); + } else if (type == "detach") { + connection->sendResponse("success"); + connection.reset(); + return false; + } else { + logger.error() << "unsupported command '" << type << "'"; + } + return true; +} + +void DebuggingServer::setClient(u64id_t client) { + this->connection = + std::make_unique(engine.getNetwork(), client); } diff --git a/src/devtools/DebuggingServer.hpp b/src/devtools/DebuggingServer.hpp index 7f557935..b8b103b8 100644 --- a/src/devtools/DebuggingServer.hpp +++ b/src/devtools/DebuggingServer.hpp @@ -3,14 +3,38 @@ #include #include +#include "typedefs.hpp" + namespace network { class Server; class Connection; + class ReadableConnection; + class Network; +} + +namespace dv { + class value; } class Engine; namespace devtools { + class ClientConnection { + public: + ClientConnection(network::Network& network, u64id_t connection) + : network(network), connection(connection) { + } + ~ClientConnection(); + + std::string read(); + void send(const dv::value& message); + void sendResponse(const std::string& type); + private: + network::Network& network; + size_t messageLength = 0; + u64id_t connection; + }; + class DebuggingServer { public: DebuggingServer(Engine& engine, const std::string& serverString); @@ -18,12 +42,12 @@ namespace devtools { bool update(); - void setClient(network::Connection& client) { - this->client = &client; - } + void setClient(u64id_t client); private: Engine& engine; network::Server& server; - network::Connection* client; + std::unique_ptr connection; + + bool performCommand(const std::string& type, const dv::value& map); }; } diff --git a/src/engine/Engine.cpp b/src/engine/Engine.cpp index 23018946..65a0be87 100644 --- a/src/engine/Engine.cpp +++ b/src/engine/Engine.cpp @@ -282,6 +282,12 @@ void Engine::postUpdate() { network->update(); postRunnables.run(); scripting::process_post_runnables(); + + if (debuggingServer) { + if (!debuggingServer->update()) { + debuggingServer.reset(); + } + } } void Engine::updateFrontend() { diff --git a/src/engine/Engine.hpp b/src/engine/Engine.hpp index c523acb8..b0f0918d 100644 --- a/src/engine/Engine.hpp +++ b/src/engine/Engine.hpp @@ -51,7 +51,7 @@ struct CoreParameters { std::filesystem::path userFolder = "."; std::filesystem::path scriptFile; std::filesystem::path projectFolder; - std::string debugServerString; + std::string debugServerString = "tcp:9030"; int tps = 20; };