mapeditor / tiled

Flexible level editor
https://www.mapeditor.org/
Other
11.15k stars 1.75k forks source link

Scripting API logging objects crashes the editor #2763

Closed MikeMnD closed 4 years ago

MikeMnD commented 4 years ago

Here a simple example script for making custom tilemap export: You can check the comments and try different ways of crashing the editor:

export_to_cusotm_tilemap.js

var customMapFormat = {
    name: "Custom Map Format",
    extension: "ctxt",

    write: function(map, fileName) {

        // This works but it's not useful
        console.log(map) //result in qml: Tiled::EditableMap(0x9dbaf0)

        // This crashes the editor
        // console.log(JSON.stringify(map));

        // This also crashes the editor
        // console.log(Object.keys(map));

        // Both return empty string
        console.log(map.asset.fileName);
        console.log(map.fileName);

        let poolIntArrayString = '';

        map.tilesets.forEach(tileset => {

            // This works as intended - and returns the full path to the .tsx file
            console.log(tileset.asset.fileName);

            // This also works
            tiled.log(Object.keys(tileset))

        });

        const tileMapName = "TileMap";
        const file = new TextFile(fileName, TextFile.WriteOnly);
        const tileMapTemplate = "empty map in custom txt file";
        file.write(tileMapTemplate);
        file.commit();
        file.close();

    },

}

tiled.registerMapFormat("Custom", customMapFormat);

This is the log from the windows event log: Windows 10, Tiled Version 1.3.2

Log Name:      Application
Source:        Application Error
Date:          3/3/2020 20:23:55
Event ID:      1000
Task Category: (100)
Level:         Error
Keywords:      Classic
User:          N/A
Computer:      DESKTOP-QL8D1P2
Description:
Faulting application name: tiled.exe, version: 1.0.0.0, time stamp: 0x5e286394
Faulting module name: Qt5Widgets.dll, version: 5.12.6.0, time stamp: 0x5dc71809
Exception code: 0xc0000005
Fault offset: 0x000000000030dff2
Faulting process id: 0x26d4
Faulting application start time: 0x01d5f188dad50ef9
Faulting application path: C:\Program Files\Tiled\tiled.exe
Faulting module path: C:\Program Files\Tiled\Qt5Widgets.dll
Report Id: c5b31c05-5478-473a-b819-d249462c68fd
Faulting package full name: 
Faulting package-relative application ID: 
Event Xml:
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
  <System>
    <Provider Name="Application Error" />
    <EventID Qualifiers="0">1000</EventID>
    <Level>2</Level>
    <Task>100</Task>
    <Keywords>0x80000000000000</Keywords>
    <TimeCreated SystemTime="2020-03-03T18:23:55.717795200Z" />
    <EventRecordID>62920</EventRecordID>
    <Channel>Application</Channel>
    <Computer>DESKTOP-QL8D1P2</Computer>
    <Security />
  </System>
  <EventData>
    <Data>tiled.exe</Data>
    <Data>1.0.0.0</Data>
    <Data>5e286394</Data>
    <Data>Qt5Widgets.dll</Data>
    <Data>5.12.6.0</Data>
    <Data>5dc71809</Data>
    <Data>c0000005</Data>
    <Data>000000000030dff2</Data>
    <Data>26d4</Data>
    <Data>01d5f188dad50ef9</Data>
    <Data>C:\Program Files\Tiled\tiled.exe</Data>
    <Data>C:\Program Files\Tiled\Qt5Widgets.dll</Data>
    <Data>c5b31c05-5478-473a-b819-d249462c68fd</Data>
    <Data>
    </Data>
    <Data>
    </Data>
  </EventData>
</Event>
MikeMnD commented 4 years ago

The problem with a console.log or JSON.stringify is the circular reference: tileset.tiles[0].tileset.tiles[0].tileset… and so on… More on that: https://stackoverflow.com/questions/4816099/chrome-sendrequest-error-typeerror-converting-circular-structure-to-json

and the workaround is using this small package: https://www.npmjs.com/package/flatted

Then you can use it like:

    console.log("--- tileset start ---");
    console.log(Flatted.stringify(tileset));
    console.log("--- tileset end ---");

That way the console in the Editor shows a text representation of the whole object.

bjorn commented 4 years ago
    // This crashes the editor
    // console.log(JSON.stringify(map));

In my tests this does not crash the editor. I'm consistently getting:

TypeError: Type error
Stack traceback:
  write@file:///home/bjorn/.config/tiled/extensions/tiled-extensions/maptestformat.js:11

Though, I don't think I've tested this with the Tiled release yet, so it maybe it does crash when using Qt 5.12.6. In that case it would be fixed when we build against Qt 5.12.7.

    // This also crashes the editor
    // console.log(Object.keys(map));

For me, this outputs:

qml: [objectName,asset,readOnly,fileName,modified,isTileMap,isTileset,width,height,size,tileWidth,tileHeight,infinite,hexSideLength,staggerAxis,staggerIndex,orientation,renderOrder,backgroundColor,layerDataFormat,layerCount,tilesets,selectedArea,currentLayer,selectedLayers,selectedObjects,objectNameChanged,property,setProperty,properties,setProperties,removeProperty,modifiedChanged,fileNameChanged,undo,redo,macro,sizeChanged,tileWidthChanged,tileHeightChanged,currentLayerChanged,selectedLayersChanged,selectedObjectsChanged,layerAt,removeLayerAt,removeLayer,insertLayerAt,addLayer,addTileset,replaceTileset,removeTileset,usedTilesets,merge,merge,resize,resize,resize,autoMap,autoMap,autoMap,autoMap,autoMap,autoMap,autoMap,autoMap,setSize,setTileSize]
    // Both return empty string
    console.log(map.asset.fileName);
    console.log(map.fileName);

This may be surprising, but indeed this object does not have a file name. In this case, the map object is a temporary script wrapper around the map's data and map.asset just points back to itself. Note that even if this object would have the file name, it may be the current file name rather than the file name you're saving as, which is available as the fileName parameter.

        // This works as intended - and returns the full path to the .tsx file
        console.log(tileset.asset.fileName);

Yes, though you don't need the .asset part, which again points back at itself for tilesets.

console.log("--- tileset start ---");
console.log(Flatted.stringify(tileset));
console.log("--- tileset end ---");

That's a nice way to serialize! Just curious, but so far I didn't manage to get includes / requires to work. Did you find a way or did you just copy source code to your script file?

MikeMnD commented 4 years ago

Just curious, but so far I didn't manage to get includes / requires to work. Did you find a way or did you just copy source code to your script file?

I've pasted the source code in a utils.js and used it from there after Tiled autoloaded the utils.js var Flatted=function(a,l) ...

That's what I found about the topic: https://stackoverflow.com/questions/38057170/qt-qjsengine-import-js-files

The .import statement does not work with QJSEngine. QJSEngine is just a bare interpreter if you want to have some "import" functionality you might switch to QQmlEngine, which is built on top of QJSEngine: http://doc.qt.io/qt-5/qtqml-syntax-imports.html#qml-import-path

With QJSEngine you basically need to manually populate the js global object (and QtCreator sintax checker will not recognize the statements you use across the different files).

So maybe we can close this issue.

bjorn commented 4 years ago

I've pasted the source code in a utils.js and used it from there after Tiled autoloaded the utils.js

Ah yes, that works of course!

QJSEngine is just a bare interpreter if you want to have some "import" functionality you might switch to QQmlEngine, which is built on top of QJSEngine

Hmm, I'm already using QQmlEngine so just using this subclass does not appear to suffice for getting imports to work.

Anyway, yeah, let's close this issue.