commit 264dcc30a67e6a00881aba57e8c7b9a7060756a2
parent d02757a0fdb7667e5d84eb732a6426e68dcfb879
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Sat, 23 Dec 2023 00:04:41 -0500
cleanup
Diffstat:
3 files changed, 125 insertions(+), 72 deletions(-)
diff --git a/pkg/web/handlers/poker.go b/pkg/web/handlers/poker.go
@@ -446,74 +446,89 @@ func PokerBetHandler(c echo.Context) error {
return c.Redirect(http.StatusFound, "/")
}
+ roomUserTopic := roomID.UserTopic(authUser.ID)
+ sub := poker.PokerPubSub.Subscribe([]string{roomUserTopic})
+ defer sub.Close()
+ quit := hutils.CloseSignalChan(c)
+ hutils.SetStreamingHeaders(c)
+
+ // Keep track of users streams, so we can limit how many are open at one time per user
+ if err := usersStreamsManager.Inst.Add(authUser.ID, roomUserTopic); err != nil {
+ return nil
+ }
+ defer usersStreamsManager.Inst.Remove(authUser.ID, roomUserTopic)
+
if c.Request().Method == http.MethodPost {
submitBtn := c.Request().PostFormValue("submitBtn")
if submitBtn == "check" {
g.Check(authUser.ID)
- return c.Redirect(http.StatusFound, c.Request().Referer())
} else if submitBtn == "call" {
g.Call(authUser.ID)
- return c.Redirect(http.StatusFound, c.Request().Referer())
} else if submitBtn == "fold" {
g.Fold(authUser.ID)
- return c.Redirect(http.StatusFound, c.Request().Referer())
} else if submitBtn == "allIn" {
g.AllIn(authUser.ID)
- return c.Redirect(http.StatusFound, c.Request().Referer())
} else {
raiseBtn := c.Request().PostFormValue("raise")
if raiseBtn == "raise" {
g.Raise(authUser.ID)
- return c.Redirect(http.StatusFound, c.Request().Referer())
} else if raiseBtn == "raiseValue" {
raiseValue := database.PokerChip(utils.DoParseUint64(c.Request().PostFormValue("raiseValue")))
g.Bet(authUser.ID, raiseValue)
- return c.Redirect(http.StatusFound, c.Request().Referer())
}
}
- }
-
- roomUserTopic := roomID.UserTopic(authUser.ID)
- sub := poker.PokerPubSub.Subscribe([]string{roomUserTopic})
- defer sub.Close()
- quit := hutils.CloseSignalChan(c)
- hutils.SetStreamingHeaders(c)
-
- // Keep track of users streams, so we can limit how many are open at one time per user
- if err := usersStreamsManager.Inst.Add(authUser.ID, roomUserTopic); err != nil {
- return nil
- }
- defer usersStreamsManager.Inst.Remove(authUser.ID, roomUserTopic)
-
- send(hutils.HtmlCssReset)
+ send(`<style>#actionsForm { display: none; }</style>`)
+ c.Response().Flush()
- if player := g.OngoingPlayer(authUser.ID); player != nil {
- canCheck := g.CanCheck(player)
- canFold := g.CanFold(player)
- playerBet := player.GetBet()
- betBtnLbl := utils.Ternary(g.IsBet(), "Bet", "Raise")
- minBet := g.MinBet()
- minRaise := g.MinRaise()
- send(`<form method="post">`)
- send(` <div style="display: inline-block; margin-right: 20px;">`)
- send(fmt.Sprintf(`<input type="number" name="raiseValue" value="%s" min="%s" style="width: 90px;" /><button type="submit" name="raise" value="raiseValue">%s</button><br />`, minRaise, minRaise, betBtnLbl))
- send(` </div>`)
- send(` <div style="display: inline-block; vertical-align: top;">`)
- if canCheck {
- send(` <button name="submitBtn" value="check">Check</button>`)
- }
- if minBet-playerBet > 0 {
- send(` <button name="submitBtn" value="call">Call</button>`)
- }
- if canFold {
- send(` <button name="submitBtn" value="fold">Fold</button>`)
- }
- send(` <button name="submitBtn" value="allIn">All-in</button>`)
- send(` </div>`)
- send(`</form>`)
- send(fmt.Sprintf(`<div style="margin-top: 4px;">Min raise: %d</div>`, minRaise))
+ } else {
+
+ send(hutils.HtmlCssReset)
+
+ if player := g.OngoingPlayer(authUser.ID); player != nil {
+ if !g.IsYourTurn(player) {
+ betBtnLbl := utils.Ternary(g.IsBet(), "Bet", "Raise")
+ minRaise := g.MinRaise()
+ send(`<form method="post" id="actionsForm">`)
+ send(` <div style="display: inline-block; margin-right: 20px;">`)
+ send(fmt.Sprintf(`<input type="number" name="raiseValue" value="%s" min="%s" style="width: 90px;" /><button type="submit" name="raise" value="raiseValue">%s</button><br />`, minRaise, minRaise, betBtnLbl))
+ send(` </div>`)
+ send(` <div style="display: inline-block; vertical-align: top;">`)
+ send(` <button name="submitBtn" value="check">Check</button>`)
+ send(` <button name="submitBtn" value="call">Call</button>`)
+ send(` <button name="submitBtn" value="fold">Fold</button>`)
+ send(` <button name="submitBtn" value="allIn">All-in</button>`)
+ send(` </div>`)
+ send(`</form>`)
+ send(fmt.Sprintf(`<div style="margin-top: 4px; font-size: 20px; font-family: Arial,Helvetica,sans-serif; font-size: 18px;">Min raise: %d</div>`, minRaise))
+ } else {
+ canCheck := g.CanCheck(player)
+ canFold := g.CanFold(player)
+ playerBet := player.GetBet()
+ betBtnLbl := utils.Ternary(g.IsBet(), "Bet", "Raise")
+ minBet := g.MinBet()
+ minRaise := g.MinRaise()
+ send(`<form method="post" id="actionsForm">`)
+ send(` <div style="display: inline-block; margin-right: 20px;">`)
+ send(fmt.Sprintf(`<input type="number" name="raiseValue" value="%s" min="%s" style="width: 90px;" /><button type="submit" name="raise" value="raiseValue">%s</button><br />`, minRaise, minRaise, betBtnLbl))
+ send(` </div>`)
+ send(` <div style="display: inline-block; vertical-align: top;">`)
+ if canCheck {
+ send(` <button name="submitBtn" value="check">Check</button>`)
+ }
+ if minBet-playerBet > 0 {
+ send(` <button name="submitBtn" value="call">Call</button>`)
+ }
+ if canFold {
+ send(` <button name="submitBtn" value="fold">Fold</button>`)
+ }
+ send(` <button name="submitBtn" value="allIn">All-in</button>`)
+ send(` </div>`)
+ send(`</form>`)
+ send(fmt.Sprintf(`<div style="margin-top: 4px; font-size: 20px; font-family: Arial,Helvetica,sans-serif; font-size: 18px;">Min raise: %d</div>`, minRaise))
+ }
+ }
+ c.Response().Flush()
}
- c.Response().Flush()
Loop:
for {
diff --git a/pkg/web/handlers/poker/events.go b/pkg/web/handlers/poker/events.go
@@ -56,6 +56,10 @@ type PlayerFoldEvent struct {
Card1Idx, Card2Idx int
}
+type PokerMinRaiseUpdatedEvent struct {
+ MinRaise database.PokerChip
+}
+
type PokerMainPotUpdatedEvent struct {
MainPot database.PokerChip
}
diff --git a/pkg/web/handlers/poker/poker.go b/pkg/web/handlers/poker/poker.go
@@ -24,7 +24,7 @@ import (
const NbPlayers = 6
const MaxUserCountdown = 60
-const MinTimeAfterGame = 1
+const MinTimeAfterGame = 10
const BackfacingDeg = "-180deg"
const BurnStackX = 400
const BurnStackY = 30
@@ -34,7 +34,7 @@ const DealSpacing = 55
const DealerStackX = 250
const DealerStackY = 30
const NbCardsPerPlayer = 2
-const animationTime = 100 * time.Millisecond
+const animationTime = 1000 * time.Millisecond
type Poker struct {
sync.Mutex
@@ -137,6 +137,7 @@ type ongoingGame struct {
autoActionEvent rwmtx.RWMtx[AutoActionEvent]
MinBet rwmtx.RWMtx[database.PokerChip]
MinRaise rwmtx.RWMtx[database.PokerChip]
+ playerToPlay rwmtx.RWMtx[database.UserID]
hasBet rwmtx.RWMtx[bool]
players pokerPlayers
createdAt time.Time
@@ -234,6 +235,13 @@ func (g *PokerGame) IsBet() (out bool) {
return
}
+func (g *PokerGame) IsYourTurn(player *pokerPlayer) (out bool) {
+ if g.ongoing != nil {
+ return player.userID == g.ongoing.playerToPlay.Get()
+ }
+ return
+}
+
func (g *PokerGame) CanCheck(player *pokerPlayer) (out bool) {
if g.ongoing != nil {
return player.bet.Get() == g.ongoing.MinBet.Get()
@@ -386,6 +394,10 @@ func (g *ongoingGame) getMainPot() (out database.PokerChip) {
return database.PokerChip(g.mainPot.Load())
}
+func (g *ongoingGame) getMinRaise() (out database.PokerChip) {
+ return g.MinRaise.Get()
+}
+
func (g *ongoingGame) setMainPot(v database.PokerChip) {
g.mainPot.Store(uint64(v))
}
@@ -811,13 +823,13 @@ func doFold(g *PokerGame, p *pokerPlayer, playerAlive *int) int {
}
func doCall(g *PokerGame, p *pokerPlayer,
- newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx, lastRaisePlayerIdx *int, playerToPlayIdx int) int {
+ newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx *int, playerToPlayIdx int) int {
pUsername := p.username
bet := utils.MinInt(g.ongoing.MinBet.Get()-p.GetBet(), p.getCash())
if bet == 0 {
return doCheck(g, p)
} else if bet == p.cash.Get() {
- return doAllIn(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx)
+ return doAllIn(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx)
} else {
p.status.Set("call")
p.doBetAndNotif(g.db, g.pokerTableID, bet, g.roomID.Topic())
@@ -828,13 +840,13 @@ func doCall(g *PokerGame, p *pokerPlayer,
}
func doAllIn(g *PokerGame, p *pokerPlayer,
- newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx, lastRaisePlayerIdx *int, playerToPlayIdx int) int {
+ newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx *int, playerToPlayIdx int) int {
bet := p.getCash()
minBet := g.ongoing.MinBet.Get()
if (p.GetBet() + bet) > minBet {
*lastBetPlayerIdx = playerToPlayIdx
- *lastRaisePlayerIdx = playerToPlayIdx
g.ongoing.MinRaise.Set(bet)
+ PokerPubSub.Pub(g.roomID.Topic(), PokerMinRaiseUpdatedEvent{MinRaise: bet})
}
g.ongoing.MinBet.Set(utils.MaxInt(p.GetBet()+bet, minBet))
p.doBetAndNotif(g.db, g.pokerTableID, bet, g.roomID.Topic())
@@ -848,13 +860,13 @@ func doAllIn(g *PokerGame, p *pokerPlayer,
}
func doRaise(g *PokerGame, p *pokerPlayer,
- newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx, lastRaisePlayerIdx *int, playerToPlayIdx int, evt playerEvent) int {
+ newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx *int, playerToPlayIdx int, evt playerEvent) int {
evt.Bet = g.ongoing.MinRaise.Get()
- return doBet(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx, evt)
+ return doBet(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx, evt)
}
func doBet(g *PokerGame, p *pokerPlayer,
- newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx, lastRaisePlayerIdx *int, playerToPlayIdx int, evt playerEvent) int {
+ newlyAllInPlayers *[]*pokerPlayer, lastBetPlayerIdx *int, playerToPlayIdx int, evt playerEvent) int {
roomTopic := g.roomID.Topic()
roomUserTopic := g.roomID.UserTopic(p.userID)
minBet := g.ongoing.MinBet.Get()
@@ -862,7 +874,7 @@ func doBet(g *PokerGame, p *pokerPlayer,
playerBet := p.bet.Get()
bet := evt.Bet + (minBet - playerBet)
if bet >= p.cash.Get() {
- return doAllIn(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx)
+ return doAllIn(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx)
}
playerTotalBet := playerBet + bet
betLbl := utils.Ternary(g.IsBet(), "bet", "raise")
@@ -878,7 +890,7 @@ func doBet(g *PokerGame, p *pokerPlayer,
return continueGetPlayerEventLoop
}
*lastBetPlayerIdx = playerToPlayIdx
- *lastRaisePlayerIdx = playerToPlayIdx
+ PokerPubSub.Pub(g.roomID.Topic(), PokerMinRaiseUpdatedEvent{MinRaise: evt.Bet})
g.ongoing.MinRaise.Set(evt.Bet)
g.ongoing.MinBet.Set(playerTotalBet)
@@ -924,14 +936,14 @@ func handleAutoActionReceived(g *PokerGame, autoCache map[database.UserID]autoAc
func applyAutoAction(g *PokerGame, p *pokerPlayer,
newlyAllInPlayers *[]*pokerPlayer,
- lastBetPlayerIdx, lastRaisePlayerIdx, playerAlive *int, playerToPlayIdx int, autoAction autoAction,
+ lastBetPlayerIdx, playerAlive *int, playerToPlayIdx int, autoAction autoAction,
autoCache map[database.UserID]autoAction) (actionResult int) {
pUserID := p.userID
roomUserTopic := g.roomID.UserTopic(pUserID)
if autoAction.action > NoAction {
time.Sleep(500 * time.Millisecond)
- actionResult = handlePlayerActionEvent(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerAlive, playerToPlayIdx, autoAction.evt)
+ actionResult = handlePlayerActionEvent(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerAlive, playerToPlayIdx, autoAction.evt)
}
delete(autoCache, pUserID)
setAutoAction(g, roomUserTopic, "")
@@ -940,7 +952,7 @@ func applyAutoAction(g *PokerGame, p *pokerPlayer,
func handlePlayerActionEvent(g *PokerGame, p *pokerPlayer,
newlyAllInPlayers *[]*pokerPlayer,
- lastBetPlayerIdx, lastRaisePlayerIdx, playerAlive *int, playerToPlayIdx int, evt playerEvent) (actionResult int) {
+ lastBetPlayerIdx, playerAlive *int, playerToPlayIdx int, evt playerEvent) (actionResult int) {
p.lastActionTS = time.Now()
if evt.Fold {
@@ -948,13 +960,13 @@ func handlePlayerActionEvent(g *PokerGame, p *pokerPlayer,
} else if evt.Check {
actionResult = doCheck(g, p)
} else if evt.Call {
- actionResult = doCall(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx)
+ actionResult = doCall(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx)
} else if evt.AllIn {
- actionResult = doAllIn(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx)
+ actionResult = doAllIn(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx)
} else if evt.Raise {
- actionResult = doRaise(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx, evt)
+ actionResult = doRaise(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx, evt)
} else if evt.Bet > 0 {
- actionResult = doBet(g, p, newlyAllInPlayers, lastBetPlayerIdx, lastRaisePlayerIdx, playerToPlayIdx, evt)
+ actionResult = doBet(g, p, newlyAllInPlayers, lastBetPlayerIdx, playerToPlayIdx, evt)
} else {
actionResult = continueGetPlayerEventLoop
}
@@ -963,15 +975,15 @@ func handlePlayerActionEvent(g *PokerGame, p *pokerPlayer,
// Return either or not the game ended because only 1 player left playing (or none)
func execBettingRound(g *PokerGame, skip int, minBet database.PokerChip) bool {
+ roomID := g.roomID
+ roomTopic := roomID.Topic()
g.ongoing.MinBet.Set(minBet)
g.ongoing.MinRaise.Set(g.PokerTableMinBet)
+ PokerPubSub.Pub(roomTopic, PokerMinRaiseUpdatedEvent{MinRaise: g.PokerTableMinBet})
db := g.db
ongoing := g.ongoing
- roomID := g.roomID
- roomTopic := roomID.Topic()
_, dealerIdx := ongoing.getPlayerBySeatIdx(int(g.dealerSeatIdx.Load()))
playerToPlayIdx := (dealerIdx + skip) % len(ongoing.players)
- lastRaisePlayerIdx := -1
lastBetPlayerIdx := -1
newlyAllInPlayers := make([]*pokerPlayer, 0)
autoCache := make(map[database.UserID]autoAction)
@@ -1000,6 +1012,7 @@ RoundIsSettledLoop:
for { // Repeat until all players have played
playerToPlayIdx = (playerToPlayIdx + 1) % len(ongoing.players)
p := ongoing.players[playerToPlayIdx]
+ g.ongoing.playerToPlay.Set(p.userID)
p.countChancesToAction++
pUserID := p.userID
roomUserTopic := roomID.UserTopic(pUserID)
@@ -1007,7 +1020,6 @@ RoundIsSettledLoop:
if playerToPlayIdx == lastBetPlayerIdx {
break AllPlayersLoop
}
- lastRaisePlayerIdx = utils.Ternary(lastRaisePlayerIdx == -1, playerToPlayIdx, lastRaisePlayerIdx)
lastBetPlayerIdx = utils.Ternary(lastBetPlayerIdx == -1, playerToPlayIdx, lastBetPlayerIdx)
if !p.canBet() {
continue AllPlayersLoop
@@ -1029,7 +1041,7 @@ RoundIsSettledLoop:
// Check for pre-selected action
if autoActionVal, ok := autoCache[pUserID]; ok {
actionResult = applyAutoAction(g, p, &newlyAllInPlayers,
- &lastBetPlayerIdx, &lastRaisePlayerIdx, &playerAlive, playerToPlayIdx, autoActionVal, autoCache)
+ &lastBetPlayerIdx, &playerAlive, playerToPlayIdx, autoActionVal, autoCache)
goto checkActionResult
}
select {
@@ -1047,7 +1059,7 @@ RoundIsSettledLoop:
goto checkActionResult
}
actionResult = handlePlayerActionEvent(g, p, &newlyAllInPlayers,
- &lastBetPlayerIdx, &lastRaisePlayerIdx, &playerAlive, playerToPlayIdx, evt)
+ &lastBetPlayerIdx, &playerAlive, playerToPlayIdx, evt)
goto checkActionResult
checkActionResult:
@@ -1700,6 +1712,8 @@ func BuildPayloadHtml(g *PokerGame, authUser *database.User, payload any) (html
html += getPokerEventHtml(evt, animationTime.String())
case PokerMainPotUpdatedEvent:
html += drawMainPotHtml(evt)
+ case PokerMinRaiseUpdatedEvent:
+ html += drawMinRaiseHtml(evt)
}
return
}
@@ -1713,6 +1727,7 @@ func buildGameDiv(g *PokerGame, authUser *database.User) (html string) {
html += buildActionsDiv(roomID)
html += buildDealerTokenHtml(g)
html += buildMainPotHtml(g)
+ html += buildMinRaiseHtml(g)
html += buildWinnerHtml()
html += buildCountdownsHtml()
html += `</div>`
@@ -1791,6 +1806,17 @@ func buildMainPotHtml(g *PokerGame) string {
return html
}
+func buildMinRaiseHtml(g *PokerGame) string {
+ ongoing := g.ongoing
+ html := `<div id="minRaise"></div>`
+ minRaise := uint64(0)
+ if ongoing != nil {
+ minRaise = uint64(ongoing.getMinRaise())
+ }
+ html += `<style>#minRaise:before { content: "Min raise: ` + itoa1(minRaise) + `"; }</style>`
+ return html
+}
+
func buildCountdownsHtml() (html string) {
for i := 1; i <= NbPlayers; i++ {
idxStr := itoa(i)
@@ -2030,6 +2056,13 @@ func drawMainPotHtml(evt PokerMainPotUpdatedEvent) (html string) {
return
}
+func drawMinRaiseHtml(evt PokerMinRaiseUpdatedEvent) (html string) {
+ html += `<style>`
+ html += `#minRaise:before { content: "Min raise: ` + itoa2(evt.MinRaise) + `"; }`
+ html += `</style>`
+ return
+}
+
func getPokerEventHtml(payload PokerEvent, animationTime string) string {
transform := `transform: translate(` + itoa(payload.Left) + `px, ` + itoa(payload.Top) + `px)`
transform += utils.Ternary(payload.Angle != "", ` rotateZ(`+payload.Angle+`)`, ``)
@@ -2305,7 +2338,8 @@ body {
#countdown4 { top: 404px; left: 375px; position: absolute; display: none; z-index: 100; }
#countdown5 { top: 404px; left: 185px; position: absolute; display: none; z-index: 100; }
#countdown6 { top: 419px; left: 59px; position: absolute; display: none; z-index: 100; }
-#mainPot { position: absolute; top: 220px; left: 250px; font-size: 20px; font-family: Arial,Helvetica,sans-serif; }
+#mainPot { position: absolute; top: 220px; left: 215px; font-size: 20px; font-family: Arial,Helvetica,sans-serif; }
+#minRaise { position: absolute; top: 220px; left: 365px; font-size: 18px; font-family: Arial,Helvetica,sans-serif; }
#winner { position: absolute; top: 265px; left: 250px; }
#errorMsg {
margin-top: 10px;