diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index b71fb7cd6..65fa6ac35 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -182,8 +182,8 @@ export function joinLobby( console.log("leaving game"); + currentGameRunner?.stop(); currentGameRunner = null; - transport.leaveGame(); return true; }; @@ -513,6 +513,8 @@ export class ClientGameRunner { this.isActive = false; this.worker.cleanup(); this.transport.leaveGame(); + this.renderer.dispose(); + this.input.destroy(); if (this.connectionCheckInterval) { clearInterval(this.connectionCheckInterval); this.connectionCheckInterval = null; diff --git a/src/client/graphics/GameRenderer.ts b/src/client/graphics/GameRenderer.ts index ec2a9ec61..c63d0ef1b 100644 --- a/src/client/graphics/GameRenderer.ts +++ b/src/client/graphics/GameRenderer.ts @@ -461,4 +461,9 @@ export class GameRenderer { this.canvas.width = Math.ceil(width / window.devicePixelRatio); this.canvas.height = Math.ceil(height / window.devicePixelRatio); } + + dispose() { + // Clean up all layers + this.layers.forEach((l) => l.dispose?.()); + } } diff --git a/src/client/graphics/layers/Layer.ts b/src/client/graphics/layers/Layer.ts index 456648f79..41960f1b9 100644 --- a/src/client/graphics/layers/Layer.ts +++ b/src/client/graphics/layers/Layer.ts @@ -7,4 +7,7 @@ export interface Layer { renderLayer?: (context: CanvasRenderingContext2D) => void; shouldTransform?: () => boolean; redraw?: () => void; + // Clean up resources when the layer is no longer needed. + // This should remove any DOM elements added during init() and release event listeners. + dispose?: () => void; } diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index e23d4d609..bf08fe2a2 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -45,6 +45,8 @@ export class NameLayer implements Layer { private userSettings: UserSettings = new UserSettings(); private isVisible: boolean = true; private firstPlace: PlayerView | null = null; + private resizeHandler: () => void; + private alternateViewHandler: (e: AlternateViewEvent) => void; constructor( private game: GameView, @@ -72,7 +74,8 @@ export class NameLayer implements Layer { public init() { this.canvas = createCanvas(); - window.addEventListener("resize", () => this.resizeCanvas()); + this.resizeHandler = () => this.resizeCanvas(); + window.addEventListener("resize", this.resizeHandler); this.resizeCanvas(); this.container = document.createElement("div"); @@ -98,7 +101,8 @@ export class NameLayer implements Layer { `; this.container.appendChild(style); - this.eventBus.on(AlternateViewEvent, (e) => this.onAlternateViewChange(e)); + this.alternateViewHandler = (e) => this.onAlternateViewChange(e); + this.eventBus.on(AlternateViewEvent, this.alternateViewHandler); } private onAlternateViewChange(event: AlternateViewEvent) { @@ -539,4 +543,25 @@ export class NameLayer implements Layer { } return icon; } + + public dispose() { + // Remove the container from the DOM + if (this.container && this.container.parentNode) { + this.container.parentNode.removeChild(this.container); + } + + // Remove resize event listener + if (this.resizeHandler) { + window.removeEventListener("resize", this.resizeHandler); + } + + // Remove eventBus listener + if (this.alternateViewHandler) { + this.eventBus.off(AlternateViewEvent, this.alternateViewHandler); + } + + // Clear render data + this.renders = []; + this.seenPlayers.clear(); + } }