How to remove file / url from LoaderPlugin on loaderror?

Hi! I am loading an array of url’s as images.
All goes fine, until an url doesn’t resolve an image. I can catch errors on the loader with

.on(`loaderror`, (listener) => { this.removeFromCache(listener) }, this)

But I can’t resolve the error.

One approach I am trying is loading a known working url with the same key as the file that gave the error, but this does not work. The offending key/file stays in the queue and the error is also thrown on subsequent loads, so it loads the fall back image also for the ‘good’ urls.

This is the relevant piece of my code:

async getHomeImages(url, element, index, homeImageKey) {
    console.log("getHomeImages")
    await convertImage(url, "128", "png")
      .then((rec) => {
        //console.log("rec", rec)
        // load all the images to phaser
        this.load.image(homeImageKey, rec)
          .on(`filecomplete-image-${homeImageKey}`, (homeImageKey) => {
            //create the home
            this.createHome(element, index, homeImageKey)
          }, this)
          .on(`loaderror`, (event) => { this.resolveLoadError(event) }, this)
        // .on(`loaderror`, (homeImageKey) => { this.resolveLoadError(element, index, homeImageKey) }, this)
        this.load.start() // start loading the image in memory
        this.load.on('complete', (event) => this.loadAgain(event), this)
      })
  }

Ideally I would like to remove an item from the loading queue and load the offending key with a known good url. But I can’t find how to remove items from the LoaderPlugin queue…

It really looks like Phaser removes the file from the load queue before the loaderror event is emitted.

And the event callback argument is file so you should look at file.key.

Hi Samme
one would expect this but I keep getting the loaderror callback even when I loaded the key again with a working url

I did manage to work around it with:

  async getHomeImages(url, element, index, homeImageKey) {
    console.log("getHomeImages")
    await convertImage(url, "128", "png")
      .then((rec) => {
        //console.log("rec", rec)
        // load all the images to phaser
        this.load.image(homeImageKey, rec)
          .on(`filecomplete-image-${homeImageKey}`, (homeImageKey) => {
            //create the home
            this.createHome(element, index, homeImageKey)
          }, this)
          .on(`loaderror`, (offendingFile) => { this.resolveLoadError(element, index, homeImageKey, offendingFile) }, this)
        this.load.start() // start loading the image in memory
      })
  }

  resolveLoadError(element, index, homeImageKey, offendingFile) {
    console.log("offendingFile", offendingFile)
    const tempKey = 'homeKey_' + element.user_id
    if (tempKey == offendingFile.key) {
      let cachObject = { element: element, index: index, homeImageKey: homeImageKey, offendingFile: offendingFile }
      this.resolveLoadErrorCache.push(cachObject)

      console.log("load offendingFile again", homeImageKey)
      this.load.image(homeImageKey, './assets/ball_grey.png')
        .on(`filecomplete-image-${homeImageKey}`, (homeImageKey) => {
          //create the home
          this.createHome(element, index, homeImageKey)
        }, this)
      this.load.start()
    }
  }

So console.log("offendingFile", offendingFile) keeps firing but I check if the error happens in the right context (against user_id)

It is a bit strange, so if there are tips on how I could do it better, it would be welcome.

I don’t think you need more than one loaderror event handler per loader (scene), ever. That handler is still registered after the loader completes or restarts.

Ah I see, that makes sense, but makes handling the error a bit tricky!

I am making a game where a lot of the content is user created (eg. drawn by the player), so I am loading a lot of content dynamically, and also in loops, for instance to get arrays of drawings.

So with on ‘loaderror’ handler it would be very difficult to know in which context (function) the file failed to load and how to get the file back in the array it belongs to.

With my solution above I would get in to trouble if there is an other function loading images and getting an error, my resolveLoadError function would only handle one case.

I could check if the loading queue is finished with
this.load.isReady() or this.load.isLoading()
but that would also not give me absolute certainty which queue is loading

So what I came up now with is making a global this.resolveErrorObjectArray wherein I pass the file that is being loaded, but also the function, index of the array it is part of etc

Then when the callback function for ‘loaderror’ is fired, it looks at that array and filters out the image that got the error.
And then with the information that is object array I can reload the image, and process it further.

But I feel that all this is a bit much to have to do, I have the feeling it could be simpler?

here is my code for reference:

this.resolveErrorObjectArray = []

    async getHomeImages(url, element, index, homeImageKey, scene) {
        console.log("getHomeImages")
        await convertImage(url, "128", "png")
            .then((rec) => {
                //console.log("rec", rec)
                // load all the images to phaser
                scene.load.image(homeImageKey, rec)
                    .on(`filecomplete-image-${homeImageKey}`, (homeImageKey) => {
                        //delete from this.resolveErrorObjectArray
                        this.resolveErrorObjectArray = this.resolveErrorObjectArray.filter((obj) => obj.imageKey !== homeImageKey)
                        console.log("this.resolveErrorObjectArray", this.resolveErrorObjectArray)
                        //create the home
                        this.createHome(element, index, homeImageKey, scene)
                    }, this)
                // put the file in the loadErrorCache, incase it doesn't load
                this.resolveErrorObjectArray.push({ loadFunction: "getHomeImage", element: element, index: index, imageKey: homeImageKey })
                scene.load.start() // start loading the image in memory
            })
    }

    resolveLoadError(offendingFile) {
      
        let resolveErrorObject = this.resolveErrorObjectArray.find(obj => obj.imageKey == offendingFile.key)

        let loadFunction = resolveErrorObject.loadFunction
        let element = resolveErrorObject.element
        let index = resolveErrorObject.index
        let imageKey = offendingFile.key
        let scene = ManageSession.currentScene

        switch (loadFunction) {
            case ("getHomeImage"):
                console.log("load offendingFile again", imageKey, offendingFile)

                scene.load.image(imageKey, './assets/ball_grey.png')
                    .on(`filecomplete-image-${imageKey}`, (imageKey) => {
                        //delete from this.resolveErrorObjectArray
                        this.resolveErrorObjectArray = this.resolveErrorObjectArray.filter((obj) => obj.imageKey !== imageKey)
                        console.log("this.resolveErrorObjectArray", this.resolveErrorObjectArray)

                        //create the home
                        this.createHome(element, index, imageKey, scene);
                    }, this)
                scene.load.start()
                break

            default:
                console.log("please state fom which function the loaderror occured!")
        }
    }