games: the card the dealer never turned over, and the bet that came back doubled
Bust every hand and the dealer doesn't draw, which is right, but it was also not turning over: reveal is only emitted by dealerPlay, and busting out skips the dealer entirely. The browser kept the hole card face down while the settled state printed the dealer's whole total under it. Emit the reveal on that path. And standing your bet back up after a reload read the hand's bet straight off the settled state, which a double has already doubled. Reload, double 200, and the next hand starts with 400 on the spot. Plus: the share card was hand-writing a Content-Length that ServeContent overwrites anyway, and serving a zero-byte 200 for a room with no card. Claude-Session: https://claude.ai/code/session_013M5nD7PgUboJXoDcYHzpuJ
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"image/png"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net/http"
|
||||
"sync"
|
||||
@@ -85,20 +86,25 @@ func (s *Server) handleGamesOG(w http.ResponseWriter, r *http.Request) {
|
||||
// No requirePlayer here, and that is the whole point: the thing fetching this
|
||||
// is a chat server's link preview, which has never signed in and never will.
|
||||
ogOnce.Do(func() { ogCards, ogErr = drawShareCards() })
|
||||
if ogErr != nil {
|
||||
room := roomAt(time.Now().Hour())
|
||||
card := ogCards[room.Slug]
|
||||
if ogErr != nil || len(card) == 0 {
|
||||
// A room with no card is a room somebody added to roomAt and not to
|
||||
// ogPalettes. Say so rather than serving a zero-byte PNG, which an unfurler
|
||||
// will happily cache as the casino's face.
|
||||
slog.Error("games: no share card", "room", room.Slug, "err", ogErr)
|
||||
http.Error(w, "share card unavailable", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
room := roomAt(time.Now().Hour())
|
||||
png := ogCards[room.Slug]
|
||||
|
||||
w.Header().Set("Content-Type", "image/png")
|
||||
// Long enough that a busy channel isn't redrawing it, short enough that the
|
||||
// room actually turns over: the lights come on at six and the card follows
|
||||
// within the hour.
|
||||
w.Header().Set("Cache-Control", "public, max-age=900")
|
||||
w.Header().Set("Content-Length", fmt.Sprint(len(png)))
|
||||
http.ServeContent(w, r, "og.png", time.Time{}, bytes.NewReader(png))
|
||||
// ServeContent sets Content-Length itself, and gets it right for a range
|
||||
// request, which a hand-written one would not.
|
||||
http.ServeContent(w, r, "og.png", time.Time{}, bytes.NewReader(card))
|
||||
}
|
||||
|
||||
// drawShareCards renders every room's card up front. If the font is broken every
|
||||
@@ -128,16 +134,16 @@ func drawShareCard(f *opentype.Font, rm room, p ogPalette) (image.Image, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer title.Close()
|
||||
line, err := face(f, 33)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer line.Close()
|
||||
small, err := face(f, 25)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer title.Close()
|
||||
defer line.Close()
|
||||
defer small.Close()
|
||||
|
||||
img := image.NewRGBA(image.Rect(0, 0, ogWidth, ogHeight))
|
||||
|
||||
Reference in New Issue
Block a user