More logic for disconnects

This commit is contained in:
nak 2026-03-18 07:50:25 +00:00
parent 9c86fc57b7
commit f867753c4a
2 changed files with 67 additions and 26 deletions

View file

@ -413,6 +413,17 @@ func (m *Manager) HandleWSMessage(username string, msg map[string]interface{}) {
case "player:bust": case "player:bust":
// Handled in table.go via broadcast, no extra routing needed // Handled in table.go via broadcast, no extra routing needed
case "player:disconnect:stand":
username, _ := msg["username"].(string)
stack, _ := msg["stack"].(float64)
if username != "" && stack > 0 && m.AdjustBalance != nil {
if _, err := m.AdjustBalance(username, int64(stack)); err != nil {
log.Printf("[poker] post-hand disconnect refund error for %s: %v", username, err)
} else {
log.Printf("[poker] post-hand refunded %d chips to %s", int64(stack), username)
}
}
case "admin:position": case "admin:position":
// Admin tablet sends position so server knows where to spawn the table // Admin tablet sends position so server knows where to spawn the table
// Store temporarily in Redis for the next table:spawn request // Store temporarily in Redis for the next table:spawn request
@ -460,3 +471,35 @@ func jsonError(w http.ResponseWriter, msg string, code int) {
w.WriteHeader(code) w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]string{"error": msg}) json.NewEncoder(w).Encode(map[string]string{"error": msg})
} }
func (m *Manager) StandDisconnected(username string) {
m.mu.RLock()
var seated *Table
for _, t := range m.tables {
t.mu.RLock()
for _, s := range t.Seats {
if s.Username == username {
seated = t
break
}
}
t.mu.RUnlock()
if seated != nil {
break
}
}
m.mu.RUnlock()
if seated == nil {
return
}
stack, refundNow := seated.Disconnect(username)
if refundNow && stack > 0 && m.AdjustBalance != nil {
if _, err := m.AdjustBalance(username, stack); err != nil {
log.Printf("[poker] disconnect refund error for %s: %v", username, err)
} else {
log.Printf("[poker] refunded %d chips to %s on disconnect", stack, username)
}
}
}

View file

@ -155,26 +155,24 @@ func (t *Table) Stand(username string) (int64, error) {
return stack, nil return stack, nil
} }
func (t *Table) Disconnect(username string) (bool, error) { func (t *Table) Disconnect(username string) (int64, bool) {
t.mu.Lock() t.mu.Lock()
seat := t.findSeat(username) seat := t.findSeat(username)
if seat == nil { if seat == nil {
t.mu.Unlock() t.mu.Unlock()
return false, nil // not seated, nothing to do return 0, false
} }
if t.Hand.Phase == PhaseWaiting { if t.Hand.Phase == PhaseWaiting {
// Safe to remove immediately
stack := seat.Stack stack := seat.Stack
*seat = Seat{} *seat = Seat{}
t.mu.Unlock() t.mu.Unlock()
t.broadcastState() t.broadcastState()
return true, nil // caller should refund stack return stack, true // safe to refund immediately
} }
// Mid-hand — mark sitout, hand will clean up at end // Mid-hand — mark sitout and let the hand clean up
seat.SitOut = true seat.SitOut = true
// If it's their turn, cancel the turn timer so the auto-fold fires immediately
if t.Seats[t.Hand.ActionOn].Username == username { if t.Seats[t.Hand.ActionOn].Username == username {
if t.cancelTurn != nil { if t.cancelTurn != nil {
t.cancelTurn() t.cancelTurn()
@ -182,7 +180,7 @@ func (t *Table) Disconnect(username string) (bool, error) {
} }
t.mu.Unlock() t.mu.Unlock()
t.broadcastState() t.broadcastState()
return false, nil // not safe to refund yet return 0, false // refund will happen via player:disconnect:stand after hand ends
} }
// TopUp adds chips to a seated player's stack (between hands only). // TopUp adds chips to a seated player's stack (between hands only).