feat(lobby): enter the game immediately and wait for the opponent inside it #51

Merged
developer merged 4 commits from feature/quick-game-open-wait into development 2026-06-13 09:14:51 +00:00
2 changed files with 37 additions and 4 deletions
Showing only changes of commit efaf633691 - Show all commits
+7 -4
View File
@@ -166,14 +166,17 @@ func toWireState(s backendclient.StateResp) wire.StateView {
// encodeMatch builds a MatchResult payload.
func encodeMatch(m backendclient.MatchResp) []byte {
b := flatbuffers.NewBuilder(512)
matched := m.Matched && m.Game != nil
// Enqueue always lands the caller in a game; an open game awaiting an opponent reports
// matched=false but still carries it, so encode the game whenever it is present (else the
// client never receives it and cannot navigate in).
hasGame := m.Game != nil
var game flatbuffers.UOffsetT
if matched {
if hasGame {
game = buildGameView(b, *m.Game)
}
fb.MatchResultStart(b)
fb.MatchResultAddMatched(b, matched)
if matched {
fb.MatchResultAddMatched(b, m.Matched)
if hasGame {
fb.MatchResultAddGame(b, game)
}
b.Finish(fb.MatchResultEnd(b))
@@ -114,6 +114,36 @@ func TestEnqueueRoundTripEncodesMatch(t *testing.T) {
}
}
func TestEnqueueEncodesOpenGameWhenNotMatched(t *testing.T) {
// An auto-match enqueue that opens a game awaiting an opponent returns matched=false but
// still carries the game; it must reach the client so it navigates into the game at once.
backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(`{"matched":false,"game":{"id":"g-open","variant":"scrabble_en","status":"open","players":2,"to_move":0,"seats":[]}}`))
})
defer cleanup()
reg := transcode.NewRegistry(backend, nil)
op, _ := reg.Lookup(transcode.MsgLobbyEnqueue)
b := flatbuffers.NewBuilder(32)
v := b.CreateString("scrabble_en")
fb.EnqueueRequestStart(b)
fb.EnqueueRequestAddVariant(b, v)
b.Finish(fb.EnqueueRequestEnd(b))
payload, err := op.Handler(context.Background(), transcode.Request{Payload: b.FinishedBytes(), UserID: "u-1"})
if err != nil {
t.Fatalf("handler: %v", err)
}
m := fb.GetRootAsMatchResult(payload, 0)
if m.Matched() {
t.Fatal("an open game awaiting an opponent must report matched=false")
}
if g := m.Game(nil); g == nil || string(g.Id()) != "g-open" {
t.Fatalf("open game must be on the wire even when not matched: %+v", g)
}
}
func TestDomainErrorSurfacesBackendCode(t *testing.T) {
backend, cleanup := fakeBackend(t, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusConflict)