Compare commits
No commits in common. "f867753c4a5a5b87eae42aa36134aff36f4842a2" and "6cba9f8f68600a7d9dd4f73c9775476a8fc4e1a6" have entirely different histories.
f867753c4a
...
6cba9f8f68
2 changed files with 22 additions and 106 deletions
|
|
@ -413,17 +413,6 @@ 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
|
||||||
|
|
@ -471,35 +460,3 @@ 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -155,34 +155,6 @@ func (t *Table) Stand(username string) (int64, error) {
|
||||||
return stack, nil
|
return stack, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) Disconnect(username string) (int64, bool) {
|
|
||||||
t.mu.Lock()
|
|
||||||
seat := t.findSeat(username)
|
|
||||||
if seat == nil {
|
|
||||||
t.mu.Unlock()
|
|
||||||
return 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Hand.Phase == PhaseWaiting {
|
|
||||||
stack := seat.Stack
|
|
||||||
*seat = Seat{}
|
|
||||||
t.mu.Unlock()
|
|
||||||
t.broadcastState()
|
|
||||||
return stack, true // safe to refund immediately
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mid-hand — mark sitout and let the hand clean up
|
|
||||||
seat.SitOut = true
|
|
||||||
if t.Seats[t.Hand.ActionOn].Username == username {
|
|
||||||
if t.cancelTurn != nil {
|
|
||||||
t.cancelTurn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.mu.Unlock()
|
|
||||||
t.broadcastState()
|
|
||||||
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).
|
||||||
func (t *Table) TopUp(username string, amount int64) error {
|
func (t *Table) TopUp(username string, amount int64) error {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
|
@ -474,6 +446,7 @@ func (t *Table) advance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) doShowdown() {
|
func (t *Table) doShowdown() {
|
||||||
|
// Build hands for non-folded players
|
||||||
remaining := make(map[string][]string)
|
remaining := make(map[string][]string)
|
||||||
for username, cards := range t.hands {
|
for username, cards := range t.hands {
|
||||||
if !t.folded[username] {
|
if !t.folded[username] {
|
||||||
|
|
@ -483,10 +456,12 @@ func (t *Table) doShowdown() {
|
||||||
|
|
||||||
winners := Winners(remaining, t.Hand.Community)
|
winners := Winners(remaining, t.Hand.Community)
|
||||||
|
|
||||||
|
// Broadcast all remaining hands + winners
|
||||||
revealedHands := make(map[string][]string)
|
revealedHands := make(map[string][]string)
|
||||||
for _, w := range winners {
|
for _, w := range winners {
|
||||||
revealedHands[w.Username] = remaining[w.Username]
|
revealedHands[w.Username] = remaining[w.Username]
|
||||||
}
|
}
|
||||||
|
// Also include any players who chose to reveal
|
||||||
t.mu.RLock()
|
t.mu.RLock()
|
||||||
for _, seat := range t.Seats {
|
for _, seat := range t.Seats {
|
||||||
if seat.Revealed && seat.Username != "" {
|
if seat.Revealed && seat.Username != "" {
|
||||||
|
|
@ -502,6 +477,7 @@ func (t *Table) doShowdown() {
|
||||||
winnerDescs[w.Username] = w.Desc
|
winnerDescs[w.Username] = w.Desc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split pot among winners
|
||||||
share := t.Hand.Pot / int64(len(winners))
|
share := t.Hand.Pot / int64(len(winners))
|
||||||
remainder := t.Hand.Pot % int64(len(winners))
|
remainder := t.Hand.Pot % int64(len(winners))
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
|
@ -510,7 +486,7 @@ func (t *Table) doShowdown() {
|
||||||
if seat != nil {
|
if seat != nil {
|
||||||
extra := int64(0)
|
extra := int64(0)
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
extra = remainder
|
extra = remainder // first winner gets remainder chips
|
||||||
}
|
}
|
||||||
seat.Stack += share + extra
|
seat.Stack += share + extra
|
||||||
}
|
}
|
||||||
|
|
@ -527,10 +503,25 @@ func (t *Table) doShowdown() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Brief pause then reset
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(5 * time.Second)
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
t.Hand.Phase = PhaseWaiting
|
t.Hand.Phase = PhaseWaiting
|
||||||
t.cleanupSeats()
|
// Remove players with no chips
|
||||||
|
for _, s := range t.Seats {
|
||||||
|
if s.Username != "" && s.Stack == 0 {
|
||||||
|
log.Printf("[poker] %s busted out of table %s", s.Username, t.Config.ID)
|
||||||
|
// Return to wallet handled by manager on bust event
|
||||||
|
if t.Broadcast != nil {
|
||||||
|
t.Broadcast(map[string]interface{}{
|
||||||
|
"type": "player:bust",
|
||||||
|
"username": s.Username,
|
||||||
|
"tableId": t.Config.ID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*s = Seat{}
|
||||||
|
}
|
||||||
|
}
|
||||||
t.mu.Unlock()
|
t.mu.Unlock()
|
||||||
t.broadcastState()
|
t.broadcastState()
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
|
|
@ -539,6 +530,7 @@ func (t *Table) doShowdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) awardPot() {
|
func (t *Table) awardPot() {
|
||||||
|
// Find the only remaining active player
|
||||||
for _, s := range t.Seats {
|
for _, s := range t.Seats {
|
||||||
if s.Username != "" && !s.Folded {
|
if s.Username != "" && !s.Folded {
|
||||||
s.Stack += t.Hand.Pot
|
s.Stack += t.Hand.Pot
|
||||||
|
|
@ -555,10 +547,7 @@ func (t *Table) awardPot() {
|
||||||
}
|
}
|
||||||
t.Hand.Pot = 0
|
t.Hand.Pot = 0
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
t.mu.Lock()
|
|
||||||
t.Hand.Phase = PhaseWaiting
|
t.Hand.Phase = PhaseWaiting
|
||||||
t.cleanupSeats()
|
|
||||||
t.mu.Unlock()
|
|
||||||
t.broadcastState()
|
t.broadcastState()
|
||||||
t.maybeStartHand()
|
t.maybeStartHand()
|
||||||
}
|
}
|
||||||
|
|
@ -668,36 +657,6 @@ func (t *Table) bettingComplete() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Table) cleanupSeats() {
|
|
||||||
for _, s := range t.Seats {
|
|
||||||
if s.Username == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if s.Stack == 0 {
|
|
||||||
log.Printf("[poker] %s busted", s.Username)
|
|
||||||
if t.Broadcast != nil {
|
|
||||||
t.Broadcast(map[string]interface{}{
|
|
||||||
"type": "player:bust",
|
|
||||||
"username": s.Username,
|
|
||||||
"tableId": t.Config.ID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
*s = Seat{}
|
|
||||||
} else if s.SitOut {
|
|
||||||
log.Printf("[poker] standing disconnected player %s with %d chips", s.Username, s.Stack)
|
|
||||||
if t.Broadcast != nil {
|
|
||||||
t.Broadcast(map[string]interface{}{
|
|
||||||
"type": "player:disconnect:stand",
|
|
||||||
"username": s.Username,
|
|
||||||
"stack": s.Stack,
|
|
||||||
"tableId": t.Config.ID,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
*s = Seat{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── State broadcast ──────────────────────────────────────────────
|
// ── State broadcast ──────────────────────────────────────────────
|
||||||
|
|
||||||
// PublicState returns the table state safe to send to all clients.
|
// PublicState returns the table state safe to send to all clients.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue