diff --git a/launcher/ui/pages/instance/McClient.cpp b/launcher/ui/pages/instance/McClient.cpp index 978eb9295..474490018 100644 --- a/launcher/ui/pages/instance/McClient.cpp +++ b/launcher/ui/pages/instance/McClient.cpp @@ -15,33 +15,21 @@ McClient::McClient(QObject *parent, QString domain, QString ip, short port): QObject(parent), m_domain(domain), m_ip(ip), m_port(port) {} -QJsonObject McClient::getStatusDataBlocking() { +void McClient::getStatusData() { qDebug() << "Connecting to socket.."; - m_socket.connectToHost(m_ip, m_port); - if (!m_socket.waitForConnected()) { - throw Exception("Failed to connect to socket"); - } - qDebug() << "Connected to socket successfully"; - sendRequest(); + connect(&m_socket, &QTcpSocket::connected, this, [this]() { + qDebug() << "Connected to socket successfully"; + sendRequest(); - if (!m_socket.waitForReadyRead()) { - throw Exception("Socket didn't send anything to read"); - } - return readResponse(); -} - -QFuture McClient::getOnlinePlayers() { - return QtConcurrent::run([this]() { - try { - auto status = getStatusDataBlocking(); - auto players = Json::requireObject(status, "players"); - return Json::requireInteger(players, "online"); - } catch (const Exception &e) { - qDebug() << "Error: " << e.what(); - return -1; - } + connect(&m_socket, &QTcpSocket::readyRead, this, &McClient::readRawResponse); }); + + connect(&m_socket, &QTcpSocket::errorOccurred, this, [this]() { + emitFail("Socket disconnected"); + }); + + m_socket.connectToHost(m_ip, m_port); } void McClient::sendRequest() { @@ -58,32 +46,25 @@ void McClient::sendRequest() { writePacketToSocket(data); // send status packet } -void McClient::readBytesExactFromSocket(QByteArray &resp, int bytesToRead) { - while (bytesToRead > 0) { - qDebug() << bytesToRead << " bytes left to read"; - if (!m_socket.waitForReadyRead()) { - throw Exception("Read timeout or error"); +// Accumulate data until we have a full response, then call parseResponse() +void McClient::readRawResponse() { + m_resp.append(m_socket.readAll()); + if (m_wantedRespLength == 0 && m_resp.size() >= 5) { + m_wantedRespLength = readVarInt(m_resp); + } + + if (m_wantedRespLength != 0 && m_resp.size() >= m_wantedRespLength) { + if (m_resp.size() > m_wantedRespLength) { + qDebug() << "Warning: Packet length doesn't match actual packet size (" << m_wantedRespLength << " expected vs " << m_resp.size() << " received)"; } - - QByteArray chunk = m_socket.read(bytesToRead); - resp.append(chunk); - bytesToRead -= chunk.size(); + parseResponse(); } } -QJsonObject McClient::readResponse() { - auto resp = m_socket.readAll(); - int length = readVarInt(resp); - - // finish ready response - readBytesExactFromSocket(resp, length-resp.size()); - - if (length != resp.size()) { - qDebug() << "Warning: Packet length doesn't match actual packet size (" << length << " expected vs " << resp.size() << " received)"; - } +void McClient::parseResponse() { qDebug() << "Received response successfully"; - int packetID = readVarInt(resp); + int packetID = readVarInt(m_resp); if (packetID != 0x00) { throw Exception( QString("Packet ID doesn't match expected value (0x00 vs 0x%1)") @@ -91,11 +72,11 @@ QJsonObject McClient::readResponse() { ); } - Q_UNUSED(readVarInt(resp)); // json length + Q_UNUSED(readVarInt(m_resp)); // json length // 'resp' should now be the JSON string - QJsonDocument doc = QJsonDocument::fromJson(resp); - return doc.object(); + QJsonDocument doc = QJsonDocument::fromJson(m_resp); + emitSucceed(doc.object()); } // From https://wiki.vg/Protocol#VarInt_and_VarLong @@ -164,3 +145,15 @@ void McClient::writePacketToSocket(QByteArray &data) { data.clear(); } + + +void McClient::emitFail(QString error) { + qDebug() << "Minecraft server ping for status error:" << error; + emit failed(); + emit finished(); +} + +void McClient::emitSucceed(QJsonObject data) { + emit succeeded(data); + emit finished(); +} diff --git a/launcher/ui/pages/instance/McClient.h b/launcher/ui/pages/instance/McClient.h index 4e54bf27e..820ce255b 100644 --- a/launcher/ui/pages/instance/McClient.h +++ b/launcher/ui/pages/instance/McClient.h @@ -15,14 +15,16 @@ class McClient : public QObject { short m_port; QTcpSocket m_socket; + unsigned m_wantedRespLength = 0; + QByteArray m_resp; + public: explicit McClient(QObject *parent, QString domain, QString ip, short port); - QFuture getOnlinePlayers(); + void getStatusData(); private: - QJsonObject getStatusDataBlocking(); void sendRequest(); - QJsonObject readResponse(); - void readBytesExactFromSocket(QByteArray &resp, int bytesToRead); + void readRawResponse(); + void parseResponse(); void writeVarInt(QByteArray &data, int value); int readVarInt(QByteArray &data); @@ -32,4 +34,12 @@ private: void writeString(QByteArray &data, const std::string &value); void writePacketToSocket(QByteArray &data); + + void emitFail(QString error); + void emitSucceed(QJsonObject data); + +signals: + void succeeded(QJsonObject data); + void failed(); + void finished(); }; diff --git a/launcher/ui/pages/instance/ServerPingTask.cpp b/launcher/ui/pages/instance/ServerPingTask.cpp index 260405dc0..0574dfca1 100644 --- a/launcher/ui/pages/instance/ServerPingTask.cpp +++ b/launcher/ui/pages/instance/ServerPingTask.cpp @@ -3,6 +3,11 @@ #include "ServerPingTask.h" #include "McResolver.h" #include "McClient.h" +#include + +unsigned getOnlinePlayers(QJsonObject data) { + return Json::requireInteger(Json::requireObject(data, "players"), "online"); +} void ServerPingTask::executeTask() { qDebug() << "Querying status of " << QString("%1:%2").arg(m_domain).arg(m_port); @@ -14,26 +19,21 @@ void ServerPingTask::executeTask() { // Now that we have the IP and port, query the server McClient *client = new McClient(nullptr, m_domain, ip, port); - auto onlineFuture = client->getOnlinePlayers(); - // Wait for query to finish - QFutureWatcher *watcher = new QFutureWatcher(); - QObject::connect(watcher, &QFutureWatcher::finished, this, [this, client, onlineFuture, watcher]() { - client->deleteLater(); - watcher->deleteLater(); - - int online = onlineFuture.result(); - if (online == -1) { - qDebug() << "Failed to get online players"; - emitFailed(); - return; - } else { - qDebug() << "Online players: " << online; - m_outputOnlinePlayers = online; - emitSucceeded(); - } + QObject::connect(client, &McClient::succeeded, this, [this](QJsonObject data) { + m_outputOnlinePlayers = getOnlinePlayers(data); + qDebug() << "Online players: " << m_outputOnlinePlayers; + emitSucceeded(); }); - watcher->setFuture(onlineFuture); + QObject::connect(client, &McClient::failed, this, [this]() { + emitFailed(); + }); + + // Delete McClient object when done + QObject::connect(client, &McClient::finished, this, [this, client]() { + client->deleteLater(); + }); + client->getStatusData(); }); // Delete McResolver object when done