Compare commits

...

1 Commits

Author SHA1 Message Date
cottongin
c756d45e24 fix: guard shard event emissions on both manuallyStopped and gameFinished
Prevent stale events from shards that ended naturally (not via
stopMonitor). handleMessage now gates on gameFinished in addition to
manuallyStopped. handleEntityUpdate properly cleans up on gameFinished
by emitting room.disconnected, removing from activeShards, and calling
disconnect. handleError also removes from activeShards. Probe message
handler and status broadcast bail out when the shard is stopped or the
game has finished.

Made-with: Cursor
2026-03-21 00:07:10 -04:00

View File

@@ -114,7 +114,9 @@ class EcastShardClient {
this.stopStatusBroadcast(); this.stopStatusBroadcast();
this.statusInterval = setInterval(() => { this.statusInterval = setInterval(() => {
this._refreshPlayerCount().finally(() => { this._refreshPlayerCount().finally(() => {
if (!this.manuallyStopped && !this.gameFinished) {
this.onEvent('game.status', this.getSnapshot()); this.onEvent('game.status', this.getSnapshot());
}
}); });
}, 20000); }, 20000);
} }
@@ -146,7 +148,7 @@ class EcastShardClient {
const timeout = setTimeout(() => done(probe), 10000); const timeout = setTimeout(() => done(probe), 10000);
probe.on('message', (data) => { probe.on('message', (data) => {
if (welcomed) return; if (welcomed || this.manuallyStopped) { clearTimeout(timeout); done(probe); return; }
try { try {
const msg = JSON.parse(data.toString()); const msg = JSON.parse(data.toString());
if (msg.opcode === 'client/welcome') { if (msg.opcode === 'client/welcome') {
@@ -155,6 +157,7 @@ class EcastShardClient {
if (playerCount > this.playerCount || playerNames.length !== this.playerNames.length) { if (playerCount > this.playerCount || playerNames.length !== this.playerNames.length) {
this.playerCount = playerCount; this.playerCount = playerCount;
this.playerNames = playerNames; this.playerNames = playerNames;
if (!this.manuallyStopped) {
this.onEvent('lobby.player-joined', { this.onEvent('lobby.player-joined', {
sessionId: this.sessionId, sessionId: this.sessionId,
gameId: this.gameId, gameId: this.gameId,
@@ -164,6 +167,7 @@ class EcastShardClient {
players: [...playerNames], players: [...playerNames],
maxPlayers: this.maxPlayers, maxPlayers: this.maxPlayers,
}); });
}
} else if (playerCount !== this.playerCount) { } else if (playerCount !== this.playerCount) {
this.playerCount = playerCount; this.playerCount = playerCount;
this.playerNames = playerNames; this.playerNames = playerNames;
@@ -196,6 +200,7 @@ class EcastShardClient {
} }
handleMessage(message) { handleMessage(message) {
if (this.manuallyStopped || this.gameFinished) return;
switch (message.opcode) { switch (message.opcode) {
case 'client/welcome': case 'client/welcome':
this.handleWelcome(message.result); this.handleWelcome(message.result);
@@ -301,6 +306,15 @@ class EcastShardClient {
playerCount: this.playerCount, playerCount: this.playerCount,
players: [...this.playerNames], players: [...this.playerNames],
}); });
this.onEvent('room.disconnected', {
sessionId: this.sessionId,
gameId: this.gameId,
roomCode: this.roomCode,
reason: 'room_closed',
finalPlayerCount: this.playerCount,
});
activeShards.delete(`${this.sessionId}-${this.gameId}`);
this.disconnect();
} }
} }
} }
@@ -367,6 +381,7 @@ class EcastShardClient {
reason: 'room_closed', reason: 'room_closed',
finalPlayerCount: this.playerCount, finalPlayerCount: this.playerCount,
}); });
activeShards.delete(`${this.sessionId}-${this.gameId}`);
this.disconnect(); this.disconnect();
} }
} }