Skip to content

Commit

Permalink
Saving balance in redis
Browse files Browse the repository at this point in the history
  • Loading branch information
atul24112001 committed Nov 9, 2024
1 parent 140ebbb commit 4f7b04a
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 68 deletions.
5 changes: 5 additions & 0 deletions server/auth/register.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package auth

import (
gameManager "flappy-bird-server/game-manager"
"flappy-bird-server/lib"
"flappy-bird-server/model"
"fmt"
"net/http"
"time"

"github.com/google/uuid"
"github.com/jackc/pgx/v5"
Expand Down Expand Up @@ -76,6 +79,8 @@ func authenticate(w http.ResponseWriter, r *http.Request) {
return
}

gameManager.GetInstance().RedisClient.Set(r.Context(), fmt.Sprintf("mr-balance-%s", user.Id), user.SolanaBalance, 24*time.Hour)

lib.WriteJson(w, http.StatusOK, map[string]interface{}{
"message": "Login successfully",
"token": token,
Expand Down
110 changes: 65 additions & 45 deletions server/game-manager/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"log"
"os"
"strconv"
"sync"
"time"

Expand Down Expand Up @@ -36,6 +37,7 @@ type GameManager struct {
StartedGames map[string]Game
DbQueue Queue
GameQueue Queue
RedisClient *redis.Client
}

var instance *GameManager
Expand All @@ -45,16 +47,15 @@ func GetInstance() *GameManager {
once.Do(func() {
redisAddr := os.Getenv("REDIS_URL")
opt, _ := redis.ParseURL(redisAddr)
dbClient := redis.NewClient(opt)
gameClient := redis.NewClient(opt)
client := redis.NewClient(opt)
dbQueue := Queue{
client: dbClient,
client: client,
queueName: "mari-arena-db-queue",
processingKey: "mari-arena-db-queue:processing",
timeout: 10 * time.Second,
}
gameQueue := Queue{
client: gameClient,
client: client,
queueName: "mari-arena-queue",
processingKey: "mari-arena-queue:processing",
timeout: 10 * time.Second,
Expand All @@ -65,6 +66,7 @@ func GetInstance() *GameManager {
StartedGames: map[string]Game{},
DbQueue: dbQueue,
GameQueue: gameQueue,
RedisClient: client,
}
ctx := context.Background()

Expand All @@ -81,27 +83,25 @@ func GetInstance() *GameManager {
return instance
}

func (gameManger *GameManager) GetGame(gameId string) *Game {
targetGame := gameManger.StartedGames[gameId]
func (gameManager *GameManager) GetGame(gameId string) *Game {
targetGame := gameManager.StartedGames[gameId]
return &targetGame
}

func (gameManger *GameManager) GetUser(userId string) (*User, bool) {
targetUser, exist := gameManger.Users[userId]
func (gameManager *GameManager) GetUser(userId string) (*User, bool) {
targetUser, exist := gameManager.Users[userId]
return &targetUser, exist
}

func (gameManger *GameManager) AddUser(userId string, publicKey string, ws *websocket.Conn) {
log.Println("Adding user")
gameManger.Users[userId] = User{
func (gameManager *GameManager) AddUser(userId string, publicKey string, ws *websocket.Conn) {
gameManager.Users[userId] = User{
Id: userId,
Ws: ws,
PublicKey: publicKey,
}
log.Println("Added user")
}

func (gameManger *GameManager) CreateGame(maxUserCount int, winnerPrice int, entry int, gameTypeId string) (*Game, error) {
func (gameManager *GameManager) CreateGame(maxUserCount int, winnerPrice int, entry int, gameTypeId string) (*Game, error) {
newGameId, err := uuid.NewUUID()
if err != nil {
log.Println(err.Error())
Expand Down Expand Up @@ -129,7 +129,7 @@ func (gameManger *GameManager) CreateGame(maxUserCount int, winnerPrice int, ent
},
}

err = gameManger.DbQueue.Enqueue(context.Background(), item)
err = gameManager.DbQueue.Enqueue(context.Background(), item)

if err != nil {
log.Println(err.Error())
Expand All @@ -139,12 +139,29 @@ func (gameManger *GameManager) CreateGame(maxUserCount int, winnerPrice int, ent
return &newGame, nil
}

func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
targetUser, exist := gameManger.GetUser(userId)
func (gameManager *GameManager) GetBalance(userId string) (int, error) {
red := gameManager.RedisClient.Get(context.Background(), fmt.Sprintf("mr-balance-%s", userId))
err := red.Err()
balance := 0
if err == nil {
balance, err = strconv.Atoi(red.Val())
} else {
err = lib.Pool.QueryRow(context.Background(), `SELECT "solanaBalance" FROM public.users WHERE id = $1`, userId).Scan(&balance)
}
return balance, err
}

func (gameManager *GameManager) SetBalance(userId string, amount int) error {
red := gameManager.RedisClient.Set(context.Background(), fmt.Sprintf("mr-balance-%s", userId), amount, 24*time.Hour)
return red.Err()
}

func (gameManager *GameManager) JoinGame(userId string, gameTypeId string) {
targetUser, exist := gameManager.GetUser(userId)
if !exist {
return
}
newGameMap, newGameMapExist := gameManger.NewGame[gameTypeId]
newGameMap, newGameMapExist := gameManager.NewGame[gameTypeId]
if !newGameMapExist {
cacheGameTypeMap, cacheGameTypeMapExist := gameTypeMap[gameTypeId]

Expand All @@ -165,9 +182,8 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
cacheGameTypeMap = gameTypeMap[gameTypeId]
}

_newGame, err := gameManger.CreateGame(cacheGameTypeMap.GameType.MaxPlayer, cacheGameTypeMap.GameType.Winner, cacheGameTypeMap.GameType.Entry, cacheGameTypeMap.GameType.Id)
_newGame, err := gameManager.CreateGame(cacheGameTypeMap.GameType.MaxPlayer, cacheGameTypeMap.GameType.Winner, cacheGameTypeMap.GameType.Entry, cacheGameTypeMap.GameType.Id)
if err != nil {
log.Println("120", err.Error())
targetUser.SendMessage("error", map[string]interface{}{
"message": "Error while creating new game",
})
Expand All @@ -178,7 +194,7 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
Game: *_newGame,
GameType: cacheGameTypeMap.GameType,
}
gameManger.NewGame[gameTypeId] = newGameMap
gameManager.NewGame[gameTypeId] = newGameMap
log.Println("Game Created", gameTypeId, newGameMap)
}

Expand All @@ -189,27 +205,26 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
return
}

var currentBalance int
err := lib.Pool.QueryRow(context.Background(), `SELECT "solanaBalance" FROM public.users WHERE id = $1`, userId).Scan(&currentBalance)
currentBalance, err := gameManager.GetBalance(userId)

if err != nil {
log.Println("151", err.Error())
targetUser.SendMessage("error", map[string]interface{}{
"message": "Something went wrong while fetching current balance",
})
return

}

if currentBalance < newGame.Entry {
log.Println("159", err.Error())
targetUser.SendMessage("error", map[string]interface{}{
"message": "Insufficient balance",
})
return
}

if !newGame.Users[userId] {
err = gameManger.DbQueue.Enqueue(context.Background(), map[string]interface{}{
err := gameManager.DbQueue.Enqueue(context.Background(), map[string]interface{}{
"type": "add-participant",
"data": map[string]interface{}{
"userId": userId,
Expand All @@ -227,7 +242,7 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {

keys := make([]string, 0, newGame.MaxUserCount)
for k := range newGame.Users {
participants, exist := gameManger.GetUser(k)
participants, exist := gameManager.GetUser(k)
if exist {
keys = append(keys, k)
participants.SendMessage("new-user", map[string]interface{}{
Expand All @@ -250,34 +265,36 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
}

newGameMap.Game = newGame
gameManger.NewGame[gameTypeId] = newGameMap
gameManager.NewGame[gameTypeId] = newGameMap

log.Printf("Game joined userId %s - gameId %s", userId, newGame.Id)
log.Printf("current: %d - max: %d", newGame.CurrentUserCount, newGame.MaxUserCount)
if newGame.CurrentUserCount == newGame.MaxUserCount {
newGame.Status = "ongoing"
gameManger.StartedGames[newGame.Id] = newGame
delete(gameManger.NewGame, gameTypeId)
gameManager.StartedGames[newGame.Id] = newGame
delete(gameManager.NewGame, gameTypeId)
keys = append(keys, userId)

ids := ""
for _, id := range keys {
balance, err := gameManager.GetBalance(id)
if err != nil {
gameManager.SetBalance(id, balance-newGame.Entry)
}
if ids == "" {
ids += fmt.Sprintf(`'%s'`, id)
} else {
ids += fmt.Sprintf(`, '%s'`, id)
}
}

err = gameManger.DbQueue.Enqueue(context.Background(), map[string]interface{}{
err = gameManager.DbQueue.Enqueue(context.Background(), map[string]interface{}{
"type": "start-game",
"data": map[string]interface{}{
"gameId": newGame.Id,
},
})
if err != nil {
for _, id := range keys {
participant, exist := gameManger.GetUser(id)
participant, exist := gameManager.GetUser(id)
if exist {
log.Println(err.Error())
participant.SendMessage("error", map[string]interface{}{
Expand All @@ -287,7 +304,7 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
}
}

err := gameManger.DbQueue.Enqueue(context.Background(), map[string]interface{}{
err := gameManager.DbQueue.Enqueue(context.Background(), map[string]interface{}{
"type": "collect-entry",
"data": map[string]interface{}{
"ids": ids,
Expand All @@ -296,15 +313,15 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
})

for _, id := range keys {
participant, exist := gameManger.GetUser(id)
participant, exist := gameManager.GetUser(id)
if exist {
if err != nil {
log.Println(err.Error())
participant.SendMessage("error", map[string]interface{}{
"message": "Something went wrong while collecting entry fees",
})
} else {
gameManger.Users[id] = User{
gameManager.Users[id] = User{
Id: participant.Id,
CurrentGameId: newGame.Id,
PublicKey: participant.PublicKey,
Expand All @@ -317,10 +334,10 @@ func (gameManger *GameManager) JoinGame(userId string, gameTypeId string) {
}
}

func (gameManger *GameManager) DeleteUser(conn *websocket.Conn, targetUserId string) {
func (gameManager *GameManager) DeleteUser(conn *websocket.Conn, targetUserId string) {
if targetUserId != "" {
targetUser, userExist := gameManger.GetUser(targetUserId)
for gameId, g := range gameManger.StartedGames {
targetUser, userExist := gameManager.GetUser(targetUserId)
for gameId, g := range gameManager.StartedGames {
_, existInGame := g.Users[targetUserId]
if existInGame && userExist {
log.Println("User exist in game", gameId, "currentGameId: ", targetUser.CurrentGameId, " end")
Expand All @@ -334,7 +351,7 @@ func (gameManger *GameManager) DeleteUser(conn *websocket.Conn, targetUserId str
inGameUsersScoreboard[key] = g.ScoreBoard[key]
}
}
gameManger.StartedGames[gameId] = Game{
gameManager.StartedGames[gameId] = Game{
Id: g.Id,
MaxUserCount: g.MaxUserCount,
CurrentUserCount: g.CurrentUserCount - 1,
Expand All @@ -345,16 +362,16 @@ func (gameManger *GameManager) DeleteUser(conn *websocket.Conn, targetUserId str
ScoreBoard: inGameUsersScoreboard,
}
} else {
gameManger.GameOver(gameId, targetUserId)
gameManager.GameOver(gameId, targetUserId)
}
}
}
}
delete(gameManger.Users, targetUserId)
delete(gameManager.Users, targetUserId)
}

func (gameManger *GameManager) DeleteGame(gameId string) {
delete(gameManger.StartedGames, gameId)
func (gameManager *GameManager) DeleteGame(gameId string) {
delete(gameManager.StartedGames, gameId)
}

func (gameManager *GameManager) UpdateBoard(gameId string, userId string) {
Expand Down Expand Up @@ -425,8 +442,7 @@ func (gameManager *GameManager) GameOver(gameId string, userId string) {
}
}

for k, _ := range targetGame.Users {
log.Println("370", k)
for k := range targetGame.Users {
participant, exist := gameManager.GetUser(k)
if exist {
gameManager.Users[k] = User{
Expand All @@ -436,6 +452,10 @@ func (gameManager *GameManager) GameOver(gameId string, userId string) {
Ws: participant.Ws,
}
if k == winnerId {
balance, err := gameManager.GetBalance(winnerId)
if err != nil {
gameManager.SetBalance(winnerId, balance+targetGame.WinnerPrice)
}
participant.SendMessage("winner", map[string]interface{}{
"amount": targetGame.WinnerPrice - targetGame.Entry,
})
Expand Down
5 changes: 5 additions & 0 deletions server/user/verify-user.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package user

import (
gameManager "flappy-bird-server/game-manager"
"flappy-bird-server/lib"
"flappy-bird-server/middleware"
"fmt"
"net/http"
"time"
)

type RequestBody struct {
Expand All @@ -23,6 +26,8 @@ func verifyUser(w http.ResponseWriter, r *http.Request) {
"data": []middleware.User{user},
}

gameManager.GetInstance().RedisClient.Set(r.Context(), fmt.Sprintf("mr-balance-%s", user.Id), user.SolanaBalance, 24*time.Hour)

if user.Email == lib.AdminPublicKey {
response["isAdmin"] = true
}
Expand Down
17 changes: 12 additions & 5 deletions web/src/app/[gameId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@ import React from "react";
import GameClient from "@/sections/game";
import { redirect } from "next/navigation";
import { GameType } from "@prisma/client";
import { customCache } from "../page";

export default async function Game({ params }: ServerProps) {
const { gameId } = await Promise.resolve(params);

const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/game-types`
);
const { data } = await response.json();
let gameTypes: GameType[] = customCache.gameTypes;
if (customCache.lastUpdated < new Date().getTime()) {
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/game-types`
);
const data = await response.json();
customCache.lastUpdated = new Date().getTime() + 3600000;
customCache.gameTypes = data.data;
gameTypes = data.data;
}

const targetGameType = data.find(
const targetGameType = gameTypes.find(
(gameType: GameType) => gameType.id === gameId
);

Expand Down
Loading

0 comments on commit 4f7b04a

Please sign in to comment.