drop observers who have left
This commit is contained in:
		
							
								
								
									
										32
									
								
								game.go
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								game.go
									
									
									
									
									
								
							| @@ -46,6 +46,11 @@ func applyAction(g *game.Match, a action) error { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type observer struct { | ||||||
|  | 	conn *websocket.Conn | ||||||
|  | 	ctx  context.Context | ||||||
|  | } | ||||||
|  |  | ||||||
| // gameActor is an actor that updates a game's state and relays changes to all | // gameActor is an actor that updates a game's state and relays changes to all | ||||||
| // observers. | // observers. | ||||||
| func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <-chan person) { | func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <-chan person) { | ||||||
| @@ -53,7 +58,7 @@ func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <- | |||||||
| 	// definitely expired. | 	// definitely expired. | ||||||
| 	ctx, stop := context.WithTimeoutCause(ctx, 4*time.Hour, errMatchExpired) | 	ctx, stop := context.WithTimeoutCause(ctx, 4*time.Hour, errMatchExpired) | ||||||
| 	defer stop() | 	defer stop() | ||||||
| 	var obs []person | 	var obs []observer | ||||||
| 	actions := make(chan action, 2) | 	actions := make(chan action, 2) | ||||||
| 	go playerActor(ctx, dealer, actions) | 	go playerActor(ctx, dealer, actions) | ||||||
| 	go playerActor(ctx, chall, actions) | 	go playerActor(ctx, chall, actions) | ||||||
| @@ -72,10 +77,8 @@ func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <- | |||||||
| 				go p.conn.Close(websocket.StatusNormalClosure, "looks like you dropped") | 				go p.conn.Close(websocket.StatusNormalClosure, "looks like you dropped") | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| 			obs = append(obs, p) | 			q := p.conn.CloseRead(ctx) | ||||||
| 			// TODO(zeph): CloseRead returns a context we can use to drop the | 			obs = append(obs, observer{conn: p.conn, ctx: q}) | ||||||
| 			// observer when they disconnect |  | ||||||
| 			p.conn.CloseRead(ctx) |  | ||||||
| 		case a := <-actions: | 		case a := <-actions: | ||||||
| 			slog.DebugContext(ctx, "got action", "from", a.Player, "action", a.Action) | 			slog.DebugContext(ctx, "got action", "from", a.Player, "action", a.Action) | ||||||
| 			err := applyAction(g, a) | 			err := applyAction(g, a) | ||||||
| @@ -102,6 +105,13 @@ func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <- | |||||||
| 				slog.ErrorContext(ctx, "action caused a mystery error", "from", a.Player, "action", a.Action, "err", err.Error()) | 				slog.ErrorContext(ctx, "action caused a mystery error", "from", a.Player, "action", a.Action, "err", err.Error()) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		// Clear observers who have left. | ||||||
|  | 		for k := len(obs) - 1; k >= 0; k-- { | ||||||
|  | 			if obs[k].ctx.Err() != nil { | ||||||
|  | 				obs[k], obs[len(obs)-1] = obs[len(obs)-1], observer{} | ||||||
|  | 				obs = obs[:len(obs)-1] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -127,7 +137,7 @@ func playerActor(ctx context.Context, p person, actions chan<- action) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func broadcast(ctx context.Context, g *game.Match, dealer, chall person, obs []person) { | func broadcast(ctx context.Context, g *game.Match, dealer, chall person, obs []observer) { | ||||||
| 	// TODO(zeph): this probably should return an error or some other signal | 	// TODO(zeph): this probably should return an error or some other signal | ||||||
| 	// if a player drops so that the actor knows to quit | 	// if a player drops so that the actor knows to quit | ||||||
| 	if err := wsjson.Write(ctx, dealer.conn, g.DTO(dealer.id)); err != nil { | 	if err := wsjson.Write(ctx, dealer.conn, g.DTO(dealer.id)); err != nil { | ||||||
| @@ -138,14 +148,18 @@ func broadcast(ctx context.Context, g *game.Match, dealer, chall person, obs []p | |||||||
| 		// TODO(zeph): concede, but we need to be careful not to recurse | 		// TODO(zeph): concede, but we need to be careful not to recurse | ||||||
| 		slog.DebugContext(ctx, "lost challenger", "player", chall.id, "err", err.Error()) | 		slog.DebugContext(ctx, "lost challenger", "player", chall.id, "err", err.Error()) | ||||||
| 	} | 	} | ||||||
|  | 	if len(obs) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	d := g.DTO(player.ID{}) | ||||||
| 	for _, p := range obs { | 	for _, p := range obs { | ||||||
| 		if err := wsjson.Write(ctx, p.conn, g.DTO(p.id)); err != nil { | 		if err := wsjson.Write(ctx, p.conn, &d); err != nil { | ||||||
| 			slog.DebugContext(ctx, "lost observer", "player", p.id, "err", err.Error()) | 			slog.DebugContext(ctx, "lost observer", "err", err.Error()) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func gameOver(ctx context.Context, dealer, chall person, obs []person) { | func gameOver(ctx context.Context, dealer, chall person, obs []observer) { | ||||||
| 	// TODO(zeph): need to communicate to the server that these have gone away | 	// TODO(zeph): need to communicate to the server that these have gone away | ||||||
| 	go dealer.conn.Close(websocket.StatusNormalClosure, "match ended") | 	go dealer.conn.Close(websocket.StatusNormalClosure, "match ended") | ||||||
| 	go chall.conn.Close(websocket.StatusNormalClosure, "match ended") | 	go chall.conn.Close(websocket.StatusNormalClosure, "match ended") | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user