zeel01 / scene-tiler

2 stars 1 forks source link

Race condition somewhere in Scene Tiler #7

Closed ggagnon76 closed 2 years ago

ggagnon76 commented 2 years ago

I've been having problems with programmatically creating Scene Tiler tiles and then assigning flags to them right away. This is the error that I get:

image

Here's what I've done to troubleshoot the issue:

  1. In a brand new world with no modules, I wrote 2 macros to create tiles:

    Flags inserted into new tile via preCreateTile hook.

    
    console.log("Starting test.  Flags inserted via preCreate hook.");

for (let i=0; i<10; i++) {

Hooks.once("preCreateTile", (document) => { document.data.update({ "flags": { "Testing": "This is a test" } }) })

const myTile = await canvas.scene.createEmbeddedDocuments("Tile", [{ img: "modules/scene-scroller/assets/Mansion 1.png", x: 1100, y: 900, width: 2700, height: 900 }]); }

### Flags inserted into new tile via setFlag
```js
console.log("Starting test.  Flags inserted via setFlag.");

for (let i=0; i<10; i++) {
  const myTile = await canvas.scene.createEmbeddedDocuments("Tile", [{
    img: "modules/scene-scroller/assets/Mansion 1.png",
    x: 1100,
    y: 900,
    width: 2700,
    height: 900
  }]);

  await myTile[0].setFlag("world", "Testing", "This is a test.");
}

I am able to spam those macros, or increase the quantity from 10 to 100 to 1000, and Foundry handles them just fine.

  1. Then in a world with only Scene Tiler enabled, I created these four macros:

    Flags inserted into new tile created with SceneTiler.create, with populate: false, via setFlag

    
    const source = game.scenes.getName("For testing");

console.log("Start test. SceneTiler.create + setFlag.");

for (let i=0; i<1; i++) {

const myTile = await SceneTiler.create(source, { x: 1100, y: 900, populate: false });

myTile.setFlag("world", "Testing", "This is a test."); }

### Flags inserted into new tile created with SceneTiler.create, with populate: false, via preCreateTile hook.
```js
const source = game.scenes.getName("For testing");

console.log("Starting test.  SceneTiler.create + Flags inserted via preCreate hook.");

for (let i=0; i<10; i++) {

  Hooks.once("preCreateTile", (document) => {
    document.data.update({
      "flags": {
        "Testing": "This is a test"
      }
    })
  })

  const myTile = await SceneTiler.create(source, {
    x: 1100,
    y: 900,
    populate: false
  });
}

Flags inserted into new tile created with SceneTiler.create, with populate: true, via setFlag

const source = game.scenes.getName("For testing");

console.log("Start test.  SceneTiler.create + setFlag.");

for (let i=0; i<1; i++) {

  const myTile = await SceneTiler.create(source, {
    x: 1100,
    y: 900,
    populate: true
  });

  await myTile.setFlag("world", "Testing", "This is a test.");

}

Flags inserted into new tile created with SceneTiler.create, with populate: true, via preCreateTile hook.

const source = game.scenes.getName("For testing");

console.log("Starting test.  SceneTiler.create + Flags inserted via preCreate hook.");

for (let i=0; i<10; i++) {

  Hooks.once("preCreateTile", (document) => {
    document.data.update({
      "flags": {
        "Testing": "This is a test"
      }
    })
  })

  const myTile = await SceneTiler.create(source, {
    x: 1100,
    y: 900,
    populate: true
  });
}

Results:

With SceneTile.create & populate: true or false & setFlag, I get the error the first time on a refreshed scene. All further uses of the macro work as expected...

With SceneTiler.create & populate: true & preCreateTile hook to embed flag, I get the error the first time on a refreshed scene. All further uses of the macro work as expected...

With SceneTiler.create @populate: false & preCreateTile hook to embed flag, the flag is set with no errors. But the tile(s) are not populated...

Pretty sure it's a race condition somewhere. I will start digging in Scene Tiler to see if I can figure it out, to give you a hand.

ggagnon76 commented 2 years ago

Ok, did more digging.

I put a breakpoint at line 36413 of foundry.js. That is where the frame is created in the draw() function. I also put a breakpoint at line 36512 of foundry.js. That is where the Border gets refreshed for the tile, such as when doing an update.

In line 321 of scene-tiler.js, you perform an update on the tile to insert all the created object id's in the tile's flags.

So what seems to be happening is you create a tile, then update the tile. Before the tile creation has a chance to execute the draw() function, the update calls the refresh() function and that's why the border is undefined, which throws the error.

I added a 100ms delay before line 321 in scene-tiler.js and sure enough, the errors go away.

I'm not sure this is a bug in your module. I think it's a core bug. Not sure yet how to reproduce it with no modules active to prove it though.

What I don't understand at all, is why this only happens the first time on a browser refresh, then it no longer happens afterward???

zeel01 commented 2 years ago

Thanks for the report, I'll see if I can reproduce and determine what's causing it.

Is this Foundry v8, v9, or both?

ggagnon76 commented 2 years ago

image

image

ggagnon76 commented 2 years ago

This is frustrating. The macros I wrote in a fresh module free world are now throwing the errors. This is not a Scene Tiler problem. I will submit it to bug reports in the foundry discord.

Adding a delay before you update the flags will get around this issue until it gets fixed. Although I'm probably the only one using Scene Tiler like this, so I can modify my own installation and continue developing my module and hope that if I manage to produce something I can release, core will have fixed this issue already.

You can close this issue with no action if you wish.

ggagnon76 commented 2 years ago

https://gitlab.com/foundrynet/foundryvtt/-/issues/6434

zeel01 commented 2 years ago

Looks like they reproduced it and scoped the fix, so all that's left to us is to wait on the patch. Good work tracking this down.