phaserjs / phaser-ce

Phaser CE is a fun, free and fast 2D game framework for making HTML5 games for desktop and mobile web browsers, supporting Canvas and WebGL rendering.
http://phaser.io
MIT License
1.35k stars 491 forks source link

Cannot create and destroy games in quick succession #728

Closed timiyay closed 2 years ago

timiyay commented 2 years ago

This Issue is about

Crash occurs at https://github.com/photonstorm/phaser-ce/blob/1af938320a72ebc0cfbd9cc9101fcf1bf17d9808/src/core/Game.js#L1242

Background

We're using a NextJS/React client to run some Phaser games. We're using React's effects inside functional components, since the rendering of a Phaser game is considered a side effect outside of React's control. In development, React has a Strict Mode that will cause these effects to double-render these functional components, in order to help devs identify unintentional side effects in their code

This means our Phaser games are being created, very quickly destroyed, then created again. This is exposing a couple of spots where Phaser currently assumes enough time has passed since new Phaser.Game was called that it's appropriately setup to call destroy.

Do folks see a way we could make the destroy function more resilient to this behaviour, so it doesn't attempt to destroy things that are already dead?

We have some code that wraps our Phaser games, and we've been setting an isDestroying boolean to help in these kind of situations.

I am happy to contribute a PR, once folks have a chance to triage and comment on the issue.

samme commented 2 years ago

Setting game.pendingDestroy = true should work.

https://codepen.io/samme/pen/wvjryrQ?editors=0010

timiyay commented 2 years ago

Thanks.

We've switched to pendingDestroy = true recently while fixing these React compatibility issues. It mostly works, though it has been smoking out other issues relating to destroy code, such as https://github.com/photonstorm/phaser-ce/issues/729.

I wanted to log the issue independently, in case this behaviour was seen as something to avoid. For example, the code tends to crash at this.raf.stop() so, if desired, I could open a PR to do some more-defensive programming around this, like:

if (this.raf) this.raf.stop()
samme commented 2 years ago

I think in that case the destruction needs to be postponed:

if (!this.isBooted)
{
    this.pendingDestroy = true;

    return;
}
samme commented 2 years ago

You're welcome to make a PR for it if you like.

samme commented 1 year ago

Fixed in e68a4f1