Skip to content

Commit

Permalink
Added an ability to spectate matches (very rudimentary). Closes #8.
Browse files Browse the repository at this point in the history
  • Loading branch information
bananu7 committed Oct 8, 2022
1 parent ddd5aa4 commit 602d128
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 11 deletions.
5 changes: 4 additions & 1 deletion packages/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,10 @@ function App() {
back to your game.</p>
<p><strong>GLHF!</strong></p>
<br />
<MatchList joinMatch={(matchId) => multiplayer.joinMatch(matchId)} />
<MatchList
joinMatch={matchId => multiplayer.joinMatch(matchId)}
spectateMatch={matchId => multiplayer.spectateMatch(matchId)}
/>
<div style={{textAlign:"center"}}>
<button onClick={() => multiplayer.createMatch()}>Create</button>
</div>
Expand Down
16 changes: 16 additions & 0 deletions packages/client/src/Multiplayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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)
Expand Down
18 changes: 11 additions & 7 deletions packages/client/src/components/MatchList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -25,18 +26,21 @@ export function MatchList(props: Props) {
};
}, []);

const matchRows = matches.map(m =>
<tr key={m.matchId}>
const matchRows = matches.map(m => {
const joinable = m.status.id == "Lobby";

return (<tr key={m.matchId}>
<td>{m.matchId}</td>
<td>{m.playerCount}</td>
<td>{m.status.id}</td>
<td>
{ m.status.id == "Lobby" &&
<button onClick={() => props.joinMatch(m.matchId)}>Join</button>
}
<button disabled={!joinable} onClick={() => props.joinMatch(m.matchId)}>Join</button>
</td>
</tr>
);
<td>
<button onClick={() => props.spectateMatch(m.matchId)}>Spectate</button>
</td>
</tr>);
});

return (
<table className="MatchTable">
Expand Down
43 changes: 40 additions & 3 deletions packages/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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(() => {
Expand All @@ -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);

Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 602d128

Please sign in to comment.