#include "collabsession.h" #include "log.h" CollabSession::CollabSession(MainWindow *mainWindow) { this->mainWindow = mainWindow; } CollabSession *collabInstance = nullptr; void CollabSession::init(MainWindow *mainWindow) { if (collabInstance) { delete collabInstance; } collabInstance = new CollabSession(mainWindow); } bool CollabSession::connect(QString host, int port) { if (!collabInstance) return false; collabInstance->socket = new QTcpSocket(); collabInstance->socket->connectToHost(host, port); QObject::connect(collabInstance->socket, &QTcpSocket::connected, [=](){ logInfo("Socket connected to collab server..."); }); QObject::connect(collabInstance->socket, &QTcpSocket::disconnected, [=](){ logInfo("Socket disconnected from collab server..."); }); QObject::connect(collabInstance->socket, &QTcpSocket::readyRead, [=](){ collabInstance->processMessage(); }); if (!collabInstance->socket->waitForConnected(10000)) { logError(collabInstance->socket->errorString()); delete collabInstance->socket; collabInstance->socket = nullptr; return false; } return true; } void CollabSession::processMessage() { QByteArray newContent = collabInstance->socket->readAll(); this->messageBuffer.append(newContent); // Process all the available messages in the read buffer. while (true) { if (this->messageBuffer.size() < 12) { break; } uint32_t signature = this->messageBuffer.at(0) | (this->messageBuffer.at(1) << 8) | (this->messageBuffer.at(2) << 16) | (this->messageBuffer.at(3) << 24); if (signature != 0x98765432) { // Invalid signature from server. Unclear how to recover--just disconnect. logError("Invalid message signature received from collab server. Disconnecting..."); this->socket->disconnectFromHost(); delete this->socket; this->socket = nullptr; break; } uint32_t payloadSize = this->messageBuffer.at(4) | (this->messageBuffer.at(5) << 8) | (this->messageBuffer.at(6) << 16) | (this->messageBuffer.at(7) << 24); int messageSize = 12 + payloadSize; if (this->messageBuffer.size() < messageSize) { break; } uint32_t messageType = this->messageBuffer.at(8) | (this->messageBuffer.at(9) << 8) | (this->messageBuffer.at(10) << 16) | (this->messageBuffer.at(11) << 24); QByteArray message = this->messageBuffer.left(messageSize); message.remove(0, 12); this->messageBuffer = this->messageBuffer.remove(0, messageSize); if (message.size() < 2) { break; } switch (messageType) { case SERVER_MESSAGE_BROADCAST_COMMAND: int commandType = (message.at(1) << 8) | message.at(0); switch (commandType) { case COMMAND_BLOCKS_CHANGED: this->handleBlocksChangedCommand(message); break; } break; } } } void CollabSession::createSession(QString sessionName) { QByteArray msg = prepareSocketMessage(sessionName.toLocal8Bit(), 0x1); logInfo(QString("Joining session: %1").arg(collabInstance->socket->write(QByteArray(msg)))); } void CollabSession::joinSession(QString sessionName) { QByteArray msg = prepareSocketMessage(sessionName.toLocal8Bit(), 0x2); logInfo(QString("Joining session: %1").arg(collabInstance->socket->write(QByteArray(msg)))); } QByteArray CollabSession::prepareSocketMessage(QByteArray message, int messageType) { QByteArray header = QByteArray(); // 4-byte little endian signature is 0x12345678. header.append(0x78); header.append(0x56); header.append(0x34); header.append(0x12); // 4-byte little endian payload size. header.append(message.size() & 0xff); header.append((message.size() >> 8) & 0xff); header.append((message.size() >> 16) & 0xff); header.append((message.size() >> 24) & 0xff); // 4-byte little endian message type. header.append(messageType & 0xff); header.append((messageType >> 8) & 0xff); header.append((messageType >> 16) & 0xff); header.append((messageType >> 24) & 0xff); // Append payload. return header + message; } void CollabSession::handleBlocksChangedCommand(QByteArray message) { uint16_t numBlocks = (message.at(3) << 8) | message.at(2); if (message.size() != 4 + numBlocks * 6) return; for (int i = 0; i < numBlocks; i++) { int index = 4 + i * 6; int x = (uint8_t(message.at(index + 1)) << 8) | uint8_t(message.at(index)); int y = (uint8_t(message.at(index + 3)) << 8) | uint8_t(message.at(index + 2)); uint16_t metatileId = (uint8_t(message.at(index + 5)) << 8) | uint8_t(message.at(index + 4)); collabInstance->mainWindow->setBlock(x, y, metatileId, 0, 0, true, false); } } void CollabSession::onBlockChanged(int x, int y, Block prevBlock, Block newBlock) { if (!collabInstance || !collabInstance->socket) return; if (prevBlock.rawValue() == newBlock.rawValue()) return; QByteArray message; message.append(COMMAND_BLOCKS_CHANGED & 0xFF); message.append((COMMAND_BLOCKS_CHANGED >> 8) & 0xFF); message.append(0x1); message.append('\0'); message.append(x & 0xFF); message.append((x >> 8) & 0xFF); message.append(y & 0xFF); message.append((y >> 8) & 0xFF); message.append(newBlock.tile & 0xFF); message.append((newBlock.tile >> 8) & 0xFF); QByteArray msg = CollabSession::prepareSocketMessage(message, 0x3); collabInstance->socket->write(QByteArray(msg)); }