diff --git a/NEWS.md b/NEWS.md index 1b12dafb56..9027fee041 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,7 @@ * Scripting: Added 'tiled.cell' function, 'cell.flags' property and 'TileLayerEdit.setCell' function (#4538) * Scripting: Added MapObject.resolvedClassName() (by MatusGuy, #4529) * Scripting: Added Tileset.selectedTilesChanged signal (#4615) +* Scripting: Fixed world scripting APIs to work without the editor (by Shubham Padkonde, #4584) * Fixed crash when the selection becomes empty while starting a move (#4536) * Fixed Properties view update on 'Reset Template Instance' and 'Replace With Template' actions * Fixed restoring of the layout for maximized windows on startup (#4580) diff --git a/docs/reference/support-for-tmx-maps.rst b/docs/reference/support-for-tmx-maps.rst index 87dd482de6..96f125b7bf 100644 --- a/docs/reference/support-for-tmx-maps.rst +++ b/docs/reference/support-for-tmx-maps.rst @@ -327,10 +327,11 @@ HTML5 (multiple engines) multi-canvas based game rendering library - `melonJS `__ A lightweight HTML5 game engine - `Panda 2 `__, a HTML5 Game Development Platform for Mac, Windows and Linux. Has `a plugin for rendering Tiled `__ maps, both orthogonal and isometric. -- `pixi-tiledmap `__ A - loader and renderer for Tiled Maps in `Pixi.JS `__ - v8+ written in TypeScript. JSON/XML support with no external deps, - full layer-type support, typed API and ESM/CJS dual output. +- `pixi-tiledmap `__ A Tiled map + runtime for `PixiJS `__ v8+, written in TypeScript. + TMX/TMJ parsing with no external dependencies, all layer types and + orientations, batched tile rendering, runtime tile editing, procedural + map generation, and export back to Tiled JSON. - `Phaser `__ A fast, free and fun open source framework supporting both JavaScript and TypeScript (`Tiled tutorial `__) diff --git a/docs/scripting-doc/index.d.ts b/docs/scripting-doc/index.d.ts index 7e8989010a..18b593082e 100644 --- a/docs/scripting-doc/index.d.ts +++ b/docs/scripting-doc/index.d.ts @@ -5472,6 +5472,7 @@ declare namespace tiled { /** * Load a world contained in a .world file in the path fileName. + * Throws an error if the world could not be loaded. * @since 1.11 */ export function loadWorld(fileName: string): void; diff --git a/src/tiled/scriptmodule.cpp b/src/tiled/scriptmodule.cpp index eab1d081a9..dbebe25e56 100644 --- a/src/tiled/scriptmodule.cpp +++ b/src/tiled/scriptmodule.cpp @@ -70,9 +70,9 @@ ScriptModule::ScriptModule(QObject *parent) connect(documentManager, &DocumentManager::documentSaved, this, &ScriptModule::documentSaved); connect(documentManager, &DocumentManager::documentAboutToClose, this, &ScriptModule::documentAboutToClose); connect(documentManager, &DocumentManager::currentDocumentChanged, this, &ScriptModule::currentDocumentChanged); - - connect(&WorldManager::instance(), &WorldManager::worldsChanged, this, &ScriptModule::worldsChanged); } + + connect(&WorldManager::instance(), &WorldManager::worldsChanged, this, &ScriptModule::worldsChanged); } ScriptModule::~ScriptModule() @@ -756,10 +756,6 @@ QList ScriptModule::worlds() const { QList worlds; - auto documentManager = DocumentManager::maybeInstance(); - if (!documentManager) - return worlds; - for (auto &worldDocument : WorldManager::instance().worlds()) worlds.append(worldDocument->editable()); @@ -768,7 +764,9 @@ QList ScriptModule::worlds() const void ScriptModule::loadWorld(const QString &fileName) const { - WorldManager::instance().loadWorld(fileName); + QString error; + if (!WorldManager::instance().loadWorld(fileName, &error)) + ScriptManager::instance().throwError(error); } void ScriptModule::unloadWorld(const QString &fileName) const diff --git a/tests/scriptmodule/scriptmodule.qbs b/tests/scriptmodule/scriptmodule.qbs new file mode 100644 index 0000000000..78f3abcf69 --- /dev/null +++ b/tests/scriptmodule/scriptmodule.qbs @@ -0,0 +1,10 @@ +TiledTest { + name: "test_scriptmodule" + + Depends { name: "libtilededitor" } + Depends { name: "Qt.qml" } + + files: [ + "test_scriptmodule.cpp", + ] +} diff --git a/tests/scriptmodule/test_scriptmodule.cpp b/tests/scriptmodule/test_scriptmodule.cpp new file mode 100644 index 0000000000..cfed9fc812 --- /dev/null +++ b/tests/scriptmodule/test_scriptmodule.cpp @@ -0,0 +1,99 @@ +#include "projectmanager.h" +#include "scriptmanager.h" +#include "worldmanager.h" + +#include +#include +#include + +#include + +using namespace Tiled; + +class test_ScriptModule : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + void cleanup(); + void worldsWithoutDocumentManager(); + void worldsChangedWithoutDocumentManager(); + void loadMissingWorld(); + +private: + std::unique_ptr mProjectManager; +}; + +void test_ScriptModule::initTestCase() +{ + QStandardPaths::setTestModeEnabled(true); + mProjectManager = std::make_unique(); + ScriptManager::instance().ensureInitialized(); +} + +void test_ScriptModule::cleanupTestCase() +{ + ScriptManager::deleteInstance(); + mProjectManager.reset(); +} + +void test_ScriptModule::cleanup() +{ + WorldManager::instance().unloadAllWorlds(); +} + +void test_ScriptModule::worldsWithoutDocumentManager() +{ + QTemporaryFile file; + QVERIFY(file.open()); + file.write("{\"type\":\"world\",\"maps\":[]}"); + file.flush(); + + auto &manager = ScriptManager::instance(); + manager.engine()->globalObject().setProperty(QStringLiteral("testWorldPath"), file.fileName()); + const auto result = manager.evaluate(QStringLiteral("tiled.loadWorld(testWorldPath); tiled.worlds.length")); + QVERIFY2(!result.isError(), qPrintable(result.toString())); + QCOMPARE(WorldManager::instance().worlds().size(), 1); + QCOMPARE(result.toInt(), 1); + + const auto afterUnload = manager.evaluate(QStringLiteral("tiled.unloadWorld(testWorldPath); tiled.worlds.length")); + QCOMPARE(afterUnload.toInt(), 0); +} + +void test_ScriptModule::worldsChangedWithoutDocumentManager() +{ + QTemporaryFile file; + QVERIFY(file.open()); + file.write("{\"type\":\"world\",\"maps\":[]}"); + file.flush(); + + auto &manager = ScriptManager::instance(); + manager.engine()->globalObject().setProperty(QStringLiteral("testWorldPath"), file.fileName()); + const auto result = manager.evaluate(QStringLiteral( + "var changes = 0;" + "tiled.worldsChanged.connect(function() { ++changes; });" + "tiled.loadWorld(testWorldPath); changes")); + QVERIFY2(!result.isError(), qPrintable(result.toString())); + QCOMPARE(result.toInt(), 1); + + const auto afterUnload = manager.evaluate(QStringLiteral("tiled.unloadAllWorlds(); changes")); + QCOMPARE(afterUnload.toInt(), 2); +} + +void test_ScriptModule::loadMissingWorld() +{ + QTemporaryDir directory; + QVERIFY(directory.isValid()); + auto &manager = ScriptManager::instance(); + manager.engine()->globalObject().setProperty(QStringLiteral("testWorldPath"), + directory.filePath(QStringLiteral("missing.world"))); + const auto result = manager.evaluate(QStringLiteral("tiled.loadWorld(testWorldPath)")); + QVERIFY(result.isError()); + QVERIFY(WorldManager::instance().worlds().isEmpty()); +} + +QTEST_MAIN(test_ScriptModule) + +#include "test_scriptmodule.moc" diff --git a/tests/tests.qbs b/tests/tests.qbs index 71d2cdb6d5..63fd01784b 100644 --- a/tests/tests.qbs +++ b/tests/tests.qbs @@ -5,6 +5,7 @@ Project { "automapping", "mapreader", "properties", + "scriptmodule", "staggeredrenderer", ] }