diff --git a/packages/client/src/App.tsx b/packages/client/src/App.tsx index 6c89877..386ac76 100644 --- a/packages/client/src/App.tsx +++ b/packages/client/src/App.tsx @@ -208,7 +208,10 @@ function App() { back to your game.

GLHF!


- multiplayer.joinMatch(matchId)} /> + multiplayer.joinMatch(matchId)} + spectateMatch={matchId => multiplayer.spectateMatch(matchId)} + />
diff --git a/packages/client/src/Multiplayer.ts b/packages/client/src/Multiplayer.ts index 9521ab3..8fbb068 100644 --- a/packages/client/src/Multiplayer.ts +++ b/packages/client/src/Multiplayer.ts @@ -68,6 +68,12 @@ export class Multiplayer { this.onUpdatePacket && this.onUpdatePacket(u); }) + this.channel.on('spectating', (data: Data) => { + this.matchId = (data as {matchId: string}).matchId; + localStorage.setItem('matchId', this.matchId); + this.onMatchConnected && this.onMatchConnected(this.matchId); + }); + this.channel.on('connected', (data: Data) => { if (!this.matchId) { throw "Server responded with connection but the multiplayer isn't initialized to a match"; @@ -139,6 +145,16 @@ export class Multiplayer { this.channel.emit('connect', data); }); }; + + spectateMatch(matchId: string) { + console.log(`[Multiplayer] spectating match ${matchId}`) + const data : IdentificationPacket = { + userId: this.userId, + matchId + }; + + this.channel.emit('spectate', data); + } async leaveMatch() { if (!this.matchId) diff --git a/packages/client/src/components/MatchList.tsx b/packages/client/src/components/MatchList.tsx index 0cc72e0..d11f568 100644 --- a/packages/client/src/components/MatchList.tsx +++ b/packages/client/src/components/MatchList.tsx @@ -4,6 +4,7 @@ import { HTTP_API_URL } from '../config' type Props = { joinMatch: (matchId: string) => void; + spectateMatch: (matchId: string) => void; } export function MatchList(props: Props) { @@ -25,18 +26,21 @@ export function MatchList(props: Props) { }; }, []); - const matchRows = matches.map(m => - + const matchRows = matches.map(m => { + const joinable = m.status.id == "Lobby"; + + return ( {m.matchId} {m.playerCount} {m.status.id} - { m.status.id == "Lobby" && - - } + - - ); + + + + ); + }); return ( diff --git a/packages/server/index.ts b/packages/server/index.ts index 92a48bc..c019146 100644 --- a/packages/server/index.ts +++ b/packages/server/index.ts @@ -23,10 +23,16 @@ type PlayerEntry = { channel?: ServerChannel, } +type SpectatorEntry = { + user: UserId, + channel: ServerChannel, +} + type Match = { game: Game, matchId: string, players: PlayerEntry[], + spectators: SpectatorEntry[], } const app = express() @@ -76,7 +82,7 @@ app.post('/create', async (req, res) => { const map = await getMap('assets/map.png'); const game = newGame(map); const matchId = String(++lastMatchId); // TODO - matches.push({ game, matchId, players: [] }); + matches.push({ game, matchId, players: [], spectators: [] }); const TICK_MS = 50; setInterval(() => { @@ -93,6 +99,10 @@ app.post('/create', async (req, res) => { p.channel.emit('tick', updatePackets[i]); }); + + match.spectators.forEach((s, i) => + s.channel.emit('tick', updatePackets[0]) + ); // io.room(matchId).emit('tick', updatePackets[0]); }, TICK_MS); @@ -183,20 +193,47 @@ io.onConnection(channel => { console.log(`${channel.id} got disconnected`) }) + channel.on('spectate', (data: Data) => { + const packet = data as IdentificationPacket; + + const m = matches.find(m => m.matchId === packet.matchId); + if (!m) { + console.warn("Received a spectate request to a match that doesn't exist"); + channel.emit('spectate failure', packet.matchId, {reliable: true}); + return; + } + + const spectatorEntry = { + channel, + user: packet.userId, + } + + m.spectators.push(spectatorEntry); + + channel.userData = { + matchId: packet.matchId + }; + + channel.join(String(packet.matchId)); + channel.emit('spectating', {matchId: packet.matchId}, {reliable: true}); + + console.log(`[index] Channel of user ${packet.userId} spectating match ${packet.matchId}`); + }); + channel.on('connect', (data: Data) => { // TODO properly validate data format const packet = data as IdentificationPacket; const m = matches.find(m => m.matchId === packet.matchId); if (!m) { - console.warn("Received a connect request to a match that doesn't exist"); + console.warn("[index] Received a connect request to a match that doesn't exist"); channel.emit('connection failure', packet.matchId, {reliable: true}); return; } const playerEntry = m.players.find(p => p.user === packet.userId); if (!playerEntry) { - console.warn(`Received a connect request to a match(${packet.matchId}) that the user(${packet.userId}) hasn't joined`); + console.warn(`[index] Received a connect request to a match(${packet.matchId}) that the user(${packet.userId}) hasn't joined`); channel.emit('connection failure', packet.matchId, {reliable: true}); return; }