# Real-time matchmaking channel. # # Client flow: # 1. Connect: ws(s)://host/cable?token= # 2. Subscribe to "MatchmakingChannel" # 3. Send { action: "start" } to enter the queue # 4. Receive { type: "matched", ... } or { type: "timeout" } # 5. Disconnecting (or sending { action: "stop" }) cancels the search. class MatchmakingChannel < ApplicationCable::Channel def subscribed # NB: must mirror MatchmakingService.broadcast, which uses # broadcast_to(user_id) → "matchmaking:". stream_for(current_user) # would use the GlobalID-based name and never receive those broadcasts. stream_from "matchmaking:#{current_user.id}" # If a match was found while this user had no subscription # (e.g. brief reconnect), deliver it now — exactly once. if match = MatchmakingService.claim(current_user.id) transmit(match) end end def start(_data) MatchmakingService.enqueue(current_user.id, rating: current_user.rating) end def stop MatchmakingService.cancel(current_user.id) end def unsubscribed MatchmakingService.cancel(current_user.id) end end