refactor pushing state over websocket into a func

This commit is contained in:
Bel LaPointe
2024-12-15 11:24:32 -07:00
parent 74a403fa6d
commit 64165c5745

View File

@@ -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.",
}, "<br>"),
}
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.",
}, "<br>"),
}
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)
}