diff --git a/forms/mainwindow.ui b/forms/mainwindow.ui
index 7fc0974c..3cec5dd2 100644
--- a/forms/mainwindow.ui
+++ b/forms/mainwindow.ui
@@ -236,23 +236,23 @@
-
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
-
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
-
@@ -365,8 +365,8 @@
0
0
- 543
- 600
+ 508
+ 665
@@ -893,8 +893,8 @@
0
0
- 443
- 74
+ 442
+ 77
@@ -1081,10 +1081,10 @@
- 8
+ 0
0
- 431
- 341
+ 425
+ 419
@@ -1499,8 +1499,8 @@
0
0
- 430
- 521
+ 98
+ 28
@@ -1544,8 +1544,8 @@
0
0
- 430
- 521
+ 98
+ 28
@@ -1589,8 +1589,8 @@
0
0
- 430
- 521
+ 98
+ 28
@@ -1634,8 +1634,8 @@
0
0
- 430
- 521
+ 98
+ 28
@@ -1679,8 +1679,8 @@
0
0
- 430
- 521
+ 98
+ 28
@@ -1730,8 +1730,8 @@
0
0
- 430
- 521
+ 98
+ 28
@@ -2431,8 +2431,8 @@
0
0
- 118
- 118
+ 101
+ 101
@@ -2693,7 +2693,7 @@
0
0
1287
- 22
+ 21
@@ -3034,6 +3053,7 @@
AdjustingStackedWidget
QStackedWidget
+ 1
GraphicsView
diff --git a/include/collabsession.h b/include/collabsession.h
new file mode 100644
index 00000000..d726ddd9
--- /dev/null
+++ b/include/collabsession.h
@@ -0,0 +1,31 @@
+#ifndef COLLABSESSION_H
+#define COLLABSESSION_H
+
+#include "mainwindow.h"
+#include
+
+#define COMMAND_BLOCKS_CHANGED 0x1
+
+#define SERVER_MESSAGE_CREATED_SESSION 0x1
+#define SERVER_MESSAGE_JOINED_SESSION 0x2
+#define SERVER_MESSAGE_BROADCAST_COMMAND 0x3
+
+class CollabSession
+{
+public:
+ CollabSession(MainWindow *mainWindow);
+ static void init(MainWindow *mainWindow);
+ static bool connect(QString host, int port);
+ static void createSession(QString sessionName);
+ static void joinSession(QString sessionName);
+ static QByteArray prepareSocketMessage(QByteArray message, int messageType);
+ static void onBlockChanged(int x, int y, Block prevBlock, Block newBlock);
+ void processMessage();
+ MainWindow *mainWindow = nullptr;
+private:
+ static void handleBlocksChangedCommand(QByteArray message);
+ QTcpSocket *socket = nullptr;
+ QByteArray messageBuffer;
+};
+
+#endif // COLLABSESSION_H
diff --git a/include/mainwindow.h b/include/mainwindow.h
index f6b382c2..a6abd6ca 100644
--- a/include/mainwindow.h
+++ b/include/mainwindow.h
@@ -1,6 +1,7 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
+#include
#include
#include
#include
@@ -232,6 +233,10 @@ private slots:
void on_actionRegion_Map_Editor_triggered();
+ void on_actionConnect_to_Collab_triggered();
+ void on_actionCreate_Collab_Session_triggered();
+ void on_actionJoin_Collab_Session_triggered();
+
private:
Ui::MainWindow *ui;
TilesetEditor *tilesetEditor = nullptr;
diff --git a/porymap.pro b/porymap.pro
index 04fb94e2..a176c409 100644
--- a/porymap.pro
+++ b/porymap.pro
@@ -4,7 +4,7 @@
#
#-------------------------------------------------
-QT += core gui qml
+QT += core gui qml network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
@@ -15,6 +15,7 @@ ICON = resources/icons/porymap.icns
QMAKE_CXXFLAGS += -std=c++11 -Wall
SOURCES += src/core/block.cpp \
+ src/collabsession.cpp \
src/core/blockdata.cpp \
src/core/event.cpp \
src/core/heallocation.cpp \
@@ -134,6 +135,7 @@ HEADERS += include/core/block.h \
include/ui/regionmapeditor.h \
include/ui/newmappopup.h \
include/ui/mapimageexporter.h \
+ include/collabsession.h \
include/config.h \
include/editor.h \
include/mainwindow.h \
diff --git a/src/collabsession.cpp b/src/collabsession.cpp
new file mode 100644
index 00000000..1677ae1c
--- /dev/null
+++ b/src/collabsession.cpp
@@ -0,0 +1,161 @@
+#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));
+}
diff --git a/src/core/map.cpp b/src/core/map.cpp
index 3db6daeb..12cfcff2 100644
--- a/src/core/map.cpp
+++ b/src/core/map.cpp
@@ -2,6 +2,7 @@
#include "map.h"
#include "imageproviders.h"
#include "scripting.h"
+#include "collabsession.h"
#include "editcommands.h"
@@ -383,6 +384,7 @@ void Map::setBlock(int x, int y, Block block, bool enableScriptCallback) {
layout->blockdata->blocks->replace(i, block);
if (enableScriptCallback) {
Scripting::cb_MetatileChanged(x, y, prevBlock, block);
+ CollabSession::onBlockChanged(x, y, prevBlock, block);
}
}
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index d98ed68a..5d987b2f 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -10,6 +10,7 @@
#include "currentselectedmetatilespixmapitem.h"
#include "customattributestable.h"
#include "scripting.h"
+#include "collabsession.h"
#include "adjustingstackedwidget.h"
#include "draggablepixmapitem.h"
#include "editcommands.h"
@@ -394,6 +395,7 @@ bool MainWindow::openProject(QString dir) {
this->setProjectSpecificUIVisibility();
Scripting::init(this);
+ CollabSession::init(this);
bool already_open = isProjectOpen() && (editor->project->root == dir);
if (!already_open) {
editor->closeProject();
@@ -1430,6 +1432,23 @@ void MainWindow::on_actionMap_Shift_triggered()
on_toolButton_Shift_clicked();
}
+void MainWindow::on_actionConnect_to_Collab_triggered()
+{
+ if (!CollabSession::connect("35.223.92.12", 4000)) {
+ logInfo("Failed to connect to collab session");
+ }
+}
+
+void MainWindow::on_actionCreate_Collab_Session_triggered()
+{
+ CollabSession::createSession("TestSession");
+}
+
+void MainWindow::on_actionJoin_Collab_Session_triggered()
+{
+ CollabSession::joinSession("TestSession");
+}
+
void MainWindow::onWheelZoom(int s) {
// Don't zoom the map when the user accidentally scrolls while performing a magic fill. (ctrl + middle button click)
if (!(QApplication::mouseButtons() & Qt::MiddleButton)) {