Add leaderboard

This commit is contained in:
Trevor Slocum 2023-12-29 23:28:29 -08:00
parent 86cc2df9cd
commit 33a850b495
4 changed files with 153 additions and 0 deletions

View file

@ -592,6 +592,57 @@ func matchHistory(username string) ([]*bgammon.HistoryMatch, error) {
return matches, nil
}
func getLeaderboard(matchType int, multiPoint bool) (*leaderboardResult, error) {
dbLock.Lock()
defer dbLock.Unlock()
tx, err := begin()
if err != nil {
return nil, err
}
defer tx.Commit(context.Background())
var columnName string
switch matchType {
case matchTypeCasual:
if !multiPoint {
columnName = "casualsingle"
} else {
columnName = "casualmulti"
}
case matchTypeRated:
if !multiPoint {
columnName = "ratedsingle"
} else {
columnName = "ratedmulti"
}
default:
log.Panicf("unknown match type: %d", matchType)
}
result := &leaderboardResult{}
rows, err := tx.Query(context.Background(), "SELECT username, "+columnName+" FROM account ORDER BY "+columnName+" DESC LIMIT 100")
if err != nil {
return nil, err
}
for rows.Next() {
if err != nil {
continue
}
entry := &leaderboardEntry{}
err = rows.Scan(&entry.User, &entry.Rating)
if err != nil {
continue
}
entry.Rating /= 100
result.Leaderboard = append(result.Leaderboard, entry)
}
if err != nil {
return nil, err
}
return result, nil
}
func dailyStats(tz *time.Location) (*serverStatsResult, error) {
dbLock.Lock()
defer dbLock.Unlock()

View file

@ -5,6 +5,15 @@ const (
matchTypeRated
)
type leaderboardEntry struct {
User string
Rating int
}
type leaderboardResult struct {
Leaderboard []*leaderboardEntry
}
type serverStatsEntry struct {
Date string
Games int

View file

@ -63,6 +63,10 @@ func matchHistory(username string) ([]*bgammon.HistoryMatch, error) {
return nil, nil
}
func getLeaderboard(matchType int, multiPoint bool) (*leaderboardResult, error) {
return nil, nil
}
func dailyStats(tz *time.Location) (*serverStatsResult, error) {
return &serverStatsResult{}, nil
}

View file

@ -50,6 +50,10 @@ type server struct {
gamesCacheTime time.Time
gamesCacheLock sync.Mutex
leaderboardCache [4][]byte
leaderboardCacheTime time.Time
leaderboardCacheLock sync.Mutex
mailServer string
passwordSalt string
resetSalt string
@ -142,6 +146,67 @@ func (s *server) cachedMatches() []byte {
return s.gamesCache
}
func (s *server) cachedLeaderboard(matchType int, multiPoint bool) []byte {
s.leaderboardCacheLock.Lock()
defer s.leaderboardCacheLock.Unlock()
var i int
switch matchType {
case matchTypeCasual:
if multiPoint {
i = 1
}
case matchTypeRated:
if !multiPoint {
i = 2
} else {
i = 3
}
}
if time.Since(s.leaderboardCacheTime) < 5*time.Minute {
return s.leaderboardCache[i]
}
s.leaderboardCacheTime = time.Now()
result, err := getLeaderboard(matchTypeCasual, false)
if err != nil {
log.Fatalf("failed to get leaderboard: %s", err)
}
s.leaderboardCache[0], err = json.Marshal(result)
if err != nil {
log.Fatalf("failed to marshal %+v: %s", result, err)
}
result, err = getLeaderboard(matchTypeCasual, true)
if err != nil {
log.Fatalf("failed to get leaderboard: %s", err)
}
s.leaderboardCache[1], err = json.Marshal(result)
if err != nil {
log.Fatalf("failed to marshal %+v: %s", result, err)
}
result, err = getLeaderboard(matchTypeRated, false)
if err != nil {
log.Fatalf("failed to get leaderboard: %s", err)
}
s.leaderboardCache[2], err = json.Marshal(result)
if err != nil {
log.Fatalf("failed to marshal %+v: %s", result, err)
}
result, err = getLeaderboard(matchTypeRated, true)
if err != nil {
log.Fatalf("failed to get leaderboard: %s", err)
}
s.leaderboardCache[3], err = json.Marshal(result)
if err != nil {
log.Fatalf("failed to marshal %+v: %s", result, err)
}
return s.leaderboardCache[i]
}
func (s *server) handleResetPassword(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, err := strconv.Atoi(vars["id"])
@ -186,6 +251,26 @@ func (s *server) handleListMatches(w http.ResponseWriter, r *http.Request) {
w.Write(s.cachedMatches())
}
func (s *server) handleLeaderboardCasualSingle(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(s.cachedLeaderboard(matchTypeCasual, false))
}
func (s *server) handleLeaderboardCasualMulti(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(s.cachedLeaderboard(matchTypeCasual, true))
}
func (s *server) handleLeaderboardRatedSingle(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(s.cachedLeaderboard(matchTypeRated, false))
}
func (s *server) handleLeaderboardRatedMulti(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write(s.cachedLeaderboard(matchTypeRated, true))
}
func (s *server) handlePrintDailyStats(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
@ -272,6 +357,10 @@ func (s *server) listenWebSocket(address string) {
m.HandleFunc("/reset/{id:[0-9]+}/{key:[A-Za-z0-9]+}", s.handleResetPassword)
m.HandleFunc("/match/{id:[0-9]+}", s.handleMatch)
m.HandleFunc("/matches", s.handleListMatches)
m.HandleFunc("/leaderboard-casual-single", s.handleLeaderboardCasualSingle)
m.HandleFunc("/leaderboard-casual-multi", s.handleLeaderboardCasualMulti)
m.HandleFunc("/leaderboard-rated-single", s.handleLeaderboardRatedSingle)
m.HandleFunc("/leaderboard-rated-multi", s.handleLeaderboardRatedMulti)
m.HandleFunc("/stats", s.handlePrintDailyStats)
m.HandleFunc("/stats-total", s.handlePrintCumulativeStats)
m.HandleFunc("/stats-tabula", s.handlePrintTabulaStats)