Implement country leaderboards
This commit is contained in:
parent
120c027fce
commit
622658f5aa
|
@ -4,14 +4,21 @@ package peppy
|
|||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/thehowl/go-osuapi"
|
||||
"github.com/valyala/fasthttp"
|
||||
"gopkg.in/redis.v5"
|
||||
"zxq.co/ripple/ocl"
|
||||
"zxq.co/ripple/rippleapi/common"
|
||||
)
|
||||
|
||||
// R is a redis client.
|
||||
var R *redis.Client
|
||||
|
||||
// GetUser retrieves general user information.
|
||||
func GetUser(c *fasthttp.RequestCtx, db *sqlx.DB) {
|
||||
if query(c, "u") == "" {
|
||||
|
@ -24,23 +31,21 @@ func GetUser(c *fasthttp.RequestCtx, db *sqlx.DB) {
|
|||
|
||||
mode := genmode(query(c, "m"))
|
||||
|
||||
var lbpos *int
|
||||
err := db.QueryRow(fmt.Sprintf(
|
||||
`SELECT
|
||||
users.id, users.username,
|
||||
users_stats.playcount_%s, users_stats.ranked_score_%s, users_stats.total_score_%s,
|
||||
leaderboard_%s.position, users_stats.pp_%s, users_stats.avg_accuracy_%s,
|
||||
users_stats.pp_%s, users_stats.avg_accuracy_%s,
|
||||
users_stats.country
|
||||
FROM users
|
||||
LEFT JOIN users_stats ON users_stats.id = users.id
|
||||
INNER JOIN leaderboard_%s ON leaderboard_%s.user = users.id
|
||||
%s
|
||||
LIMIT 1`,
|
||||
mode, mode, mode, mode, mode, mode, mode, mode, whereClause,
|
||||
mode, mode, mode, mode, mode, whereClause,
|
||||
), p).Scan(
|
||||
&user.UserID, &user.Username,
|
||||
&user.Playcount, &user.RankedScore, &user.TotalScore,
|
||||
&lbpos, &user.PP, &user.Accuracy,
|
||||
&user.PP, &user.Accuracy,
|
||||
&user.Country,
|
||||
)
|
||||
if err != nil {
|
||||
|
@ -50,9 +55,9 @@ func GetUser(c *fasthttp.RequestCtx, db *sqlx.DB) {
|
|||
}
|
||||
return
|
||||
}
|
||||
if lbpos != nil {
|
||||
user.Rank = *lbpos
|
||||
}
|
||||
|
||||
user.Rank = int(R.ZRevRank("ripple:leaderboard:"+mode, strconv.Itoa(user.UserID)).Val()) + 1
|
||||
user.CountryRank = int(R.ZRevRank("ripple:leaderboard:"+mode+":"+strings.ToLower(user.Country), strconv.Itoa(user.UserID)).Val()) + 1
|
||||
user.Level = ocl.GetLevelPrecise(user.TotalScore)
|
||||
|
||||
json(c, 200, []osuapi.User{user})
|
||||
|
|
|
@ -62,6 +62,7 @@ func Start(conf common.Conf, dbO *sqlx.DB) *fhr.Router {
|
|||
Password: conf.RedisPassword,
|
||||
DB: conf.RedisDB,
|
||||
})
|
||||
peppy.R = red
|
||||
|
||||
// token updater
|
||||
go tokenUpdater(db)
|
||||
|
|
|
@ -2,6 +2,12 @@ package v1
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
redis "gopkg.in/redis.v5"
|
||||
|
||||
"zxq.co/ripple/ocl"
|
||||
"zxq.co/ripple/rippleapi/common"
|
||||
|
@ -28,29 +34,48 @@ SELECT
|
|||
|
||||
users_stats.ranked_score_%[1]s, users_stats.total_score_%[1]s, users_stats.playcount_%[1]s,
|
||||
users_stats.replays_watched_%[1]s, users_stats.total_hits_%[1]s,
|
||||
users_stats.avg_accuracy_%[1]s, users_stats.pp_%[1]s, leaderboard_%[1]s.position as %[1]s_position
|
||||
FROM leaderboard_%[1]s
|
||||
INNER JOIN users ON users.id = leaderboard_%[1]s.user
|
||||
INNER JOIN users_stats ON users_stats.id = leaderboard_%[1]s.user
|
||||
%[2]s`
|
||||
users_stats.avg_accuracy_%[1]s, users_stats.pp_%[1]s
|
||||
FROM users
|
||||
INNER JOIN users_stats ON users_stats.id = users.id
|
||||
WHERE users.id IN (?)
|
||||
`
|
||||
|
||||
// LeaderboardGET gets the leaderboard.
|
||||
func LeaderboardGET(md common.MethodData) common.CodeMessager {
|
||||
m := getMode(md.Query("mode"))
|
||||
w := &common.WhereClause{
|
||||
Clause: "WHERE " + md.User.OnlyUserPublic(md.HasQuery("see_everything")),
|
||||
|
||||
// md.Query.Country
|
||||
p := common.Int(md.Query("p")) - 1
|
||||
if p < 0 {
|
||||
p = 0
|
||||
}
|
||||
w.Where("users_stats.country = ?", md.Query("country"))
|
||||
// Admins may not want to see banned users on the leaderboard.
|
||||
// This is the default setting. In case they do, they have to activate see_everything.
|
||||
query := fmt.Sprintf(lbUserQuery, m, w.Clause+
|
||||
` ORDER BY leaderboard_`+m+`.position `+common.Paginate(md.Query("p"), md.Query("l"), 500))
|
||||
rows, err := md.DB.Query(query, w.Params...)
|
||||
l := common.InString(1, md.Query("l"), 500, 50)
|
||||
|
||||
key := "ripple:leaderboard:" + m
|
||||
if md.Query("country") != "" {
|
||||
key += ":" + md.Query("country")
|
||||
}
|
||||
|
||||
results, err := md.R.ZRevRange(key, int64(p*l), int64(p*l+l-1)).Result()
|
||||
if err != nil {
|
||||
md.Err(err)
|
||||
return Err500
|
||||
}
|
||||
|
||||
var resp leaderboardResponse
|
||||
resp.Code = 200
|
||||
|
||||
if len(results) == 0 {
|
||||
return resp
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(lbUserQuery+` ORDER BY users_stats.pp_%[1]s DESC, users_stats.ranked_score_%[1]s DESC`, m)
|
||||
query, params, _ := sqlx.In(query, results)
|
||||
rows, err := md.DB.Query(query, params...)
|
||||
if err != nil {
|
||||
md.Err(err)
|
||||
return Err500
|
||||
}
|
||||
for rows.Next() {
|
||||
var u leaderboardUser
|
||||
err := rows.Scan(
|
||||
|
@ -60,15 +85,28 @@ func LeaderboardGET(md common.MethodData) common.CodeMessager {
|
|||
|
||||
&u.ChosenMode.RankedScore, &u.ChosenMode.TotalScore, &u.ChosenMode.PlayCount,
|
||||
&u.ChosenMode.ReplaysWatched, &u.ChosenMode.TotalHits,
|
||||
&u.ChosenMode.Accuracy, &u.ChosenMode.PP, &u.ChosenMode.GlobalLeaderboardRank,
|
||||
&u.ChosenMode.Accuracy, &u.ChosenMode.PP,
|
||||
)
|
||||
if err != nil {
|
||||
md.Err(err)
|
||||
continue
|
||||
}
|
||||
u.ChosenMode.Level = ocl.GetLevelPrecise(int64(u.ChosenMode.TotalScore))
|
||||
if i := leaderboardPosition(md.R, m, u.ID); i != 0 {
|
||||
u.ChosenMode.GlobalLeaderboardRank = &i
|
||||
}
|
||||
if i := countryPosition(md.R, m, u.ID, u.Country); i != 0 {
|
||||
u.ChosenMode.CountryLeaderboardRank = &i
|
||||
}
|
||||
resp.Users = append(resp.Users, u)
|
||||
}
|
||||
resp.Code = 200
|
||||
return resp
|
||||
}
|
||||
|
||||
func leaderboardPosition(r *redis.Client, mode string, user int) int {
|
||||
return int(r.ZRevRank("ripple:leaderboard:"+mode, strconv.Itoa(user)).Val()) + 1
|
||||
}
|
||||
|
||||
func countryPosition(r *redis.Client, mode string, user int, country string) int {
|
||||
return int(r.ZRevRank("ripple:leaderboard:"+mode+":"+strings.ToLower(country), strconv.Itoa(user)).Val()) + 1
|
||||
}
|
||||
|
|
|
@ -166,16 +166,24 @@ func UserWhatsTheIDGET(md common.MethodData) common.CodeMessager {
|
|||
return r
|
||||
}
|
||||
|
||||
var modesToReadable = [...]string{
|
||||
"std",
|
||||
"taiko",
|
||||
"ctb",
|
||||
"mania",
|
||||
}
|
||||
|
||||
type modeData struct {
|
||||
RankedScore uint64 `json:"ranked_score"`
|
||||
TotalScore uint64 `json:"total_score"`
|
||||
PlayCount int `json:"playcount"`
|
||||
ReplaysWatched int `json:"replays_watched"`
|
||||
TotalHits int `json:"total_hits"`
|
||||
Level float64 `json:"level"`
|
||||
Accuracy float64 `json:"accuracy"`
|
||||
PP int `json:"pp"`
|
||||
GlobalLeaderboardRank *int `json:"global_leaderboard_rank"`
|
||||
RankedScore uint64 `json:"ranked_score"`
|
||||
TotalScore uint64 `json:"total_score"`
|
||||
PlayCount int `json:"playcount"`
|
||||
ReplaysWatched int `json:"replays_watched"`
|
||||
TotalHits int `json:"total_hits"`
|
||||
Level float64 `json:"level"`
|
||||
Accuracy float64 `json:"accuracy"`
|
||||
PP int `json:"pp"`
|
||||
GlobalLeaderboardRank *int `json:"global_leaderboard_rank"`
|
||||
CountryLeaderboardRank *int `json:"country_leaderboard_rank"`
|
||||
}
|
||||
type userFullResponse struct {
|
||||
common.ResponseBase
|
||||
|
@ -214,33 +222,25 @@ SELECT
|
|||
|
||||
users_stats.ranked_score_std, users_stats.total_score_std, users_stats.playcount_std,
|
||||
users_stats.replays_watched_std, users_stats.total_hits_std,
|
||||
users_stats.avg_accuracy_std, users_stats.pp_std, leaderboard_std.position as std_position,
|
||||
users_stats.avg_accuracy_std, users_stats.pp_std,
|
||||
|
||||
users_stats.ranked_score_taiko, users_stats.total_score_taiko, users_stats.playcount_taiko,
|
||||
users_stats.replays_watched_taiko, users_stats.total_hits_taiko,
|
||||
users_stats.avg_accuracy_taiko, users_stats.pp_taiko, leaderboard_taiko.position as taiko_position,
|
||||
users_stats.avg_accuracy_taiko, users_stats.pp_taiko,
|
||||
|
||||
users_stats.ranked_score_ctb, users_stats.total_score_ctb, users_stats.playcount_ctb,
|
||||
users_stats.replays_watched_ctb, users_stats.total_hits_ctb,
|
||||
users_stats.avg_accuracy_ctb, users_stats.pp_ctb, leaderboard_ctb.position as ctb_position,
|
||||
users_stats.avg_accuracy_ctb, users_stats.pp_ctb,
|
||||
|
||||
users_stats.ranked_score_mania, users_stats.total_score_mania, users_stats.playcount_mania,
|
||||
users_stats.replays_watched_mania, users_stats.total_hits_mania,
|
||||
users_stats.avg_accuracy_mania, users_stats.pp_mania, leaderboard_mania.position as mania_position,
|
||||
users_stats.avg_accuracy_mania, users_stats.pp_mania,
|
||||
|
||||
users.silence_reason, users.silence_end
|
||||
|
||||
FROM users
|
||||
LEFT JOIN users_stats
|
||||
ON users.id=users_stats.id
|
||||
LEFT JOIN leaderboard_std
|
||||
ON users.id=leaderboard_std.user
|
||||
LEFT JOIN leaderboard_taiko
|
||||
ON users.id=leaderboard_taiko.user
|
||||
LEFT JOIN leaderboard_ctb
|
||||
ON users.id=leaderboard_ctb.user
|
||||
LEFT JOIN leaderboard_mania
|
||||
ON users.id=leaderboard_mania.user
|
||||
WHERE ` + whereClause + ` AND ` + md.User.OnlyUserPublic(true) + `
|
||||
LIMIT 1
|
||||
`
|
||||
|
@ -261,19 +261,19 @@ LIMIT 1
|
|||
|
||||
&r.STD.RankedScore, &r.STD.TotalScore, &r.STD.PlayCount,
|
||||
&r.STD.ReplaysWatched, &r.STD.TotalHits,
|
||||
&r.STD.Accuracy, &r.STD.PP, &r.STD.GlobalLeaderboardRank,
|
||||
&r.STD.Accuracy, &r.STD.PP,
|
||||
|
||||
&r.Taiko.RankedScore, &r.Taiko.TotalScore, &r.Taiko.PlayCount,
|
||||
&r.Taiko.ReplaysWatched, &r.Taiko.TotalHits,
|
||||
&r.Taiko.Accuracy, &r.Taiko.PP, &r.Taiko.GlobalLeaderboardRank,
|
||||
&r.Taiko.Accuracy, &r.Taiko.PP,
|
||||
|
||||
&r.CTB.RankedScore, &r.CTB.TotalScore, &r.CTB.PlayCount,
|
||||
&r.CTB.ReplaysWatched, &r.CTB.TotalHits,
|
||||
&r.CTB.Accuracy, &r.CTB.PP, &r.CTB.GlobalLeaderboardRank,
|
||||
&r.CTB.Accuracy, &r.CTB.PP,
|
||||
|
||||
&r.Mania.RankedScore, &r.Mania.TotalScore, &r.Mania.PlayCount,
|
||||
&r.Mania.ReplaysWatched, &r.Mania.TotalHits,
|
||||
&r.Mania.Accuracy, &r.Mania.PP, &r.Mania.GlobalLeaderboardRank,
|
||||
&r.Mania.Accuracy, &r.Mania.PP,
|
||||
|
||||
&r.SilenceInfo.Reason, &r.SilenceInfo.End,
|
||||
)
|
||||
|
@ -290,8 +290,15 @@ LIMIT 1
|
|||
r.CustomBadge = &b
|
||||
}
|
||||
|
||||
for _, m := range []*modeData{&r.STD, &r.Taiko, &r.CTB, &r.Mania} {
|
||||
for modeID, m := range [...]*modeData{&r.STD, &r.Taiko, &r.CTB, &r.Mania} {
|
||||
m.Level = ocl.GetLevelPrecise(int64(m.TotalScore))
|
||||
|
||||
if i := leaderboardPosition(md.R, modesToReadable[modeID], r.ID); i != 0 {
|
||||
m.GlobalLeaderboardRank = &i
|
||||
}
|
||||
if i := countryPosition(md.R, modesToReadable[modeID], r.ID, r.Country); i != 0 {
|
||||
m.CountryLeaderboardRank = &i
|
||||
}
|
||||
}
|
||||
|
||||
rows, err := md.DB.Query("SELECT b.id, b.name, b.icon FROM user_badges ub "+
|
||||
|
|
Loading…
Reference in New Issue
Block a user