commit a4a48277daa79181313a818b72fdbe7da9e541f2
parent b308f34b33bc41aadf2d4604d8a79db5ef9744a4
Author: n0tr1v <n0tr1v@protonmail.com>
Date: Tue, 12 Dec 2023 04:54:21 -0500
keep track of chips when server restart
Diffstat:
4 files changed, 228 insertions(+), 30 deletions(-)
diff --git a/cmd/dkf/migrations/147.sql b/cmd/dkf/migrations/147.sql
@@ -0,0 +1,45 @@
+-- +migrate Up
+CREATE TABLE IF NOT EXISTS poker_tables (
+ id INTEGER NOT NULL PRIMARY KEY,
+ slug VARCHAR(255) UNIQUE NOT NULL,
+ name VARCHAR(50) NOT NULL,
+ min_buy_in INTEGER NOT NULL,
+ max_buy_in INTEGER NOT NULL,
+ min_bet INTEGER NOT NULL,
+ is_test TINYINT(1) NOT NULL DEFAULT 1);
+CREATE INDEX poker_tables_slug_idx ON poker_tables (slug);
+CREATE INDEX poker_tables_is_test_idx ON poker_tables (is_test);
+
+INSERT INTO poker_tables (slug, name, min_buy_in, max_buy_in, min_bet, is_test) VALUES ('test', 'test', 1000, 2000, 20, 1);
+
+CREATE TABLE IF NOT EXISTS poker_table_accounts (
+ id INTEGER NOT NULL PRIMARY KEY,
+ user_id INTEGER NOT NULL,
+ poker_table_id INTEGER NOT NULL,
+ amount INTEGER NOT NULL,
+ UNIQUE (user_id, poker_table_id),
+ CONSTRAINT poker_table_accounts_user_id_fk
+ FOREIGN KEY (user_id)
+ REFERENCES users (id)
+ ON DELETE NO ACTION
+ ON UPDATE CASCADE,
+ CONSTRAINT poker_table_accounts_poker_table_id_fk
+ FOREIGN KEY (poker_table_id)
+ REFERENCES poker_tables (id)
+ ON DELETE NO ACTION
+ ON UPDATE CASCADE);
+CREATE INDEX poker_table_accounts_poker_table_id_idx ON poker_table_accounts (poker_table_id);
+CREATE INDEX poker_table_accounts_user_id_idx ON poker_table_accounts (user_id);
+CREATE INDEX poker_table_accounts_amount_idx ON poker_table_accounts (amount);
+
+CREATE TABLE IF NOT EXISTS poker_table_bets (
+ poker_table_account_id INTEGER NOT NULL PRIMARY KEY,
+ amount INTEGER NOT NULL,
+ CONSTRAINT poker_table_bets_poker_table_account_id_fk
+ FOREIGN KEY (poker_table_account_id)
+ REFERENCES poker_table_accounts (id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE);
+CREATE INDEX poker_table_bets_poker_table_account_id_idx ON poker_table_bets (poker_table_account_id);
+
+-- +migrate Down
diff --git a/pkg/actions/actions.go b/pkg/actions/actions.go
@@ -11,6 +11,7 @@ import (
"dkforest/pkg/utils"
"dkforest/pkg/web"
"dkforest/pkg/web/handlers/interceptors"
+ "dkforest/pkg/web/handlers/poker"
"errors"
"fmt"
"github.com/mattn/go-colorable"
@@ -82,6 +83,8 @@ func Start(c *cli.Context) error {
utils.SGo(func() { xmrWatch(db) })
utils.SGo(func() { openBrowser(noBrowser, int64(port)) })
+ poker.Refund(db)
+
interceptors.LoadFilters(db)
interceptors.ChessInstance = interceptors.NewChess(db)
interceptors.BattleshipInstance = interceptors.NewBattleship(db)
diff --git a/pkg/database/tablePokerTables.go b/pkg/database/tablePokerTables.go
@@ -0,0 +1,67 @@
+package database
+
+import (
+ "github.com/sirupsen/logrus"
+)
+
+type PokerTable struct {
+ ID int64
+ Slug string
+ Name string
+ MinBuyIn int64
+ MaxBuyIn int64
+ MinBet int64
+ IsTest bool
+}
+
+func (d *DkfDB) GetPokerTableBySlug(slug string) (out PokerTable, err error) {
+ err = d.db.First(&out, "slug = ?", slug).Error
+ return
+}
+
+type PokerTableAccount struct {
+ ID int64
+ UserID UserID
+ PokerTableID int64
+ Amount int64
+}
+
+func (a *PokerTableAccount) Save(db *DkfDB) error {
+ return db.db.Save(a).Error
+}
+
+func (a *PokerTableAccount) DoSave(db *DkfDB) {
+ if err := a.Save(db); err != nil {
+ logrus.Error(err)
+ }
+}
+
+func (d *DkfDB) GetPositivePokerTableAccounts() (out []PokerTableAccount, err error) {
+ err = d.db.Find(&out, "amount > 0").Error
+ return
+}
+
+func (d *DkfDB) GetPokerTableAccount(userID UserID, pokerTableID int64) (out PokerTableAccount, err error) {
+ if err = d.db.First(&out, "user_id = ? AND poker_table_id = ?", userID, pokerTableID).Error; err != nil {
+ out = PokerTableAccount{UserID: userID, PokerTableID: pokerTableID}
+ err = d.db.Create(&out).Error
+ }
+ return
+}
+
+func (d *DkfDB) GetPokerTableBet(accountID int64) (out PokerTableBet, err error) {
+ if err = d.db.First(&out, "poker_table_account_id = ?", accountID).Error; err != nil {
+ out = PokerTableBet{PokerTableAccountID: accountID}
+ err = d.db.Create(&out).Error
+ }
+ return
+}
+
+type PokerTableBet struct {
+ PokerTableAccountID int64
+ Amount int64
+}
+
+func (b *PokerTableBet) UpdateAmount(db *DkfDB, accountID, v int64) {
+ db.db.Model(b).Where("poker_table_account_id = ?", accountID).Update("amount", v)
+}
diff --git a/pkg/web/handlers/poker/poker.go b/pkg/web/handlers/poker/poker.go
@@ -76,6 +76,7 @@ type PlayerEvent struct {
Check bool
Fold bool
AllIn bool
+ Unsit bool
Bet int
}
@@ -130,7 +131,14 @@ func (p *PokerPlayer) isAllIn() bool {
return p.Cash == 0
}
-func (p *PokerPlayer) doBet(bet int) {
+func (p *PokerPlayer) doBet(db *database.DkfDB, roomID string, bet int) {
+ user, _ := db.GetUserByUsername(database.Username(p.Username))
+ pokerTable, _ := db.GetPokerTableBySlug(roomID)
+ account, _ := db.GetPokerTableAccount(user.ID, pokerTable.ID)
+ account.Amount -= int64(bet)
+ account.DoSave(db)
+ tableBet, _ := db.GetPokerTableBet(account.ID)
+ tableBet.UpdateAmount(db, account.ID, tableBet.Amount+int64(bet))
p.RoundTotalBet += bet
p.Bet += bet
p.Cash -= bet
@@ -332,18 +340,18 @@ func (g *PokerGame) incrDealerIdx() {
g.bigBlindIdx = (int(dealerIdx) + 2) % nbPlayers
}
-func (g *PokerGame) UnSitPlayer(db *database.DkfDB, roomID, playerUsername string) error {
+func (g *PokerGame) UnSitPlayer(db *database.DkfDB, roomID string, authUser *database.User, pokerTable database.PokerTable) error {
g.PlayersMtx.RLock()
defer g.PlayersMtx.RUnlock()
for idx, p := range g.Players {
- if p != nil && p.Username == playerUsername {
- return g.UnSitPlayer1(db, roomID, p, idx)
+ if p != nil && p.Username == authUser.Username.String() {
+ return g.UnSitPlayer1(db, roomID, p, idx, pokerTable)
}
}
return nil
}
-func (g *PokerGame) UnSitPlayer1(db *database.DkfDB, roomID string, player *PokerStandingPlayer, idx int) error {
+func (g *PokerGame) UnSitPlayer1(db *database.DkfDB, roomID string, player *PokerStandingPlayer, idx int, pokerTable database.PokerTable) error {
roomTopic := "room_" + roomID
user, err := db.GetUserByUsername(database.Username(player.Username))
@@ -351,11 +359,22 @@ func (g *PokerGame) UnSitPlayer1(db *database.DkfDB, roomID string, player *Poke
return err
}
- user.ChipsTest += player.Cash
+ account, err := db.GetPokerTableAccount(user.ID, pokerTable.ID)
+ if err != nil {
+ return err
+ }
+
+ user.ChipsTest += int(account.Amount)
user.DoSave(db)
+ account.Amount = 0
+ account.DoSave(db)
if g.Ongoing != nil {
if player := g.Ongoing.GetPlayer(player.Username); player != nil {
+ select {
+ case g.PlayersEventCh <- PlayerEvent{Player: player.Username, Unsit: true}:
+ default:
+ }
player.Folded.Store(true)
player.CardsMtx.RLock()
if len(player.Cards) >= 1 {
@@ -375,7 +394,7 @@ func (g *PokerGame) UnSitPlayer1(db *database.DkfDB, roomID string, player *Poke
return nil
}
-func (g *PokerGame) SitPlayer(db *database.DkfDB, authUser *database.User, pos int) error {
+func (g *PokerGame) SitPlayer(authUser *database.User, pos int, chips int64) error {
g.PlayersMtx.Lock()
defer g.PlayersMtx.Unlock()
for _, p := range g.Players {
@@ -386,10 +405,7 @@ func (g *PokerGame) SitPlayer(db *database.DkfDB, authUser *database.User, pos i
if g.Players[pos] != nil {
return errors.New("seat already taken")
}
- chips := utils.MinInt(authUser.ChipsTest, 2000)
- authUser.ChipsTest -= chips
- authUser.DoSave(db)
- g.Players[pos] = &PokerStandingPlayer{Username: authUser.Username.String(), Cash: chips, LastActionTS: time.Now()}
+ g.Players[pos] = &PokerStandingPlayer{Username: authUser.Username.String(), Cash: int(chips), LastActionTS: time.Now()}
return nil
}
@@ -458,7 +474,7 @@ func setWaitTurn(g *PokerGame, roomTopic string, seatIdx int) {
}
// Return either or not the game ended because only 1 player left playing (or none)
-func waitPlayersActionFn(g *PokerGame, roomID string, skip, minBet int) bool {
+func waitPlayersActionFn(db *database.DkfDB, g *PokerGame, roomID string, skip, minBet int) bool {
roomTopic := "room_" + roomID
roomLogsTopic := "room_" + roomID + "_logs"
dealerSeatIdx := int(g.DealerSeatIdx.Load())
@@ -537,6 +553,14 @@ OUTER:
break LOOP
}
+ if evt.Unsit {
+ playerAlive--
+ if playerAlive == 1 {
+ break OUTER
+ }
+ continue
+ }
+
if evt.Player != p.Username {
continue
}
@@ -573,7 +597,7 @@ OUTER:
if bet == 0 {
newLogEvent(g, roomLogsTopic, fmt.Sprintf("%s check", p.Username))
} else {
- p.doBet(bet)
+ p.doBet(db, roomID, bet)
PokerPubSub.Pub(roomTopic, PlayerBetEvent{PlayerSeatIdx: p.SeatIdx, Player: p.Username, Bet: bet, TotalBet: p.Bet, Cash: p.Cash})
logMsg := fmt.Sprintf("%s call (%d)", p.Username, bet)
if p.isAllIn() {
@@ -589,7 +613,7 @@ OUTER:
lastRaisePlayerIdx = playerToPlayIdx
}
minBet = p.Bet + bet
- p.doBet(bet)
+ p.doBet(db, roomID, bet)
PokerPubSub.Pub(roomTopic, PlayerBetEvent{PlayerSeatIdx: p.SeatIdx, Player: p.Username, Bet: bet, TotalBet: p.Bet, Cash: p.Cash})
logMsg := fmt.Sprintf("%s all-in (%d)", p.Username, bet)
if p.isAllIn() {
@@ -613,7 +637,7 @@ OUTER:
lastRaisePlayerIdx = playerToPlayIdx
}
minBet = p.Bet + bet
- p.doBet(bet)
+ p.doBet(db, roomID, bet)
PokerPubSub.Pub(roomTopic, PlayerBetEvent{PlayerSeatIdx: p.SeatIdx, Player: p.Username, Bet: bet, TotalBet: p.Bet, Cash: p.Cash})
logMsg := fmt.Sprintf("%s bet %d", p.Username, bet)
if p.isAllIn() {
@@ -786,7 +810,7 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
p := g.Ongoing.Players[g.smallBlindIdx]
bet := bigBlindBet / 2
- p.doBet(bet)
+ p.doBet(db, roomID, bet)
PokerPubSub.Pub(roomTopic, PlayerBetEvent{PlayerSeatIdx: p.SeatIdx, Player: p.Username, Bet: bet, TotalBet: p.Bet, Cash: p.Cash})
newLogEvent(g, roomLogsTopic, fmt.Sprintf("%s small blind %d", p.Username, bet))
@@ -794,7 +818,7 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
p = g.Ongoing.Players[g.bigBlindIdx]
bet = bigBlindBet
- p.doBet(bet)
+ p.doBet(db, roomID, bet)
PokerPubSub.Pub(roomTopic, PlayerBetEvent{PlayerSeatIdx: p.SeatIdx, Player: p.Username, Bet: bet, TotalBet: p.Bet, Cash: p.Cash})
newLogEvent(g, roomLogsTopic, fmt.Sprintf("%s big blind %d", p.Username, bet))
@@ -805,7 +829,7 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
// Wait for players to bet/call/check/fold...
time.Sleep(time.Second)
- if waitPlayersActionFn(g, roomID, 2, bigBlindBet) {
+ if waitPlayersActionFn(db, g, roomID, 2, bigBlindBet) {
goto END
}
@@ -819,7 +843,7 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
// Wait for players to bet/call/check/fold...
time.Sleep(time.Second)
- if waitPlayersActionFn(g, roomID, 0, 0) {
+ if waitPlayersActionFn(db, g, roomID, 0, 0) {
goto END
}
@@ -831,7 +855,7 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
// Wait for players to bet/call/check/fold...
time.Sleep(time.Second)
- if waitPlayersActionFn(g, roomID, 0, 0) {
+ if waitPlayersActionFn(db, g, roomID, 0, 0) {
goto END
}
@@ -843,7 +867,7 @@ func dealerThread(db *database.DkfDB, g *PokerGame, roomID string) {
// Wait for players to bet/call/check/fold...
time.Sleep(time.Second)
- if waitPlayersActionFn(g, roomID, 0, 0) {
+ if waitPlayersActionFn(db, g, roomID, 0, 0) {
goto END
}
@@ -863,6 +887,13 @@ END:
}
for _, el := range playersGain {
newLogEvent(g, roomLogsTopic, fmt.Sprintf("Winner #%d: %s %s -> %d", el.Group, el.Player.Username, el.HandStr, el.Gain))
+ user, _ := db.GetUserByUsername(database.Username(el.Player.Username))
+ pokerTable, _ := db.GetPokerTableBySlug(roomID)
+ account, _ := db.GetPokerTableAccount(user.ID, pokerTable.ID)
+ account.Amount += int64(el.Gain)
+ account.DoSave(db)
+ tableBet, _ := db.GetPokerTableBet(account.ID)
+ tableBet.UpdateAmount(db, account.ID, 0)
winnersStr += el.Player.Username + " "
el.Player.Cash += el.Gain
}
@@ -876,13 +907,17 @@ END:
time.Sleep(MinTimeAfterGame * time.Second)
// Auto unsit inactive players
- for idx, p := range g.Players {
- if p != nil && p.LastActionTS.Before(g.Ongoing.CreatedAt) {
- if err := g.UnSitPlayer1(db, roomID, p, idx); err == nil {
- PokerPubSub.Pub(roomTopic, PokerSeatLeftEvent{})
- newLogEvent(g, roomLogsTopic, fmt.Sprintf("%s auto un-sit", p.Username))
+ if pokerTable, err := db.GetPokerTableBySlug(roomID); err == nil {
+ for idx, p := range g.Players {
+ if p != nil && p.LastActionTS.Before(g.Ongoing.CreatedAt) {
+ if err := g.UnSitPlayer1(db, roomID, p, idx, pokerTable); err == nil {
+ PokerPubSub.Pub(roomTopic, PokerSeatLeftEvent{})
+ newLogEvent(g, roomLogsTopic, fmt.Sprintf("%s auto un-sit", p.Username))
+ }
}
}
+ } else {
+ logrus.Error(err)
}
g.IsGameStarted.Store(false)
@@ -1131,6 +1166,21 @@ func PokerFoldHandler(c echo.Context) error {
return c.HTML(http.StatusOK, hutils.HtmlCssReset+`<form method="post"><button>Fold</button></form>`)
}
+func Refund(db *database.DkfDB) {
+ accounts, _ := db.GetPositivePokerTableAccounts()
+ for _, account := range accounts {
+ if user, err := db.GetUserByID(account.UserID); err == nil {
+ tableBet, _ := db.GetPokerTableBet(account.ID)
+ account.Amount += tableBet.Amount
+ user.ChipsTest += int(account.Amount)
+ user.DoSave(db)
+ account.Amount = 0
+ account.DoSave(db)
+ tableBet.UpdateAmount(db, account.ID, 0)
+ }
+ }
+}
+
func PokerDealHandler(c echo.Context) error {
roomID := c.Param("roomID")
authUser := c.Get("authUser").(*database.User)
@@ -1151,6 +1201,11 @@ func PokerUnSitHandler(c echo.Context) error {
db := c.Get("database").(*database.DkfDB)
authUser := c.Get("authUser").(*database.User)
roomID := c.Param("roomID")
+ html := hutils.HtmlCssReset + `<form method="post"><button>UnSit</button></form>`
+ pokerTable, err := db.GetPokerTableBySlug(roomID)
+ if err != nil {
+ return c.HTML(http.StatusOK, html)
+ }
roomTopic := "room_" + roomID
roomLogsTopic := "room_" + roomID + "_logs"
g := PokerInstance.GetGame(roomID)
@@ -1158,13 +1213,11 @@ func PokerUnSitHandler(c echo.Context) error {
return c.NoContent(http.StatusNotFound)
}
if c.Request().Method == http.MethodPost {
- if err := g.UnSitPlayer(db, roomID, authUser.Username.String()); err == nil {
+ if err := g.UnSitPlayer(db, roomID, authUser, pokerTable); err == nil {
PokerPubSub.Pub(roomTopic, PokerSeatLeftEvent{})
newLogEvent(g, roomLogsTopic, fmt.Sprintf("%s un-sit", authUser.Username.String()))
}
}
- html := hutils.HtmlCssReset
- html += `<form method="post"><button>UnSit</button></form>`
return c.HTML(http.StatusOK, html)
}
@@ -1178,14 +1231,32 @@ func PokerSitHandler(c echo.Context) error {
}
pos--
roomID := c.Param("roomID")
+ pokerTable, err := db.GetPokerTableBySlug(roomID)
+ if err != nil {
+ return c.HTML(http.StatusOK, html)
+ }
roomTopic := "room_" + roomID
roomLogsTopic := "room_" + roomID + "_logs"
g := PokerInstance.GetGame(roomID)
if g == nil {
return c.HTML(http.StatusOK, html)
}
+
if c.Request().Method == http.MethodPost {
- if err := g.SitPlayer(db, authUser, pos); err != nil {
+ tableAccount, err := db.GetPokerTableAccount(authUser.ID, pokerTable.ID)
+ if err != nil {
+ logrus.Error(err)
+ return c.HTML(http.StatusOK, html)
+ }
+ if int64(authUser.ChipsTest)+tableAccount.Amount < pokerTable.MinBuyIn {
+ return c.HTML(http.StatusOK, html)
+ }
+ needed := pokerTable.MinBuyIn - tableAccount.Amount
+ authUser.ChipsTest -= int(needed)
+ authUser.DoSave(db)
+ tableAccount.Amount += needed
+ tableAccount.DoSave(db)
+ if err := g.SitPlayer(authUser, pos, tableAccount.Amount); err != nil {
fmt.Println(err)
return c.HTML(http.StatusOK, html)
}
@@ -1788,6 +1859,12 @@ body {
func PokerLogsHandler(c echo.Context) error {
roomID := c.Param("roomID")
+ db := c.Get("database").(*database.DkfDB)
+
+ if _, err := db.GetPokerTableBySlug(roomID); err != nil {
+ return c.Redirect(http.StatusFound, "/")
+ }
+
send := func(s string) { _, _ = c.Response().Write([]byte(s)) }
g := PokerInstance.GetOrCreateGame(roomID)
roomLogsTopic := "room_" + roomID + "_logs"
@@ -1835,6 +1912,12 @@ Loop:
func PokerHandler(c echo.Context) error {
roomID := c.Param("roomID")
authUser := c.Get("authUser").(*database.User)
+ db := c.Get("database").(*database.DkfDB)
+
+ if _, err := db.GetPokerTableBySlug(roomID); err != nil {
+ return c.Redirect(http.StatusFound, "/")
+ }
+
roomTopic := "room_" + roomID
roomUserTopic := "room_" + roomID + "_" + authUser.Username.String()
send := func(s string) { _, _ = c.Response().Write([]byte(s)) }