Add spectator mode for late-joining players
- Server: Late joiners are added as spectators instead of players - Server: Send forced-spectator event only to joining spectator (not broadcast) - Server: Track spectators separately and move them to players after game ends - Client: Handle forced-spectator event to show all player boards - Client: Spectators see all boards equally without main/spectator highlighting - Client: Mobile view shows scrollable vertical list of all boards for spectators - Fix: All cleared lines are sent as garbage to each opponent (not randomized) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+48
-2
@@ -4,14 +4,19 @@ class NetworkManager {
|
||||
constructor() {
|
||||
this.socket = null;
|
||||
this.currentPlayerId = null;
|
||||
this.isSpectator = false;
|
||||
this.players = {};
|
||||
this.spectators = {};
|
||||
this.gameState = {};
|
||||
this.listeners = {
|
||||
onPlayerJoined: null,
|
||||
onPlayerLeft: null,
|
||||
onGameStarted: null,
|
||||
onStateUpdate: null,
|
||||
onGameOver: null
|
||||
onGameOver: null,
|
||||
onSpectatorJoined: null,
|
||||
onSpectatorLeft: null,
|
||||
onForcedSpectator: null
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,6 +67,33 @@ class NetworkManager {
|
||||
this.listeners.onGameOver(data);
|
||||
}
|
||||
});
|
||||
|
||||
// Forced spectator mode - game already in progress
|
||||
this.socket.on('forced-spectator', ({ spectatorId, states, players }) => {
|
||||
this.isSpectator = true;
|
||||
this.updateStates(states);
|
||||
this.updatePlayers(players);
|
||||
if (this.listeners.onForcedSpectator) {
|
||||
this.listeners.onForcedSpectator({ states, players });
|
||||
}
|
||||
});
|
||||
|
||||
// Spectator joined
|
||||
this.socket.on('spectator-joined', ({ spectator, spectators }) => {
|
||||
this.updateSpectators(spectators);
|
||||
if (this.listeners.onSpectatorJoined) {
|
||||
this.listeners.onSpectatorJoined(spectator);
|
||||
}
|
||||
});
|
||||
|
||||
// Spectator left
|
||||
this.socket.on('spectator-left', ({ spectatorId, spectators }) => {
|
||||
delete this.spectators[spectatorId];
|
||||
this.updateSpectators(spectators);
|
||||
if (this.listeners.onSpectatorLeft) {
|
||||
this.listeners.onSpectatorLeft(spectatorId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
joinLobby(playerName) {
|
||||
@@ -116,6 +148,13 @@ class NetworkManager {
|
||||
});
|
||||
}
|
||||
|
||||
updateSpectators(spectators) {
|
||||
this.spectators = {};
|
||||
spectators.forEach(s => {
|
||||
this.spectators[s.id] = s;
|
||||
});
|
||||
}
|
||||
|
||||
getPlayer(playerId) {
|
||||
return this.players[playerId];
|
||||
}
|
||||
@@ -132,13 +171,20 @@ class NetworkManager {
|
||||
return this.gameState;
|
||||
}
|
||||
|
||||
getAllSpectators() {
|
||||
return this.spectators;
|
||||
}
|
||||
|
||||
setListener(event, callback) {
|
||||
const listenerMap = {
|
||||
'player-joined': 'onPlayerJoined',
|
||||
'player-left': 'onPlayerLeft',
|
||||
'game-started': 'onGameStarted',
|
||||
'state-update': 'onStateUpdate',
|
||||
'game-over': 'onGameOver'
|
||||
'game-over': 'onGameOver',
|
||||
'forced-spectator': 'onForcedSpectator',
|
||||
'spectator-joined': 'onSpectatorJoined',
|
||||
'spectator-left': 'onSpectatorLeft'
|
||||
};
|
||||
if (callback) {
|
||||
this.listeners[listenerMap[event]] = callback;
|
||||
|
||||
Reference in New Issue
Block a user