Skip to content

Commit

Permalink
Fix exception when changing an external tilemap file after it was ini…
Browse files Browse the repository at this point in the history
…tially missing
  • Loading branch information
4ian committed Sep 6, 2024
1 parent 0c2341c commit 02c3e93
Show file tree
Hide file tree
Showing 2 changed files with 142 additions and 51 deletions.
110 changes: 84 additions & 26 deletions Extensions/TileMap/JsExtension.js
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,14 @@ module.exports = {
// the Tilemap will properly emit events when hovered/clicked.
// By default, this is not implemented in pixi-tilemap.
this._pixiObject.containsPoint = (position) => {
if (!this._pixiObject) {
// Ease debugging by throwing now rather than waiting for an exception later.
throw new Error(
'containsPoint called on a destroyed PIXI object - this object was not properly removed from the PIXI container.'
);
return;
}

// Turns the world position to the local object coordinates
const localPosition = new PIXI.Point();
this._pixiObject.worldTransform.applyInverse(position, localPosition);
Expand All @@ -1638,21 +1646,32 @@ module.exports = {
super.onRemovedFromScene();
// Keep textures because they are shared by all tile maps.
this._pixiObject.destroy(false);

// Not strictly necessary, but helps finding wrong
// handling of this._pixiObject in its container.
this._pixiObject = null;
}

_replacePixiObject(newPixiObject) {
if (this._pixiObject !== null)
this._pixiContainer.removeChild(this._pixiObject);
this._pixiObject = newPixiObject;
this._pixiContainer.addChild(this._pixiObject);
}

onLoadingError() {
_onLoadingError() {
this.errorPixiObject =
this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this._pixiContainer.addChild(this.errorPixiObject);
this._pixiObject = this.errorPixiObject;

this._replacePixiObject(this.errorPixiObject);
}

onLoadingSuccess() {
_onLoadingSuccess() {
if (this.errorPixiObject) {
this._pixiContainer.removeChild(this.errorPixiObject);
this._replacePixiObject(this.tileMapPixiObject);

this.errorPixiObject = null;
this._pixiObject = this.tileMapPixiObject;
}
}

Expand Down Expand Up @@ -1731,7 +1750,7 @@ module.exports = {
pako,
(tileMap) => {
if (!tileMap) {
this.onLoadingError();
this._onLoadingError();
// _loadTileMapWithCallback already log errors
return;
}
Expand All @@ -1750,11 +1769,11 @@ module.exports = {
levelIndex,
(textureCache) => {
if (!textureCache) {
this.onLoadingError();
this._onLoadingError();
// getOrLoadTextureCache already log warns and errors.
return;
}
this.onLoadingSuccess();
this._onLoadingSuccess();

this.width = tileMap.getWidth();
this.height = tileMap.getHeight();
Expand Down Expand Up @@ -1920,6 +1939,14 @@ module.exports = {
// the Tilemap will properly emit events when hovered/clicked.
// By default, this is not implemented in pixi-tilemap.
this._pixiObject.containsPoint = (position) => {
if (!this._pixiObject) {
// Ease debugging by throwing now rather than waiting for an exception later.
throw new Error(
'containsPoint called on a destroyed PIXI object - this object was not properly removed from the PIXI container.'
);
return;
}

// Turns the world position to the local object coordinates
const localPosition = new PIXI.Point();
if (this.tileMapPixiObject.visible) {
Expand Down Expand Up @@ -1963,21 +1990,32 @@ module.exports = {
super.onRemovedFromScene();
// Keep textures because they are shared by all tile maps.
this._pixiObject.destroy(false);

// Not strictly necessary, but helps finding wrong
// handling of this._pixiObject in its container.
this._pixiObject = null;
}

onLoadingError() {
_replacePixiObject(newPixiObject) {
if (this._pixiObject !== null)
this._pixiContainer.removeChild(this._pixiObject);
this._pixiObject = newPixiObject;
this._pixiContainer.addChild(this._pixiObject);
}

_onLoadingError() {
this.errorPixiObject =
this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this._pixiContainer.addChild(this.errorPixiObject);
this._pixiObject = this.errorPixiObject;

this._replacePixiObject(this.errorPixiObject);
}

onLoadingSuccess() {
_onLoadingSuccess() {
if (this.errorPixiObject) {
this._pixiContainer.removeChild(this.errorPixiObject);
this._replacePixiObject(this.tileMapPixiObject);

this.errorPixiObject = null;
this._pixiObject = this.tileMapPixiObject;
}
}

Expand Down Expand Up @@ -2052,7 +2090,7 @@ module.exports = {
rowCount,
(tileMap) => {
if (!tileMap) {
this.onLoadingError();
this._onLoadingError();
console.error('Could not parse tilemap.');
return;
}
Expand All @@ -2073,7 +2111,7 @@ module.exports = {
/** @type {TileMapHelper.TileTextureCache | null} */
textureCache
) => {
this.onLoadingSuccess();
this._onLoadingSuccess();
if (!this._editableTileMap) return;

this.width = this._editableTileMap.getWidth();
Expand Down Expand Up @@ -2145,7 +2183,7 @@ module.exports = {
/** @type {TileMapHelper.TileTextureCache | null} */
textureCache
) => {
this.onLoadingSuccess();
this._onLoadingSuccess();
if (!this._editableTileMap) return;

this.width = this._editableTileMap.getWidth();
Expand Down Expand Up @@ -2253,12 +2291,21 @@ module.exports = {
);

this.tileMapPixiObject = new PIXI.Graphics();
this.tileMapPixiObject._0iAmTheTileMapPixiObject = true;
this._pixiObject = this.tileMapPixiObject;

// Implement `containsPoint` so that we can set `interactive` to true and
// the Tilemap will properly emit events when hovered/clicked.
// By default, this is not implemented in pixi-tilemap.
this._pixiObject.containsPoint = (position) => {
if (!this._pixiObject) {
// Ease debugging by throwing now rather than waiting for an exception later.
throw new Error(
'containsPoint called on a destroyed PIXI object - this object was not properly removed from the PIXI container.'
);
return;
}

// Turns the world position to the local object coordinates
const localPosition = new PIXI.Point();
this._pixiObject.worldTransform.applyInverse(position, localPosition);
Expand All @@ -2281,21 +2328,32 @@ module.exports = {
onRemovedFromScene() {
super.onRemovedFromScene();
this._pixiObject.destroy();

// Not strictly necessary, but helps finding wrong
// handling of this._pixiObject in its container.
this._pixiObject = null;
}

_replacePixiObject(newPixiObject) {
if (this._pixiObject !== null)
this._pixiContainer.removeChild(this._pixiObject);
this._pixiObject = newPixiObject;
this._pixiContainer.addChild(this._pixiObject);
}

onLoadingError() {
_onLoadingError() {
this.errorPixiObject =
this.errorPixiObject ||
new PIXI.Sprite(this._pixiResourcesLoader.getInvalidPIXITexture());
this._pixiContainer.addChild(this.errorPixiObject);
this._pixiObject = this.errorPixiObject;

this._replacePixiObject(this.errorPixiObject);
}

onLoadingSuccess() {
_onLoadingSuccess() {
if (this.errorPixiObject) {
this._pixiContainer.removeChild(this.errorPixiObject);
this._replacePixiObject(this.tileMapPixiObject);

this.errorPixiObject = null;
this._pixiObject = this.tileMapPixiObject;
}
}

Expand Down Expand Up @@ -2363,11 +2421,11 @@ module.exports = {
pako,
(tileMap) => {
if (!tileMap) {
this.onLoadingError();
this._onLoadingError();
// _loadTiledMapWithCallback already log errors
return;
}
this.onLoadingSuccess();
this._onLoadingSuccess();

this.width = tileMap.getWidth();
this.height = tileMap.getHeight();
Expand Down
83 changes: 58 additions & 25 deletions newIDE/app/src/InstancesEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ import {
import ClickInterceptor from './ClickInterceptor';
import getObjectByName from '../Utils/GetObjectByName';
import { AffineTransformation } from '../Utils/AffineTransformation';
import { ErrorFallbackComponent } from '../UI/ErrorBoundary';
import { Trans } from '@lingui/macro';
import { generateUUID } from 'three/src/math/MathUtils';

const gd: libGDevelop = global.gd;

export const instancesEditorId = 'instances-editor-canvas';
Expand Down Expand Up @@ -123,7 +127,14 @@ type Props = {|
showObjectInstancesIn3D: boolean,
|};

export default class InstancesEditor extends Component<Props> {
type State = {|
renderingError: null | {|
error: Error,
uniqueErrorId: string,
|},
|};

export default class InstancesEditor extends Component<Props, State> {
lastContextMenuX = 0;
lastContextMenuY = 0;
lastCursorX: number | null = null;
Expand Down Expand Up @@ -162,6 +173,10 @@ export default class InstancesEditor extends Component<Props> {
hasCursorMovedSinceItIsDown = false;
_showObjectInstancesIn3D: boolean = false;

state = {
renderingError: null,
};

componentDidMount() {
// Initialize the PIXI renderer, if possible
if (this.canvasArea && !this.pixiRenderer) {
Expand Down Expand Up @@ -1499,31 +1514,38 @@ export default class InstancesEditor extends Component<Props> {
if (this._renderingPaused) return;

// Avoid killing the CPU by limiting the rendering calls.
if (
this.fpsLimiter.shouldUpdate() &&
!shouldPreventRenderingInstanceEditors()
) {
this.canvasCursor.render();
this.grid.render();
this.highlightedInstance.render();
this.tileMapPaintingPreview.render();
this.clickInterceptor.render();
this.selectedInstances.render();
this.selectionRectangle.render();
this.windowBorder.render();
this.windowMask.render();
this.statusBar.render();
this.background.render();

this.instancesRenderer.render(
this.pixiRenderer,
this.threeRenderer,
this.viewPosition,
this.uiPixiContainer,
this.backgroundPixiContainer
);
try {
if (
this.fpsLimiter.shouldUpdate() &&
!shouldPreventRenderingInstanceEditors()
) {
this.canvasCursor.render();
this.grid.render();
this.highlightedInstance.render();
this.tileMapPaintingPreview.render();
this.clickInterceptor.render();
this.selectedInstances.render();
this.selectionRectangle.render();
this.windowBorder.render();
this.windowMask.render();
this.statusBar.render();
this.background.render();

this.instancesRenderer.render(
this.pixiRenderer,
this.threeRenderer,
this.viewPosition,
this.uiPixiContainer,
this.backgroundPixiContainer
);
}
this.nextFrame = requestAnimationFrame(this._renderScene);
} catch (error) {
console.error('Exception caught while doing the rendering:', error);
this.setState({
renderingError: { error, uniqueErrorId: generateUUID() },
});
}
this.nextFrame = requestAnimationFrame(this._renderScene);
};

pauseSceneRendering = () => {
Expand Down Expand Up @@ -1558,6 +1580,17 @@ export default class InstancesEditor extends Component<Props> {
render() {
if (!this.props.project) return null;

if (this.state.renderingError) {
return (
<ErrorFallbackComponent
error={this.state.renderingError.error}
componentTitle={<Trans>Instances editor rendering</Trans>}
componentStack="[InstancesEditor rendering]"
uniqueErrorId={this.state.renderingError.uniqueErrorId}
/>
);
}

return (
<DropTarget
canDrop={() => true}
Expand Down

0 comments on commit 02c3e93

Please sign in to comment.