552 lines
23 KiB
Markdown
552 lines
23 KiB
Markdown
# Project Felt — Phase 2 Product Specification
|
|
|
|
## Cash Game Operations
|
|
|
|
**Version:** 0.1 Draft
|
|
**Date:** 2026-02-28
|
|
**Depends on:** Phase 1 (Tournament Management) — shared Leaf, displays, player DB, network
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [Overview](#1-overview)
|
|
2. [Data Architecture](#2-data-architecture)
|
|
3. [Feature Specification](#3-feature-specification)
|
|
4. [Display Integration](#4-display-integration)
|
|
5. [Player Mobile Experience](#5-player-mobile-experience)
|
|
6. [API Design](#6-api-design)
|
|
7. [Sync & Cloud Features](#7-sync--cloud-features)
|
|
8. [Roadmap](#8-roadmap)
|
|
|
|
---
|
|
|
|
## 1. Overview
|
|
|
|
### What Phase 2 Adds
|
|
|
|
Phase 1 handles tournaments — structured events with a beginning and end. Phase 2 adds cash games — continuous, open-ended sessions that are the daily revenue driver for most poker rooms.
|
|
|
|
Cash games have fundamentally different management needs: waitlists instead of registrations, seat availability instead of seating assignments, rake tracking instead of prize pool calculation, session duration instead of bust-out order.
|
|
|
|
### Design Principles
|
|
|
|
1. **Coexistence.** Tournaments and cash games run simultaneously on the same Leaf. Display nodes can show tournament clocks on some screens and cash game status on others.
|
|
2. **Shared player database.** A player who plays tournaments and cash games has one profile. Their history includes both.
|
|
3. **Shared dealer pool.** Dealers are assigned to cash tables or tournament tables from the same scheduling system (Phase 3 completes this, but the data model supports it from Phase 2).
|
|
4. **Real-time everywhere.** Waitlist updates, seat availability, and table status push to displays and mobile devices in real-time via the same WebSocket infrastructure used for tournaments.
|
|
5. **Offline-first.** Cash game management works fully offline. Cloud sync happens when available.
|
|
|
|
---
|
|
|
|
## 2. Data Architecture
|
|
|
|
### New Tables (Leaf SQLite + Core PostgreSQL)
|
|
|
|
```sql
|
|
-- Game Type Registry
|
|
CREATE TABLE game_types (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL, -- "No-Limit Hold'em"
|
|
short_name TEXT NOT NULL, -- "NLH"
|
|
variant TEXT NOT NULL, -- holdem, omaha, omaha_hilo, stud, stud_hilo, draw, mixed, short_deck, other
|
|
betting TEXT NOT NULL, -- no_limit, pot_limit, fixed_limit, spread_limit
|
|
max_players INTEGER NOT NULL DEFAULT 10, -- Max seats per table for this game
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Rake Structures
|
|
CREATE TABLE rake_structures (
|
|
id TEXT PRIMARY KEY,
|
|
name TEXT NOT NULL, -- "Standard 5% cap €10"
|
|
type TEXT NOT NULL, -- percentage, time_rake, flat
|
|
percentage REAL, -- 0.05 for 5%
|
|
cap INTEGER, -- Max rake per pot (cents)
|
|
time_amount INTEGER, -- Time rake amount per interval (cents)
|
|
time_interval INTEGER, -- Time rake interval (minutes)
|
|
flat_amount INTEGER, -- Flat rake per hand (cents)
|
|
no_flop_no_drop BOOLEAN NOT NULL DEFAULT true,
|
|
notes TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Cash Tables
|
|
CREATE TABLE cash_tables (
|
|
id TEXT PRIMARY KEY,
|
|
table_number INTEGER NOT NULL, -- Physical table number
|
|
game_type_id TEXT NOT NULL REFERENCES game_types(id),
|
|
stakes TEXT NOT NULL, -- "1/2", "2/5", "5/10"
|
|
small_blind INTEGER NOT NULL, -- Cents
|
|
big_blind INTEGER NOT NULL, -- Cents
|
|
straddle BOOLEAN NOT NULL DEFAULT false,
|
|
min_buyin INTEGER NOT NULL, -- Cents (e.g., 10000 = €100)
|
|
max_buyin INTEGER, -- Cents (NULL = uncapped)
|
|
max_players INTEGER NOT NULL DEFAULT 10,
|
|
rake_id TEXT REFERENCES rake_structures(id),
|
|
status TEXT NOT NULL DEFAULT 'closed', -- closed, open, breaking
|
|
dealer_id TEXT REFERENCES players(id), -- Current dealer (Phase 3)
|
|
opened_at DATETIME,
|
|
closed_at DATETIME,
|
|
must_move_for TEXT REFERENCES cash_tables(id), -- If this is a must-move table, which main game
|
|
notes TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Seats (real-time seat tracking)
|
|
CREATE TABLE cash_seats (
|
|
id TEXT PRIMARY KEY,
|
|
table_id TEXT NOT NULL REFERENCES cash_tables(id),
|
|
seat_number INTEGER NOT NULL,
|
|
player_id TEXT REFERENCES players(id), -- NULL = empty
|
|
session_id TEXT REFERENCES cash_sessions(id),
|
|
status TEXT NOT NULL DEFAULT 'empty', -- empty, occupied, reserved, away
|
|
occupied_at DATETIME,
|
|
UNIQUE(table_id, seat_number)
|
|
);
|
|
|
|
-- Player Sessions
|
|
CREATE TABLE cash_sessions (
|
|
id TEXT PRIMARY KEY,
|
|
player_id TEXT NOT NULL REFERENCES players(id),
|
|
table_id TEXT NOT NULL REFERENCES cash_tables(id),
|
|
seat_number INTEGER NOT NULL,
|
|
game_type_id TEXT NOT NULL REFERENCES game_types(id),
|
|
stakes TEXT NOT NULL,
|
|
buyin_total INTEGER NOT NULL DEFAULT 0, -- Total bought in (cents), accumulates with rebuys
|
|
cashout_amount INTEGER, -- Final cashout (cents), NULL = still playing
|
|
started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
ended_at DATETIME,
|
|
duration_minutes INTEGER, -- Calculated on session close
|
|
notes TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Session Buy-ins (track each individual buy-in/rebuy)
|
|
CREATE TABLE cash_session_buyins (
|
|
id TEXT PRIMARY KEY,
|
|
session_id TEXT NOT NULL REFERENCES cash_sessions(id),
|
|
amount INTEGER NOT NULL, -- Cents
|
|
type TEXT NOT NULL DEFAULT 'buyin', -- buyin, rebuy, addon
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Waitlists
|
|
CREATE TABLE waitlists (
|
|
id TEXT PRIMARY KEY,
|
|
game_type_id TEXT NOT NULL REFERENCES game_types(id),
|
|
stakes TEXT NOT NULL, -- "1/2", "2/5" — or "any" for game-type-only
|
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Waitlist Entries
|
|
CREATE TABLE waitlist_entries (
|
|
id TEXT PRIMARY KEY,
|
|
waitlist_id TEXT NOT NULL REFERENCES waitlists(id),
|
|
player_id TEXT NOT NULL REFERENCES players(id),
|
|
position INTEGER NOT NULL, -- Queue position
|
|
status TEXT NOT NULL DEFAULT 'waiting', -- waiting, called, seated, expired, removed
|
|
called_at DATETIME, -- When player was notified
|
|
response_deadline DATETIME, -- Must respond by
|
|
notification_sent BOOLEAN NOT NULL DEFAULT false,
|
|
notes TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Seat Change Requests
|
|
CREATE TABLE seat_change_requests (
|
|
id TEXT PRIMARY KEY,
|
|
session_id TEXT NOT NULL REFERENCES cash_sessions(id),
|
|
player_id TEXT NOT NULL REFERENCES players(id),
|
|
table_id TEXT NOT NULL REFERENCES cash_tables(id),
|
|
requested_seat INTEGER, -- NULL = any seat, specific number = that seat
|
|
reason TEXT, -- "Prefer seat 1", "Want to move tables"
|
|
status TEXT NOT NULL DEFAULT 'pending', -- pending, fulfilled, cancelled
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
fulfilled_at DATETIME
|
|
);
|
|
|
|
-- Rake Log (per table per time period)
|
|
CREATE TABLE rake_log (
|
|
id TEXT PRIMARY KEY,
|
|
table_id TEXT NOT NULL REFERENCES cash_tables(id),
|
|
period_start DATETIME NOT NULL,
|
|
period_end DATETIME NOT NULL,
|
|
total_rake INTEGER NOT NULL, -- Cents
|
|
hands_dealt INTEGER, -- If tracked
|
|
notes TEXT,
|
|
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
```
|
|
|
|
### Extended Player Table
|
|
|
|
Phase 2 adds columns to the existing `players` table:
|
|
|
|
```sql
|
|
ALTER TABLE players ADD COLUMN preferred_games TEXT; -- JSON array of game_type_ids
|
|
ALTER TABLE players ADD COLUMN preferred_stakes TEXT; -- JSON array of stakes strings
|
|
ALTER TABLE players ADD COLUMN total_cash_hours REAL DEFAULT 0; -- Lifetime cash game hours
|
|
ALTER TABLE players ADD COLUMN total_cash_sessions INTEGER DEFAULT 0;
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Feature Specification
|
|
|
|
### 3.1 Game Type Registry
|
|
|
|
The venue defines all games they spread. This is the foundation for waitlists, table assignments, dealer skills (Phase 3), and analytics.
|
|
|
|
**Built-in game types (pre-populated, editable):**
|
|
|
|
| Variant | Betting Structures |
|
|
|---------|-------------------|
|
|
| Hold'em | No-Limit, Pot-Limit, Fixed-Limit, Spread-Limit |
|
|
| Omaha | Pot-Limit, No-Limit, Fixed-Limit |
|
|
| Omaha Hi-Lo | Pot-Limit, Fixed-Limit |
|
|
| Stud | Fixed-Limit |
|
|
| Stud Hi-Lo | Fixed-Limit |
|
|
| Razz | Fixed-Limit |
|
|
| Draw (2-7, 5-Card) | Fixed-Limit, No-Limit |
|
|
| Short Deck (6+) | No-Limit, Ante-only |
|
|
| Mixed (H.O.R.S.E., 8-Game, Dealer's Choice) | Rotation |
|
|
|
|
Venues can add custom game types. Each game type has a max players setting (e.g., Hold'em = 10, Short Deck = 6).
|
|
|
|
### 3.2 Table Management
|
|
|
|
**Table lifecycle:** Closed → Open → Breaking → Closed
|
|
|
|
**Opening a table:**
|
|
1. Operator selects game type and stakes from dropdown
|
|
2. System applies default rake structure (editable)
|
|
3. Table status changes to "open"
|
|
4. Display nodes immediately show the table in the "Games Running" view
|
|
5. Waitlist players for this game/stakes are notified
|
|
|
|
**Table status board (operator view):**
|
|
```
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
│ TABLE MANAGEMENT │
|
|
│ │
|
|
│ Table 1 │ NLH 1/2 │ 8/10 seats │ ● OPEN │ [Manage] │
|
|
│ Table 2 │ PLO 2/5 │ 6/6 seats │ ● OPEN │ [Manage] │
|
|
│ Table 3 │ NLH 1/2 │ 9/10 seats │ ● OPEN │ [Manage] │
|
|
│ Table 4 │ NLH 2/5 │ 5/10 seats │ ● OPEN │ [Manage] │
|
|
│ Table 5 │ — │ — │ ○ CLOSED │ [Open] │
|
|
│ Table 6 │ Tournament │ Tournament │ ● TOURNY │ │
|
|
│ │
|
|
│ [Open New Table] [Close Table] [Break Table] │
|
|
└─────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
**Seat map (per table):**
|
|
```
|
|
┌──────────────────────────────────────────┐
|
|
│ TABLE 1 — NLH 1/2 — 8/10 occupied │
|
|
│ │
|
|
│ [8] [9] [10] │
|
|
│ [7] [1] │
|
|
│ [6] [2] │
|
|
│ [5] [4] [3] │
|
|
│ │
|
|
│ ● = Occupied ○ = Empty ⊘ = Reserved │
|
|
│ │
|
|
│ Seat 1: Mike S. (sat 2h15m, in €200) │
|
|
│ Seat 2: — EMPTY — [Seat Player] │
|
|
│ Seat 3: Anna K. (sat 45m, in €100) │
|
|
│ ... │
|
|
└──────────────────────────────────────────┘
|
|
```
|
|
|
|
### 3.3 Waitlist Management
|
|
|
|
**Waitlist structure:** One waitlist per game-type + stakes combination. If a venue runs NLH 1/2 and NLH 2/5, those are separate waitlists.
|
|
|
|
**Player joins waitlist:**
|
|
1. Player requests via mobile PWA or operator adds them
|
|
2. System assigns queue position
|
|
3. Player sees their position on mobile in real-time
|
|
4. When a seat opens, the top player is called
|
|
|
|
**Calling a player:**
|
|
1. Seat opens at a table matching the waitlist
|
|
2. System notifies the first player (mobile push + SMS optional)
|
|
3. Player has a configurable response window (default: 5 minutes)
|
|
4. If player responds "on my way" → seat is reserved
|
|
5. If player doesn't respond → marked as expired, next player called
|
|
6. Operator can manually override (skip players, call out of order)
|
|
|
|
**Waitlist display (shown on venue TVs):**
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────┐
|
|
│ WAITLIST │
|
|
│ │
|
|
│ NLH 1/2 │ NLH 2/5 │ PLO 2/5 │
|
|
│ 1. Mike S. │ 1. David R. │ No waitlist │
|
|
│ 2. Anna K. │ 2. Tom H. │ │
|
|
│ 3. Chris L. │ │ │
|
|
│ 4. Sarah M. │ │ │
|
|
│ │ │ │
|
|
│ Tables: 2 open │ Tables: 1 │ Tables: 1 │
|
|
│ Seats avail: 1 │ Seats: FULL │ Seats: 2 │
|
|
└──────────────────────────────────────────────────┘
|
|
```
|
|
|
|
### 3.4 Session Tracking
|
|
|
|
When a player sits down at a cash table, a session begins. The session tracks:
|
|
|
|
- When they sat down
|
|
- All buy-ins and rebuys (amounts and timestamps)
|
|
- When they stood up
|
|
- Cashout amount (optional — not all venues track this)
|
|
- Total duration
|
|
|
|
**Privacy considerations:**
|
|
- Buy-in and cashout tracking is **venue-configurable.** Some venues want full financial tracking for analytics. Some just want to know who's playing.
|
|
- Individual session financials are never visible to other players
|
|
- Aggregated data (average buy-in, average session length) available for analytics
|
|
- Players can see their own session history in their mobile profile
|
|
|
|
### 3.5 Must-Move Tables
|
|
|
|
When a main game is full and demand is high, venues open a "must-move" table. Players at the must-move table get automatically moved to the main table when a seat opens (longest-waiting first).
|
|
|
|
**How it works:**
|
|
1. Operator opens Table 3 as a must-move for Table 1 (both NLH 1/2)
|
|
2. System tracks must-move players' time at Table 3
|
|
3. When a seat opens at Table 1, the player who has been at Table 3 the longest is notified
|
|
4. Player moves to Table 1. Their session updates (new table, same game, continuous clock)
|
|
5. If the must-move table gets short-handed (≤4 players), operator can break it and move remaining players to the main game's waitlist
|
|
|
|
### 3.6 Rake Tracking
|
|
|
|
Configurable rake structures:
|
|
|
|
| Type | How It Works | Example |
|
|
|------|-------------|---------|
|
|
| Percentage with cap | X% of each pot, capped at Y | 5% up to €10 per hand |
|
|
| Time rake | Fixed amount per time interval | €6 per 30 minutes per seat |
|
|
| Flat per hand | Fixed amount per hand dealt | €1 per hand |
|
|
| No rake | Used for private games | — |
|
|
|
|
**No-flop-no-drop:** Configurable per rake structure. If no flop is dealt, no rake is taken.
|
|
|
|
Rake is logged per table per period (configurable: per hour, per shift, per session). Venue owner sees daily/weekly/monthly rake revenue in analytics.
|
|
|
|
### 3.7 Seat Change Requests
|
|
|
|
Players can request a seat change within the same game or to a different table:
|
|
|
|
1. Player requests via mobile or tells the floor (operator enters)
|
|
2. System queues the request
|
|
3. When the requested seat (or any seat at the target table) opens, the requesting player is offered it
|
|
4. Player accepts → moved. Player declines → request stays in queue.
|
|
5. Seat change requests have lower priority than new waitlist entries (configurable)
|
|
|
|
---
|
|
|
|
## 4. Display Integration
|
|
|
|
Cash game views use the same display node infrastructure as tournaments. A display node can be assigned to any of these views:
|
|
|
|
### Cash Game Views
|
|
|
|
**Games Running Board:**
|
|
Shows all active cash games — game type, stakes, seat availability, waitlist depth. Updated in real-time. This is the first thing a player sees when they walk in.
|
|
|
|
**Table Detail View:**
|
|
Shows a specific table's seat map, current players (first name + last initial), and game info. Useful for large rooms where players can't easily see which seats are open.
|
|
|
|
**Waitlist View:**
|
|
Shows all active waitlists, player names, and positions. Players can check their position without asking the floor.
|
|
|
|
**Combined View (Auto-Cycle):**
|
|
Cycles between games running, waitlist, and tournament info. Configurable cycle timing. Most venues will use this on their main lobby TV.
|
|
|
|
### Display Priority
|
|
|
|
When a tournament and cash games are running simultaneously:
|
|
|
|
1. Screens assigned to tournament → show tournament
|
|
2. Screens assigned to cash → show cash
|
|
3. Screens assigned to "auto" → show tournament during active levels, cash during breaks
|
|
4. Info screen playlists continue on screens not assigned to either
|
|
|
|
---
|
|
|
|
## 5. Player Mobile Experience
|
|
|
|
### Cash Game Features on Mobile PWA
|
|
|
|
**View current games:** See all running games, stakes, seat availability without walking to the venue or calling.
|
|
|
|
**Join waitlist remotely:** A player at home can join the 2/5 NLH waitlist, see their position, and head to the venue when they're close to the top. Game changer for player retention.
|
|
|
|
**Session dashboard (during play):**
|
|
```
|
|
┌─────────────────────────────┐
|
|
│ YOUR SESSION │
|
|
│ │
|
|
│ Table 1 — NLH 1/2 │
|
|
│ Seat 4 │
|
|
│ Playing for: 2h 45m │
|
|
│ Total in: €300 │
|
|
│ │
|
|
│ [Request Seat Change] │
|
|
│ [Leave Table] │
|
|
└─────────────────────────────┘
|
|
```
|
|
|
|
**Cash game history:** See all past sessions — dates, games played, duration, buy-in/cashout (if tracked). Lifetime stats: total hours played, favorite game type, average session length.
|
|
|
|
---
|
|
|
|
## 6. API Design
|
|
|
|
### REST (Operator)
|
|
|
|
Prefix: `/api/v1`
|
|
|
|
**Cash Tables:**
|
|
```
|
|
GET /cash/tables List all tables (with current status)
|
|
POST /cash/tables Create/configure a table
|
|
GET /cash/tables/:id Get table detail (seats, players)
|
|
PUT /cash/tables/:id Update table config
|
|
POST /cash/tables/:id/open Open table (set game, stakes)
|
|
POST /cash/tables/:id/close Close table (end all sessions)
|
|
POST /cash/tables/:id/break Start breaking table
|
|
```
|
|
|
|
**Seats:**
|
|
```
|
|
POST /cash/tables/:id/seats/:num/sit Seat a player
|
|
POST /cash/tables/:id/seats/:num/stand Player stands up
|
|
POST /cash/tables/:id/seats/:num/reserve Reserve seat (waitlist call)
|
|
POST /cash/tables/:id/seats/:num/away Mark player away
|
|
POST /cash/tables/:id/transfer Transfer player between seats/tables
|
|
```
|
|
|
|
**Sessions:**
|
|
```
|
|
GET /cash/sessions List sessions (active + recent)
|
|
GET /cash/sessions/:id Get session detail
|
|
POST /cash/sessions/:id/buyin Add buy-in/rebuy to session
|
|
POST /cash/sessions/:id/cashout Close session with cashout
|
|
```
|
|
|
|
**Waitlists:**
|
|
```
|
|
GET /cash/waitlists List active waitlists
|
|
POST /cash/waitlists/:id/join Add player to waitlist
|
|
POST /cash/waitlists/:id/call Call next player
|
|
POST /cash/waitlists/:id/remove Remove player from waitlist
|
|
GET /cash/waitlists/:id/position Get player's position (used by mobile)
|
|
```
|
|
|
|
**Seat Changes:**
|
|
```
|
|
POST /cash/seat-changes Request seat change
|
|
GET /cash/seat-changes List pending requests
|
|
POST /cash/seat-changes/:id/fulfill Fulfill request
|
|
POST /cash/seat-changes/:id/cancel Cancel request
|
|
```
|
|
|
|
**Game Types:**
|
|
```
|
|
GET /cash/game-types List all game types
|
|
POST /cash/game-types Create custom game type
|
|
PUT /cash/game-types/:id Update
|
|
```
|
|
|
|
**Rake:**
|
|
```
|
|
GET /cash/rake-structures List rake structures
|
|
POST /cash/rake-structures Create
|
|
GET /cash/rake/report Rake report (date range, per table/game)
|
|
POST /cash/rake/log Log rake for a period
|
|
```
|
|
|
|
### WebSocket (Real-Time)
|
|
|
|
Cash game state updates use the same WebSocket infrastructure as tournaments:
|
|
|
|
```
|
|
ws://leaf.local/ws/cash/tables → Real-time table status updates
|
|
ws://leaf.local/ws/cash/waitlist → Waitlist position updates
|
|
ws://leaf.local/ws/cash/session/:id → Player's own session updates
|
|
```
|
|
|
|
### Player Mobile API
|
|
|
|
```
|
|
GET /api/v1/me/cash/sessions My cash session history
|
|
GET /api/v1/me/cash/sessions/stats My lifetime cash stats
|
|
GET /api/v1/me/cash/waitlist My current waitlist positions
|
|
POST /api/v1/me/cash/waitlist/join Join a waitlist
|
|
POST /api/v1/me/cash/waitlist/leave Leave a waitlist
|
|
POST /api/v1/me/cash/seat-change Request seat change
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Sync & Cloud Features
|
|
|
|
Cash game data syncs to Core using the same NATS JetStream infrastructure as tournaments.
|
|
|
|
**What syncs:**
|
|
- Game type registry (Core → Leaf for multi-venue consistency, Leaf → Core for custom types)
|
|
- Session records (Leaf → Core for player history and analytics)
|
|
- Rake logs (Leaf → Core for financial reporting)
|
|
- Waitlist activity is NOT synced (ephemeral, local-only)
|
|
- Seat status is NOT synced in real-time (local-only, too volatile)
|
|
|
|
**Cloud features (require subscription):**
|
|
- Cross-venue cash game history for players
|
|
- Remote waitlist joining (player at home joins via `play.venue.felt.io`)
|
|
- Cash game analytics (revenue per game type, table utilization, peak hours)
|
|
- Multi-venue game type standardization (operator defines games once, pushes to all venues)
|
|
|
|
---
|
|
|
|
## 8. Roadmap
|
|
|
|
### v2.0 — Core Cash Game
|
|
|
|
- Game type registry with built-in types
|
|
- Cash table management (open, close, seat tracking)
|
|
- Waitlist management with player notification
|
|
- Session tracking (start, buy-in, rebuy, cashout, end)
|
|
- Display views: games running board, waitlist, table detail
|
|
- Player mobile: view games, join waitlist, session dashboard
|
|
- Basic rake tracking (percentage with cap)
|
|
|
|
### v2.1 — Advanced Features
|
|
|
|
- Must-move table automation
|
|
- Seat change request system
|
|
- All rake types (time rake, flat, no-flop-no-drop)
|
|
- Table transfer with continuous session tracking
|
|
- Waitlist SMS notifications (Twilio integration)
|
|
- Auto-open/close tables based on waitlist depth (configurable threshold)
|
|
- Cash game stats in player profiles
|
|
|
|
### v2.2 — Analytics & Integration
|
|
|
|
- Revenue dashboards (rake by game, table, period)
|
|
- Table utilization analytics (seats occupied vs. available over time)
|
|
- Peak hour analysis (when are games fullest?)
|
|
- Player analytics (session frequency, favorite games, average duration)
|
|
- Integration with Phase 3 dealer scheduling (assign dealers to cash tables)
|
|
|
|
---
|
|
|
|
*Phase 2 builds on Phase 1's infrastructure — same Leaf, same displays, same network, same player database. Cash games are additive, not a separate product.*
|