package main import ( "context" "encoding/json" "fmt" "io" "net/http" "slices" "strconv" "strings" "time" "github.com/coder/websocket" ) func isWS(r *http.Request) bool { return r.URL.Path == "/ws" || strings.HasPrefix(r.URL.Path, "/ws/") } func (s *S) serveWS(w http.ResponseWriter, r *http.Request) error { c, err := websocket.Accept(w, r, nil) if err != nil { return err } defer c.CloseNow() gameServer, err := NewUserGameServer(r.Context(), s.Session(r.Context()), s.games) if err != nil { return err } game := gameServer.ID ctx, can := context.WithCancel(r.Context()) defer can() go gameServer.Listen(ctx, can, func(ctx context.Context) ([]byte, error) { _, b, err := c.Read(ctx) return b, err }) var last time.Time for { select { case <-ctx.Done(): return ctx.Err() case <-time.After(time.Second * 1): } if events, err := s.games.GameEvents(ctx, game, last); err != nil { return err } else if len(events) == 0 { continue } last = time.Now() gameState, err := s.games.GameState(ctx, game) if err != nil { return err } msg := map[string]any{ "help": strings.Join([]string{ "CARD ASSASSINS (Mobile Ed.)", "", "1. Get any target to say any of his or her kill words.", "2. Click on the kill word a target said.", "", "The game ends when everyone has been assassinated.", }, "
"), } if gameState.Started { msg["page"] = "B" if gameState.Completed.IsZero() { msg["event"] = "A" items := []map[string]any{} for k, v := range gameState.Players { if k == s.Session(ctx).ID { continue } name, err := s.games.UserName(ctx, k) if err != nil { return err } tags := []map[string]any{} if self := gameState.Players[s.Session(ctx).ID]; self.KillWords.Assignee == k { for _, private := range v.KillWords.Assignment.Private { tags = append(tags, map[string]any{ "k": private.Word, "v": private.Points, }) } } for _, public := range v.KillWords.Assignment.Public { tags = append(tags, map[string]any{ "k": public.Word, "v": public.Points, }) } if self := gameState.Players[s.Session(ctx).ID]; !slices.ContainsFunc(self.Kills, func(a Kill) bool { return a.Victim == k }) { tags = append(tags, map[string]any{ "k": self.KillWords.Global.Word, "v": self.KillWords.Global.Points, }) } items = append(items, map[string]any{ "name": name, "title": strconv.Itoa(v.Points()), "tags": tags, }) } slices.SortFunc(items, func(a, b map[string]any) int { an, _ := strconv.Atoi(fmt.Sprint(a["title"])) bn, _ := strconv.Atoi(fmt.Sprint(b["title"])) return an - bn }) return io.EOF } else { msg["event"] = "B" items := []map[string]any{} for k, v := range gameState.Players { name, err := s.games.UserName(ctx, k) if err != nil { return err } tags := []map[string]any{} for _, kill := range v.Kills { tags = append(tags, map[string]any{ "k": kill.KillWord.Word, "v": kill.Victim, }) } items = append(items, map[string]any{ "name": name, "title": fmt.Sprint(v.Points()), "tags": tags, }) } msg["items"] = items } } else { msg["page"] = "A" items := []map[string]any{} for k := range gameState.Players { name, err := s.games.UserName(ctx, k) if err != nil { return err } items = append(items, map[string]any{"name": name}) } msg["items"] = items } msgB, _ := json.Marshal(msg) if err := c.Write(ctx, 1, msgB); err != nil { return err } } }