2016-06-14 07:37:11 +00:00
|
|
|
package v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2017-04-11 21:18:02 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
|
|
|
|
|
|
redis "gopkg.in/redis.v5"
|
2016-06-14 07:37:11 +00:00
|
|
|
|
2019-02-23 13:14:37 +00:00
|
|
|
"zxq.co/ripple/ocl"
|
2019-02-23 13:11:47 +00:00
|
|
|
"github.com/osuYozora/api/common"
|
2016-06-14 07:37:11 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type leaderboardUser struct {
|
|
|
|
userData
|
|
|
|
ChosenMode modeData `json:"chosen_mode"`
|
|
|
|
PlayStyle int `json:"play_style"`
|
|
|
|
FavouriteMode int `json:"favourite_mode"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type leaderboardResponse struct {
|
|
|
|
common.ResponseBase
|
|
|
|
Users []leaderboardUser `json:"users"`
|
|
|
|
}
|
|
|
|
|
|
|
|
const lbUserQuery = `
|
|
|
|
SELECT
|
2016-07-03 22:06:23 +00:00
|
|
|
users.id, users.username, users.register_datetime, users.privileges, users.latest_activity,
|
2016-07-03 19:55:03 +00:00
|
|
|
|
2016-07-06 21:43:43 +00:00
|
|
|
users_stats.username_aka, users_stats.country,
|
2016-06-14 07:37:11 +00:00
|
|
|
users_stats.play_style, users_stats.favourite_mode,
|
2016-07-03 19:55:03 +00:00
|
|
|
|
2016-06-14 07:37:11 +00:00
|
|
|
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,
|
2017-04-11 21:18:02 +00:00
|
|
|
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 (?)
|
|
|
|
`
|
2019-02-23 14:10:04 +00:00
|
|
|
const lbUserQueryRx = `
|
|
|
|
SELECT
|
|
|
|
users.id, users.username, users.register_datetime, users.privileges, users.latest_activity,
|
|
|
|
|
|
|
|
users_stats.username_aka, users_stats.country,
|
|
|
|
users_stats.play_style, users_stats.favourite_mode,
|
|
|
|
|
|
|
|
users_stats.ranked_score_%[1]s_rx, users_stats.total_score_%[1]s_rx, users_stats.playcount_%[1]s_rx,
|
2019-02-23 14:13:27 +00:00
|
|
|
users_stats.replays_watched_%[1]s, users_stats.total_hits_%[1]s,
|
2019-02-23 14:10:04 +00:00
|
|
|
users_stats.avg_accuracy_%[1]s_rx, users_stats.pp_%[1]s_rx
|
|
|
|
FROM users
|
|
|
|
INNER JOIN users_stats ON users_stats.id = users.id
|
|
|
|
WHERE users.id IN (?)
|
|
|
|
`
|
|
|
|
|
|
|
|
const lbUserQueryAp = `
|
|
|
|
SELECT
|
|
|
|
users.id, users.username, users.register_datetime, users.privileges, users.latest_activity,
|
|
|
|
|
|
|
|
users_stats.username_aka, users_stats.country,
|
|
|
|
users_stats.play_style, users_stats.favourite_mode,
|
|
|
|
|
|
|
|
users_stats.ranked_score_%[1]s_ap, users_stats.total_score_%[1]s_ap, users_stats.playcount_%[1]s_ap,
|
2019-02-23 14:13:27 +00:00
|
|
|
users_stats.replays_watched_%[1]s, users_stats.total_hits_%[1]s,
|
2019-02-23 14:10:04 +00:00
|
|
|
users_stats.avg_accuracy_%[1]s_ap, users_stats.pp_%[1]s_auto
|
|
|
|
FROM users
|
|
|
|
INNER JOIN users_stats ON users_stats.id = users.id
|
2019-02-23 14:22:28 +00:00
|
|
|
WHERE users.id IN (?) AND users.privileges >= 3
|
2019-02-23 14:10:04 +00:00
|
|
|
`
|
|
|
|
|
2016-06-14 07:37:11 +00:00
|
|
|
|
|
|
|
// LeaderboardGET gets the leaderboard.
|
|
|
|
func LeaderboardGET(md common.MethodData) common.CodeMessager {
|
2016-08-15 17:59:46 +00:00
|
|
|
m := getMode(md.Query("mode"))
|
2017-04-11 21:18:02 +00:00
|
|
|
|
|
|
|
// md.Query.Country
|
|
|
|
p := common.Int(md.Query("p")) - 1
|
|
|
|
if p < 0 {
|
|
|
|
p = 0
|
2016-09-28 21:00:24 +00:00
|
|
|
}
|
2017-04-11 21:18:02 +00:00
|
|
|
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()
|
2016-06-14 07:37:11 +00:00
|
|
|
if err != nil {
|
|
|
|
md.Err(err)
|
|
|
|
return Err500
|
|
|
|
}
|
2017-04-11 21:18:02 +00:00
|
|
|
|
2016-06-14 07:37:11 +00:00
|
|
|
var resp leaderboardResponse
|
2017-04-11 21:18:02 +00:00
|
|
|
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
|
|
|
|
}
|
2016-06-14 07:37:11 +00:00
|
|
|
for rows.Next() {
|
2016-07-06 21:43:43 +00:00
|
|
|
var u leaderboardUser
|
2016-06-14 07:37:11 +00:00
|
|
|
err := rows.Scan(
|
2016-07-06 20:32:30 +00:00
|
|
|
&u.ID, &u.Username, &u.RegisteredOn, &u.Privileges, &u.LatestActivity,
|
2016-06-14 07:37:11 +00:00
|
|
|
|
2016-07-06 21:43:43 +00:00
|
|
|
&u.UsernameAKA, &u.Country, &u.PlayStyle, &u.FavouriteMode,
|
2016-06-14 07:37:11 +00:00
|
|
|
|
|
|
|
&u.ChosenMode.RankedScore, &u.ChosenMode.TotalScore, &u.ChosenMode.PlayCount,
|
|
|
|
&u.ChosenMode.ReplaysWatched, &u.ChosenMode.TotalHits,
|
2017-04-11 21:18:02 +00:00
|
|
|
&u.ChosenMode.Accuracy, &u.ChosenMode.PP,
|
2016-06-14 07:37:11 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
md.Err(err)
|
|
|
|
continue
|
|
|
|
}
|
2016-09-28 18:49:58 +00:00
|
|
|
u.ChosenMode.Level = ocl.GetLevelPrecise(int64(u.ChosenMode.TotalScore))
|
2017-04-13 06:57:11 +00:00
|
|
|
if i := leaderboardPosition(md.R, m, u.ID); i != nil {
|
|
|
|
u.ChosenMode.GlobalLeaderboardRank = i
|
2017-04-11 21:18:02 +00:00
|
|
|
}
|
2017-04-13 06:57:11 +00:00
|
|
|
if i := countryPosition(md.R, m, u.ID, u.Country); i != nil {
|
|
|
|
u.ChosenMode.CountryLeaderboardRank = i
|
2017-04-11 21:18:02 +00:00
|
|
|
}
|
2016-06-14 07:37:11 +00:00
|
|
|
resp.Users = append(resp.Users, u)
|
|
|
|
}
|
|
|
|
return resp
|
|
|
|
}
|
2017-04-11 21:18:02 +00:00
|
|
|
|
2017-04-13 06:57:11 +00:00
|
|
|
func leaderboardPosition(r *redis.Client, mode string, user int) *int {
|
|
|
|
return _position(r, "ripple:leaderboard:"+mode, user)
|
2017-04-11 21:18:02 +00:00
|
|
|
}
|
|
|
|
|
2017-04-13 06:57:11 +00:00
|
|
|
func countryPosition(r *redis.Client, mode string, user int, country string) *int {
|
|
|
|
return _position(r, "ripple:leaderboard:"+mode+":"+strings.ToLower(country), user)
|
|
|
|
}
|
|
|
|
|
|
|
|
func _position(r *redis.Client, key string, user int) *int {
|
|
|
|
res := r.ZRevRank(key, strconv.Itoa(user))
|
|
|
|
if res.Err() == redis.Nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
x := int(res.Val()) + 1
|
|
|
|
return &x
|
2017-04-11 21:18:02 +00:00
|
|
|
}
|
2019-02-23 14:06:24 +00:00
|
|
|
|
|
|
|
// LeaderboardGET gets the leaderboard.
|
|
|
|
func LeaderboardRxGET(md common.MethodData) common.CodeMessager {
|
|
|
|
m := getMode(md.Query("mode"))
|
|
|
|
|
|
|
|
// md.Query.Country
|
|
|
|
p := common.Int(md.Query("p")) - 1
|
|
|
|
if p < 0 {
|
|
|
|
p = 0
|
|
|
|
}
|
|
|
|
l := common.InString(1, md.Query("l"), 500, 50)
|
|
|
|
|
|
|
|
key := "ripple:leaderboard_relax:" + 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
|
|
|
|
}
|
|
|
|
|
2019-02-23 14:10:04 +00:00
|
|
|
query := fmt.Sprintf(lbUserQueryRx+` ORDER BY users_stats.pp_%[1]s_rx DESC, users_stats.ranked_score_%[1]s_rx DESC`, m)
|
2019-02-23 14:06:24 +00:00
|
|
|
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(
|
|
|
|
&u.ID, &u.Username, &u.RegisteredOn, &u.Privileges, &u.LatestActivity,
|
|
|
|
|
|
|
|
&u.UsernameAKA, &u.Country, &u.PlayStyle, &u.FavouriteMode,
|
|
|
|
|
|
|
|
&u.ChosenMode.RankedScore, &u.ChosenMode.TotalScore, &u.ChosenMode.PlayCount,
|
|
|
|
&u.ChosenMode.ReplaysWatched, &u.ChosenMode.TotalHits,
|
|
|
|
&u.ChosenMode.Accuracy, &u.ChosenMode.PP,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
md.Err(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
u.ChosenMode.Level = ocl.GetLevelPrecise(int64(u.ChosenMode.TotalScore))
|
|
|
|
if i := leaderboardPositionRx(md.R, m, u.ID); i != nil {
|
|
|
|
u.ChosenMode.GlobalLeaderboardRank = i
|
|
|
|
}
|
|
|
|
if i := countryPositionRx(md.R, m, u.ID, u.Country); i != nil {
|
|
|
|
u.ChosenMode.CountryLeaderboardRank = i
|
|
|
|
}
|
|
|
|
resp.Users = append(resp.Users, u)
|
|
|
|
}
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
// LeaderboardGET gets the leaderboard.
|
|
|
|
func LeaderboardApGET(md common.MethodData) common.CodeMessager {
|
|
|
|
m := getMode(md.Query("mode"))
|
|
|
|
|
|
|
|
// md.Query.Country
|
|
|
|
p := common.Int(md.Query("p")) - 1
|
|
|
|
if p < 0 {
|
|
|
|
p = 0
|
|
|
|
}
|
|
|
|
l := common.InString(1, md.Query("l"), 500, 50)
|
|
|
|
|
|
|
|
key := "ripple:leaderboard_auto:" + 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
|
|
|
|
}
|
|
|
|
|
2019-02-23 14:10:04 +00:00
|
|
|
query := fmt.Sprintf(lbUserQueryAp+` ORDER BY users_stats.pp_%[1]s_auto DESC, users_stats.ranked_score_%[1]s_ap DESC`, m)
|
2019-02-23 14:06:24 +00:00
|
|
|
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(
|
|
|
|
&u.ID, &u.Username, &u.RegisteredOn, &u.Privileges, &u.LatestActivity,
|
|
|
|
|
|
|
|
&u.UsernameAKA, &u.Country, &u.PlayStyle, &u.FavouriteMode,
|
|
|
|
|
|
|
|
&u.ChosenMode.RankedScore, &u.ChosenMode.TotalScore, &u.ChosenMode.PlayCount,
|
|
|
|
&u.ChosenMode.ReplaysWatched, &u.ChosenMode.TotalHits,
|
|
|
|
&u.ChosenMode.Accuracy, &u.ChosenMode.PP,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
md.Err(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
u.ChosenMode.Level = ocl.GetLevelPrecise(int64(u.ChosenMode.TotalScore))
|
|
|
|
if i := leaderboardPositionAp(md.R, m, u.ID); i != nil {
|
|
|
|
u.ChosenMode.GlobalLeaderboardRank = i
|
|
|
|
}
|
|
|
|
if i := countryPositionAp(md.R, m, u.ID, u.Country); i != nil {
|
|
|
|
u.ChosenMode.CountryLeaderboardRank = i
|
|
|
|
}
|
|
|
|
resp.Users = append(resp.Users, u)
|
|
|
|
}
|
|
|
|
return resp
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func leaderboardPositionRx(r *redis.Client, mode string, user int) *int {
|
|
|
|
return _position(r, "ripple:leaderboard_relax:"+mode, user)
|
|
|
|
}
|
|
|
|
|
|
|
|
func countryPositionRx(r *redis.Client, mode string, user int, country string) *int {
|
|
|
|
return _position(r, "ripple:leaderboard_relax:"+mode+":"+strings.ToLower(country), user)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func leaderboardPositionAp(r *redis.Client, mode string, user int) *int {
|
|
|
|
return _position(r, "ripple:leaderboard_auto:"+mode, user)
|
|
|
|
}
|
|
|
|
|
|
|
|
func countryPositionAp(r *redis.Client, mode string, user int, country string) *int {
|
|
|
|
return _position(r, "ripple:leaderboard_auto:"+mode+":"+strings.ToLower(country), user)
|
|
|
|
}
|