Skip to content

Commit a571dd6

Browse files
Add leave lobby button to example
1 parent 83154c6 commit a571dd6

File tree

11 files changed

+109
-1
lines changed

11 files changed

+109
-1
lines changed

docs/api-reference.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ interface LobbyOptions {
3838
##### `join(code: string, password?: string): Promise<void>`
3939
Joins an existing lobby.
4040

41+
##### `leave(): Promise<void>`
42+
Leaves the current lobby without closing the network.
43+
4144
##### `list(filter?: object): Promise<Lobby[]>`
4245
Lists available lobbies with optional MongoDB-style filtering.
4346
```typescript

example/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
<a href="javascript:void(0)" data-action="create">create lobby</a>
3333
<a href="javascript:void(0)" data-action="join">join custom lobby</a>
34+
<a href="javascript:void(0)" data-action="leave">leave lobby</a>
3435
<script src="./main.ts" type="module"></script>
3536
</body>
3637
</html>

example/main.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ n.on('ready', () => {
5555
}
5656
})
5757

58+
document.querySelector('a[data-action="leave"]')?.addEventListener('click', () => {
59+
if (n.currentLobby !== undefined) {
60+
void n.leave()
61+
}
62+
})
63+
5864
const queryLobbies = (): void => {
5965
console.log('querying lobbies...')
6066
let filter = {}
@@ -100,6 +106,10 @@ n.on('lobby', code => {
100106
log(`lobby code ready: ${code} (and you are ${n.id})`)
101107
})
102108

109+
n.on('leave', () => {
110+
log('left lobby')
111+
})
112+
103113
n.on('signalingerror', console.error.bind(console.error))
104114
n.on('rtcerror', console.error.bind(console.error))
105115

features/basic.feature

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,12 @@ Feature: Players can create and connect a network of players
6666
Then "yellow" receives the network event "close"
6767
Then "blue" receives the network event "disconnected" with the argument "[Peer: h5yzwyizlwao]"
6868
Then "green" receives the network event "disconnected" with the argument "[Peer: h5yzwyizlwao]"
69+
70+
Scenario: A player leaves the lobby but stays connected
71+
Given "blue" is connected as "1u8fw4aph5ypt" and ready for game "4307bd86-e1df-41b8-b9df-e22afcf084bd"
72+
And "yellow" is connected as "h5yzwyizlwao" and ready for game "4307bd86-e1df-41b8-b9df-e22afcf084bd"
73+
74+
Given "blue,yellow" are joined in a lobby
75+
When "yellow" leaves the lobby
76+
Then "yellow" receives the network event "leave"
77+
Then "blue" receives the network event "disconnected" with the argument "[Peer: h5yzwyizlwao]"

features/support/steps/network.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ When('{string} disconnects', async function (this: World, playerName: string) {
202202
player.network.close()
203203
})
204204

205+
When('{string} leaves the lobby', async function (this: World, playerName: string) {
206+
const player = this.players.get(playerName)
207+
if (player == null) {
208+
throw new Error('no such player')
209+
}
210+
await player.network.leave()
211+
})
212+
205213
When('{string} requests all lobbies', async function (this: World, playerName: string) {
206214
const player = this.players.get(playerName)
207215
if (player == null) {

features/support/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ interface RecordedEvent {
66
eventPayload: IArguments
77
}
88

9-
const allEvents = ['close', 'ready', 'lobby', 'connected', 'disconnected', 'reconnecting', 'reconnected', 'message', 'signalingerror', 'signalingreconnected', 'leader', 'lobbyUpdated']
9+
const allEvents = ['close', 'ready', 'lobby', 'leave', 'connected', 'disconnected', 'reconnecting', 'reconnected', 'message', 'signalingerror', 'signalingreconnected', 'leader', 'lobbyUpdated']
1010

1111
export class Player {
1212
public lastReceivedLobbies: LobbyListEntry[] = []

internal/signaling/peer.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ func (p *Peer) HandlePacket(ctx context.Context, typ string, raw []byte) error {
108108
return fmt.Errorf("unable to handle packet: %w", err)
109109
}
110110

111+
case "leaveLobby":
112+
packet := LeaveLobbyPacket{}
113+
if err := json.Unmarshal(raw, &packet); err != nil {
114+
return fmt.Errorf("unable to unmarshal json: %w", err)
115+
}
116+
err = p.HandleLeaveLobbyPacket(ctx, packet)
117+
if err != nil {
118+
return fmt.Errorf("unable to handle packet: %w", err)
119+
}
120+
111121
case "list":
112122
packet := ListPacket{}
113123
if err := json.Unmarshal(raw, &packet); err != nil {
@@ -311,6 +321,37 @@ func (p *Peer) HandleClosePacket(ctx context.Context, packet ClosePacket) error
311321
return nil
312322
}
313323

324+
func (p *Peer) HandleLeaveLobbyPacket(ctx context.Context, packet LeaveLobbyPacket) error {
325+
logger := logging.GetLogger(ctx)
326+
327+
if p.ID == "" {
328+
return fmt.Errorf("peer not connected")
329+
}
330+
if p.Lobby == "" {
331+
return fmt.Errorf("not in a lobby")
332+
}
333+
334+
err := p.store.LeaveLobby(ctx, p.Game, p.Lobby, p.ID)
335+
if err != nil {
336+
return err
337+
}
338+
disc := DisconnectPacket{Type: "disconnect", ID: p.ID}
339+
data, err := json.Marshal(disc)
340+
if err == nil {
341+
if err := p.store.Publish(ctx, p.Game+p.Lobby, data); err != nil {
342+
logger.Error("failed to publish disconnect packet", zap.Error(err))
343+
}
344+
}
345+
_, err = p.doLeaderElectionAndPublish(ctx)
346+
if err != nil {
347+
return err
348+
}
349+
350+
p.Lobby = ""
351+
352+
return p.Send(ctx, LeftLobbyPacket{RequestID: packet.RequestID, Type: "leftLobby"})
353+
}
354+
314355
func (p *Peer) HandleListPacket(ctx context.Context, packet ListPacket) error {
315356
if p.ID == "" {
316357
return fmt.Errorf("peer not connected")

internal/signaling/types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ type LobbyUpdatedPacket struct {
9494
LobbyInfo stores.Lobby `json:"lobbyInfo"`
9595
}
9696

97+
type LeaveLobbyPacket struct {
98+
RequestID string `json:"rid"`
99+
Type string `json:"type"`
100+
}
101+
102+
type LeftLobbyPacket struct {
103+
RequestID string `json:"rid"`
104+
Type string `json:"type"`
105+
}
106+
97107
type ConnectPacket struct {
98108
Type string `json:"type"`
99109

lib/network.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Credentials from './credentials'
99
interface NetworkListeners {
1010
ready: () => void | Promise<void>
1111
lobby: (code: string, lobbyInfo: LobbyListEntry) => void | Promise<void>
12+
leave: () => void | Promise<void>
1213
leader: (leader: string) => void | Promise<void>
1314
lobbyUpdated: (code: string, settings: LobbySettings) => void | Promise<void>
1415
connecting: (peer: Peer) => void | Promise<void>
@@ -102,6 +103,14 @@ export default class Network extends EventEmitter<NetworkListeners> {
102103
return true
103104
}
104105

106+
async leave (): Promise<void> {
107+
if (this._closing || this.signaling.receivedID === undefined || this.signaling.currentLobby === undefined) {
108+
return
109+
}
110+
await this.signaling.request({ type: 'leaveLobby' })
111+
this.peers.forEach(peer => peer.close('left lobby'))
112+
}
113+
105114
close (reason?: string): void {
106115
if (this._closing || this.signaling.receivedID === undefined) {
107116
return

lib/signaling.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,13 @@ export default class Signaling extends EventEmitter<SignalingListeners> {
245245
this.network.emit('lobbyUpdated', packet.lobbyInfo.code, packet.lobbyInfo)
246246
break
247247

248+
case 'leftLobby':
249+
this.currentLobby = undefined
250+
this.currentLeader = undefined
251+
this.currentLobbyInfo = undefined
252+
this.network.emit('leave')
253+
break
254+
248255
case 'connect':
249256
if (this.receivedID === packet.id) {
250257
return // Skip self

0 commit comments

Comments
 (0)