From 96eb7f3e09686d8e84c44b96c1a97542c29b2a2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= Date: Sun, 15 Jul 2018 17:37:21 +0300 Subject: [PATCH] GloomEd|Gloom: Send a "loadmap" command to the viewer app --- doomsday/apps/gloom/src/gloomapp.cpp | 27 +++++ doomsday/apps/gloomed/src/editor.cpp | 120 ++++++++++++++++----- doomsday/apps/gloomed/src/editor.h | 3 + doomsday/apps/gloomed/src/editorwindow.cpp | 7 +- doomsday/apps/gloomed/src/main.cpp | 76 ++++++++++--- 5 files changed, 193 insertions(+), 40 deletions(-) diff --git a/doomsday/apps/gloom/src/gloomapp.cpp b/doomsday/apps/gloom/src/gloomapp.cpp index b89eb9074d..53a082376a 100644 --- a/doomsday/apps/gloom/src/gloomapp.cpp +++ b/doomsday/apps/gloom/src/gloomapp.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -56,6 +57,8 @@ DE_PIMPL(GloomApp) // GloomEd will tell us what to do via the command socket. { commandSocket.reset(new_Datagram()); + setUserData_Object(commandSocket, this); + iConnect(Datagram, commandSocket, message, commandSocket, receivedRemoteCommand); for (int attempt = 0; attempt < 12; ++attempt) { if (open_Datagram(commandSocket, duint16(COMMAND_PORT + 4 + attempt))) @@ -87,6 +90,30 @@ DE_PIMPL(GloomApp) self().glDeinit(); } + static void receivedRemoteCommand(iAny *, iDatagram *socket) + { + while (String msgData = Block::take(receive_Datagram(socket, nullptr))) + { + Loop::mainCall([msgData]() { + const Info msg(msgData); + for (const auto *elem : msg.root().contentsInOrder()) + { + if (elem->isBlock()) + { + const auto &block = elem->as(); + if (block.blockType() == "command") + { + if (block.name() == "loadmap") + { + debug("load map: '%s'", block["package"].c_str()); + } + } + } + } + }); + } + } + void loadAllShaders() { // Load all the shader program definitions. diff --git a/doomsday/apps/gloomed/src/editor.cpp b/doomsday/apps/gloomed/src/editor.cpp index 6092f45360..8ea2ace94c 100644 --- a/doomsday/apps/gloomed/src/editor.cpp +++ b/doomsday/apps/gloomed/src/editor.cpp @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #include @@ -88,6 +90,7 @@ DE_PIMPL(Editor) Map map; String mapId; String filePath; + String packageName; bool isModified = false; List undoStack; @@ -128,11 +131,14 @@ DE_PIMPL(Editor) loadMap(persistentMapPath()); } - // Check for previous state. + // Check for previous editor state. { QSettings st; - viewScale = st.value("viewScale", 10).toFloat(); - viewOrigin = toVec2d(st.value("viewOrigin").value()); + + viewScale = st.value("viewScale", 10).toFloat(); + viewOrigin = toVec2d(st.value("viewOrigin").value()); + mapId = convert(st.value("mapId").toString()); + packageName = convert(st.value("packageName", "user.editorproject").toString()); } } @@ -142,12 +148,41 @@ DE_PIMPL(Editor) { QSettings st; st.setValue("mapId", convert(mapId)); + st.setValue("packageName", convert(packageName)); st.setValue("filePath", convert(filePath)); st.setValue("viewScale", viewScale); st.setValue("viewOrigin", toQVector2D(viewOrigin)); } } + void updateWindowTitle() + { + if (self().parentWidget()) + { + const String path = filePath ? filePath : "(unsaved)"; + const String id = mapId ? mapId : "(unnamed)"; + const String pkg = packageName ? packageName : "(no package)"; + + self().parentWidget()->setWindowTitle( + convert(Stringf("%s (%s) " DE_CHAR_MDASH " %s " DE_CHAR_MDASH " GloomEd", + path.c_str(), id.c_str(), pkg.c_str()))); + } + } + + void resetState() + { + undoStack.clear(); + isModified = false; + floorPoints.clear(); + selection.clear(); + hoverPoint = 0; + hoverLine = 0; + hoverSector = 0; + hoverEntity = 0; + hoverPlane = 0; + self().update(); + } + QString persistentMapPath() const { return QSettings().value("filePath", "").toString(); @@ -995,7 +1030,7 @@ DE_PIMPL(Editor) map = Map(); mapId.clear(); filePath.clear(); - setWindowTitle("(unnamed)"); + updateWindowTitle(); resetState(); } @@ -1028,7 +1063,7 @@ DE_PIMPL(Editor) const QByteArray mapData = f.readAll(); map.deserialize(Block(mapData.constData(), mapData.size())); resetState(); - setWindowTitle(convert(filePath.fileName())); + updateWindowTitle(); } void saveAsFile() @@ -1038,7 +1073,7 @@ DE_PIMPL(Editor) if (!newPath.isEmpty()) { filePath = convert(newPath); - setWindowTitle(convert(filePath.fileName())); + updateWindowTitle(); saveFile(); } } @@ -1123,7 +1158,7 @@ DE_PIMPL(Editor) qDebug() << "Texture:" << n << img.size().asText(); img.toQImage().save(n + ".png"); }*/ - importer.exportPackage("/home/user.editorproject.pack"); + importer.exportPackage(packageRootPath()); } // Update the editor's map. @@ -1131,7 +1166,7 @@ DE_PIMPL(Editor) mapId = importer.mapId(); filePath.clear(); resetState(); - setWindowTitle(convert(mapId)); + updateWindowTitle(); } } } @@ -1140,26 +1175,48 @@ DE_PIMPL(Editor) } } - void setWindowTitle(const QString &text) + String packageRootPath() const { - if (self().parentWidget()) - { - self().parentWidget()->setWindowTitle(QString("%1 (%2)").arg(text, convert(mapId))); - } + return "/home/" + packageName + ".pack"; } - void resetState() + void exportPackage() { - undoStack.clear(); - isModified = false; - floorPoints.clear(); - selection.clear(); - hoverPoint = 0; - hoverLine = 0; - hoverSector = 0; - hoverEntity = 0; - hoverPlane = 0; - self().update(); + if (!mapId) + { + mapId = convert(QInputDialog::getText(nullptr, "Export Package", "Map ID:")); + if (!mapId) return; + } + if (!packageName) + { + packageName = convert(QInputDialog::getText(nullptr, "Export Package", "Package ID:")); + if (!packageName) return; + } + + updateWindowTitle(); + + DE_ASSERT(mapId); + DE_ASSERT(packageName); + + Folder &root = FS::get().makeFolder(packageRootPath()); // or use existing folder... + + // Rewrite the .gloommap file. + { + const auto mapData = map.serialize(); + File &mapFile = root.replaceFile("maps" / mapId + ".gloommap"); + mapFile << mapData; + mapFile.flush(); + } + + // Check that the maps.dei includes this map. + if (const auto *mapsInfoFile = root.tryLocate("maps.dei")) + { + Info mapsInfo(*mapsInfoFile); + if (mapsInfo.root().contains(mapId)) + { + + } + } } }; @@ -1219,6 +1276,21 @@ gloom::Map &Editor::map() return d->map; } +String Editor::packageName() const +{ + return d->packageName; +} + +void Editor::exportPackage() const +{ + d->exportPackage(); +} + +void Editor::updateWindowTitle() const +{ + d->updateWindowTitle(); +} + bool Editor::maybeClose() { if (!d->askSaveFile()) diff --git a/doomsday/apps/gloomed/src/editor.h b/doomsday/apps/gloomed/src/editor.h index c5e841cb4a..0ba84d3def 100644 --- a/doomsday/apps/gloomed/src/editor.h +++ b/doomsday/apps/gloomed/src/editor.h @@ -42,7 +42,10 @@ class Editor : public QWidget de::String mapId() const; gloom::Map &map(); + de::String packageName() const; + void exportPackage() const; + void updateWindowTitle() const; bool maybeClose(); QSet selection() const; void markAsChanged(); diff --git a/doomsday/apps/gloomed/src/editorwindow.cpp b/doomsday/apps/gloomed/src/editorwindow.cpp index bb52e85c44..fa987145f7 100644 --- a/doomsday/apps/gloomed/src/editorwindow.cpp +++ b/doomsday/apps/gloomed/src/editorwindow.cpp @@ -41,6 +41,7 @@ EditorWindow::EditorWindow() { d->editor = new Editor; setCentralWidget(d->editor); + d->editor->updateWindowTitle(); const QStringList allMaterials({"", "world.stone", @@ -56,7 +57,7 @@ EditorWindow::EditorWindow() QToolBar *matr = new QToolBar(tr("Line Material")); addToolBar(Qt::BottomToolBarArea, matr); - connect(d->editor, &Editor::modeChanged, [matr] (int mode) { + connect(d->editor, &Editor::modeChanged, matr, [matr] (int mode) { matr->setVisible(mode == Editor::EditLines); }); @@ -138,7 +139,7 @@ EditorWindow::EditorWindow() QToolBar *matr = new QToolBar(tr("Plane Material")); addToolBar(Qt::BottomToolBarArea, matr); - connect(d->editor, &Editor::modeChanged, [matr] (int mode) { + connect(d->editor, &Editor::modeChanged, matr, [matr] (int mode) { matr->setVisible(mode == Editor::EditPlanes); }); @@ -159,7 +160,7 @@ EditorWindow::EditorWindow() Map &map = d->editor->map(); // Change line materials. - for (ID id : d->editor->selection()) + foreach (ID id, d->editor->selection()) { if (map.isPlane(id)) { diff --git a/doomsday/apps/gloomed/src/main.cpp b/doomsday/apps/gloomed/src/main.cpp index 78cd69ef6c..a98dbcfa2c 100644 --- a/doomsday/apps/gloomed/src/main.cpp +++ b/doomsday/apps/gloomed/src/main.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include + using namespace de; struct GloomCommander; @@ -43,11 +46,12 @@ static const duint16 COMMAND_PORT = 14666; /** * Sends commands to the Gloom viewer app and listens to beacon messages. */ -struct GloomCommander : DE_OBSERVES(Beacon, Discovery) +struct GloomCommander : DE_OBSERVES(Beacon, Discovery), public Waitable { - cplus::ref proc; - Beacon beacon{{COMMAND_PORT, COMMAND_PORT + 4}}; - Address address; + cplus::ref proc; + Beacon beacon{{COMMAND_PORT, COMMAND_PORT + 4}}; + Address address; + cplus::ref commandSocket; GloomCommander() { @@ -56,19 +60,49 @@ struct GloomCommander : DE_OBSERVES(Beacon, Discovery) void beaconFoundHost(const Address &host, const Block &message) override { - if (!address.isNull()) return; // Ignore additional replies. + if (isConnected()) return; // Ignore additional replies. qDebug("GloomEd beacon found:%s [%s]", host.asText().c_str(), message.c_str()); if (message.beginsWith(DE_STR("GloomApp:"))) { - const Info msg(message.mid(9)); - const duint16 commandPort = duint16(msg.root().keyValue("port").text.toInt()); - qDebug("Viewer command port: %u", commandPort); + const Info msg(message.mid(9)); + const duint16 commandPort = duint16(msg["port"].toUInt32()); beacon.stop(); address = host; + + commandSocket.reset(new_Datagram()); + for (int i = 0; i < 10; ++i) + { + // Open a socket in the private port range. + if (open_Datagram(commandSocket, duint16(Rangeui(0xc000, 0x10000).random()))) + { + break; + } + } + if (!isOpen_Datagram(commandSocket)) + { + qWarning("Failed to open UDP port for sending commands"); + } + connect_Datagram(commandSocket, Address(host.hostName(), commandPort)); + + post(); } } + + bool isConnected() const + { + return !address.isNull(); + } + + void sendCommand(const String &command) + { + if (!isConnected()) + { + wait(15.0); + } + write_Datagram(commandSocket, command.toUtf8()); + } }; /** @@ -78,7 +112,9 @@ static bool launchGloom() { if (gloomCommander) { +#if 0 if (gloomCommander->proc && isRunning_Process(gloomCommander->proc)) +#endif { return true; } @@ -89,13 +125,16 @@ static bool launchGloom() } gloomCommander->beacon.discover(0.0, 0.5); + return true; +#if 0 CommandLine cmd; #if defined (MACOSX) cmd << convert(qApp->applicationDirPath() + "/../../../Gloom.app/Contents/MacOS/Gloom"); #endif gloomCommander->proc.reset(cmd.executeProcess()); return bool(gloomCommander->proc); +#endif } struct EditorApp : public EmbeddedApp, public DoomsdayApp @@ -136,7 +175,10 @@ int main(int argc, char **argv) QObject::connect(&win.editor(), &Editor::buildMapRequested, &win, [&win, &app]() { try { + auto &editor = win.editor(); + // Export/update the map package. + editor.exportPackage(); // Launch the Gloom app. if (!launchGloom()) @@ -146,7 +188,15 @@ int main(int argc, char **argv) } // Wait for the process to start listening and tell it to load the map. - + async( + [&editor]() { + gloomCommander->sendCommand( + Stringf("command loadmap{package=\"%s\"}", editor.packageName().c_str())); + return 0; + }, + [](int) { + qDebug("Viewer has been requested to load the map"); + }); } catch (const Error &er) { @@ -160,18 +210,18 @@ int main(int argc, char **argv) * available and active. Use a QTimer to continually check for events and perform * loop iteration. */ - EditorApp deApp(makeList(argc, argv)); + EditorApp edApp(makeList(argc, argv)); { - auto &amd = deApp.metadata(); + auto &amd = edApp.metadata(); amd.set(App::APP_NAME, convert(app.applicationName())); amd.set(App::APP_VERSION, convert(app.applicationVersion())); amd.set(App::ORG_NAME, convert(app.organizationName())); amd.set(App::ORG_DOMAIN, convert(app.organizationDomain())); amd.set(App::UNIX_HOME, ".gloomed"); } - deApp.initialize(); + edApp.initialize(); QTimer deTimer; - QObject::connect(&deTimer, &QTimer::timeout, [&deApp]() { deApp.processEvents(); }); + QObject::connect(&deTimer, &QTimer::timeout, [&edApp]() { edApp.processEvents(); }); deTimer.start(100); int rc = app.exec();