diff --git a/cmd/server/ws.go b/cmd/server/ws.go
index cce5c60..91cc414 100644
--- a/cmd/server/ws.go
+++ b/cmd/server/ws.go
@@ -17,136 +17,154 @@ 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 {
- ctx, can := context.WithCancel(r.Context())
- defer can()
+type WS struct {
+ *S
+ c *websocket.Conn
+}
+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()
+ ws := WS{S: s, c: c}
+ return ws.serve(w, r)
+}
- ugs, err := NewUserGameServer(ctx, s.Session(ctx), s.games)
+func (ws WS) serve(w http.ResponseWriter, r *http.Request) error {
+ ctx, can := context.WithCancel(r.Context())
+ defer can()
+ r = r.WithContext(ctx)
+
+ ugs, err := ws.newUserGameServer(ctx)
if err != nil {
return err
}
go ugs.Listen(ctx, can, func(ctx context.Context) ([]byte, error) {
- _, b, err := c.Read(ctx)
+ _, b, err := ws.c.Read(ctx)
return b, err
})
for ugs.More(ctx) == nil {
- gameState, err := ugs.State(ctx)
- 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 the word to collect points.",
- "3. Review new kill words.",
- "",
- "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 {
+ if err := ws.Push(ctx, ugs); err != nil {
return err
}
}
return ctx.Err()
}
+
+func (ws WS) newUserGameServer(ctx context.Context) (*UserGameServer, error) {
+ return NewUserGameServer(ctx, ws.Session(ctx), ws.games)
+}
+
+func (ws WS) Push(ctx context.Context, ugs *UserGameServer) error {
+ gameState, err := ugs.State(ctx)
+ 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 the word to collect points.",
+ "3. Review new kill words.",
+ "",
+ "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 == ws.Session(ctx).ID {
+ continue
+ }
+
+ name, err := ws.games.UserName(ctx, k)
+ if err != nil {
+ return err
+ }
+
+ tags := []map[string]any{}
+ if self := gameState.Players[ws.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[ws.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 := ws.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 := ws.games.UserName(ctx, k)
+ if err != nil {
+ return err
+ }
+ items = append(items, map[string]any{"name": name})
+ }
+ msg["items"] = items
+ }
+
+ msgB, _ := json.Marshal(msg)
+ return ws.c.Write(ctx, 1, msgB)
+}