added endpoint for sidebar metastats
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/an0nfunc/go-steam/v3/netutil"
|
"github.com/an0nfunc/go-steam/v3/netutil"
|
||||||
"github.com/an0nfunc/go-steam/v3/protocol/gamecoordinator"
|
"github.com/an0nfunc/go-steam/v3/protocol/gamecoordinator"
|
||||||
"github.com/an0nfunc/go-steam/v3/protocol/steamlang"
|
"github.com/an0nfunc/go-steam/v3/protocol/steamlang"
|
||||||
|
"github.com/go-redis/cache/v8"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"go.uber.org/ratelimit"
|
"go.uber.org/ratelimit"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
@@ -37,6 +38,7 @@ type DemoMatchLoaderConfig struct {
|
|||||||
Worker int
|
Worker int
|
||||||
ApiKey string
|
ApiKey string
|
||||||
RateLimit ratelimit.Limiter
|
RateLimit ratelimit.Limiter
|
||||||
|
Cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
type DemoMatchLoader struct {
|
type DemoMatchLoader struct {
|
||||||
@@ -53,6 +55,7 @@ type DemoMatchLoader struct {
|
|||||||
dp *DemoParser
|
dp *DemoParser
|
||||||
parseDemo chan *Demo
|
parseDemo chan *Demo
|
||||||
parseMap map[string]bool
|
parseMap map[string]bool
|
||||||
|
cache *cache.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func AccountId2SteamId(accId uint32) uint64 {
|
func AccountId2SteamId(accId uint32) uint64 {
|
||||||
@@ -172,6 +175,7 @@ func (d *DemoMatchLoader) Setup(config *DemoMatchLoaderConfig) error {
|
|||||||
d.db = config.Db
|
d.db = config.Db
|
||||||
d.dp = &DemoParser{}
|
d.dp = &DemoParser{}
|
||||||
d.parseMap = map[string]bool{}
|
d.parseMap = map[string]bool{}
|
||||||
|
d.cache = config.Cache
|
||||||
err := d.dp.Setup(config.Db, config.Lock, config.Worker)
|
err := d.dp.Setup(config.Db, config.Lock, config.Worker)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -486,6 +490,12 @@ func (d *DemoMatchLoader) gcWorker(apiKey string, rl ratelimit.Limiter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clear cache for player
|
||||||
|
for _, p := range players {
|
||||||
|
_ = d.cache.Delete(context.Background(), fmt.Sprintf(utils.SideMetaCacheKey, p.ID))
|
||||||
|
_ = d.cache.Delete(context.Background(), fmt.Sprintf(utils.MatchMetaCacheKey, p.ID))
|
||||||
|
}
|
||||||
|
|
||||||
err = d.dp.ParseDemo(demo)
|
err = d.dp.ParseDemo(demo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("[DL] Can't queue demo %d for parsing: %v", demo.MatchId, err)
|
log.Warningf("[DL] Can't queue demo %d for parsing: %v", demo.MatchId, err)
|
||||||
|
74
main.go
74
main.go
@@ -46,6 +46,7 @@ var (
|
|||||||
configFlag = flag.String("config", "config.yaml", "Set config to use")
|
configFlag = flag.String("config", "config.yaml", "Set config to use")
|
||||||
authCodeFlag = flag.String("authcode", "", "Provide Steam AuthCode to login")
|
authCodeFlag = flag.String("authcode", "", "Provide Steam AuthCode to login")
|
||||||
journalLogFlag = flag.Bool("journal", false, "Log to systemd journal instead of stdout")
|
journalLogFlag = flag.Bool("journal", false, "Log to systemd journal instead of stdout")
|
||||||
|
sqlDebugFlag = flag.Bool("sqldebug", false, "Debug SQL queries")
|
||||||
)
|
)
|
||||||
|
|
||||||
func housekeeping() {
|
func housekeeping() {
|
||||||
@@ -146,6 +147,64 @@ func housekeeping() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPlayerMeta(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains)
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
l := mux.Vars(r)["limit"]
|
||||||
|
|
||||||
|
var limit int
|
||||||
|
var err error
|
||||||
|
if l != "" {
|
||||||
|
limit, err = strconv.Atoi(l)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("[GPM] limit not an int: %v", err)
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
limit = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
tPlayer, err := utils.GetPlayer(db, id, conf.Steam.APIKey, nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("[GP] Player not found: %+v", err)
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
metaStats := new(utils.MetaStatsResponse)
|
||||||
|
err = rdc.Get(context.Background(), fmt.Sprintf(utils.SideMetaCacheKey, tPlayer.ID), &metaStats)
|
||||||
|
if err != nil {
|
||||||
|
metaStats, err = utils.GetMetaStats(tPlayer, db.Lock, limit)
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("[GPM] Unable to get MetaStats: %v", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rdc.Set(&cache.Item{
|
||||||
|
Ctx: context.Background(),
|
||||||
|
Key: fmt.Sprintf(utils.SideMetaCacheKey, tPlayer.ID),
|
||||||
|
Value: metaStats,
|
||||||
|
TTL: time.Hour * 24 * 30,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("[GPM] Failure saving to cache: %v", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("[GPM] SideMetaStats for %d saved to cache", tPlayer.ID)
|
||||||
|
} else {
|
||||||
|
log.Debugf("[GPM] SideMetaStats for %d from cache", tPlayer.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = utils.SendJSON(metaStats, w)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("[GPM] Unable to marshal JSON: %v", err)
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getPlayer(w http.ResponseWriter, r *http.Request) {
|
func getPlayer(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains)
|
w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains)
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
@@ -205,7 +264,7 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
metaStats := new(utils.MatchStats)
|
metaStats := new(utils.MatchStats)
|
||||||
err = rdc.Get(context.Background(), fmt.Sprintf("csgowtfd_meta_%d", tPlayer.ID), &metaStats)
|
err = rdc.Get(context.Background(), fmt.Sprintf(utils.MatchMetaCacheKey, tPlayer.ID), &metaStats)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wins, ties, losses, err := utils.GetMatchStats(tPlayer, db.Lock)
|
wins, ties, losses, err := utils.GetMatchStats(tPlayer, db.Lock)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -222,9 +281,9 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = rdc.Set(&cache.Item{
|
err = rdc.Set(&cache.Item{
|
||||||
Ctx: context.Background(),
|
Ctx: context.Background(),
|
||||||
Key: fmt.Sprintf("csgowtfd_meta_%d", tPlayer.ID),
|
Key: fmt.Sprintf(utils.MatchMetaCacheKey, tPlayer.ID),
|
||||||
Value: response.MatchStats,
|
Value: response.MatchStats,
|
||||||
TTL: time.Hour * 12,
|
TTL: time.Hour * 24 * 30,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("[GP] Failure saving to cache: %v", err)
|
log.Errorf("[GP] Failure saving to cache: %v", err)
|
||||||
@@ -233,7 +292,7 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
log.Debugf("[GP] Metastats for %d saved to cache", tPlayer.ID)
|
log.Debugf("[GP] Metastats for %d saved to cache", tPlayer.ID)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("[GP] Metastats for %d from redis", tPlayer.ID)
|
log.Debugf("[GP] Metastats for %d from cache", tPlayer.ID)
|
||||||
|
|
||||||
response.MatchStats = &utils.MatchStats{
|
response.MatchStats = &utils.MatchStats{
|
||||||
Win: metaStats.Win,
|
Win: metaStats.Win,
|
||||||
@@ -734,6 +793,10 @@ func main() {
|
|||||||
}(db.Client)
|
}(db.Client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *sqlDebugFlag {
|
||||||
|
db.Client = db.Client.Debug()
|
||||||
|
}
|
||||||
|
|
||||||
if err := db.Client.Schema.Create(
|
if err := db.Client.Schema.Create(
|
||||||
context.Background(),
|
context.Background(),
|
||||||
migrate.WithDropIndex(true),
|
migrate.WithDropIndex(true),
|
||||||
@@ -779,7 +842,8 @@ func main() {
|
|||||||
// routes
|
// routes
|
||||||
router = mux.NewRouter().StrictSlash(true)
|
router = mux.NewRouter().StrictSlash(true)
|
||||||
router.HandleFunc("/player/{id}", getPlayer).Methods(http.MethodGet, http.MethodOptions)
|
router.HandleFunc("/player/{id}", getPlayer).Methods(http.MethodGet, http.MethodOptions)
|
||||||
router.HandleFunc(`/player/{id}/after/{time:\d+}`, getPlayer).Methods(http.MethodGet, http.MethodOptions)
|
router.HandleFunc(`/player/{id}/next/{time:\d+}`, getPlayer).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
router.HandleFunc(`/player/{id}/meta/{limit:\d*}`, getPlayerMeta).Methods(http.MethodGet, http.MethodOptions)
|
||||||
router.HandleFunc("/player/{id}/track", postPlayerTrack).Methods(http.MethodPost, http.MethodOptions)
|
router.HandleFunc("/player/{id}/track", postPlayerTrack).Methods(http.MethodPost, http.MethodOptions)
|
||||||
router.HandleFunc("/player/{id}/track", deletePlayerTrack).Methods(http.MethodOptions, http.MethodDelete)
|
router.HandleFunc("/player/{id}/track", deletePlayerTrack).Methods(http.MethodOptions, http.MethodDelete)
|
||||||
router.HandleFunc("/match/parse/{sharecode}", getMatchParse).Methods(http.MethodGet, http.MethodOptions)
|
router.HandleFunc("/match/parse/{sharecode}", getMatchParse).Methods(http.MethodGet, http.MethodOptions)
|
||||||
|
217
utils/utils.go
217
utils/utils.go
@@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/an0nfunc/go-steamapi"
|
"github.com/an0nfunc/go-steamapi"
|
||||||
|
"github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/common"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"go.uber.org/ratelimit"
|
"go.uber.org/ratelimit"
|
||||||
"io"
|
"io"
|
||||||
@@ -18,6 +19,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -137,16 +139,33 @@ type PlayerResponse struct {
|
|||||||
VAC bool `json:"vac"`
|
VAC bool `json:"vac"`
|
||||||
VACDate int64 `json:"vac_date,omitempty"`
|
VACDate int64 `json:"vac_date,omitempty"`
|
||||||
GameBan bool `json:"game_ban"`
|
GameBan bool `json:"game_ban"`
|
||||||
GameBanDate int64 `json:"game_ban_date"`
|
GameBanDate int64 `json:"game_ban_date,omitempty"`
|
||||||
Tracked bool `json:"tracked"`
|
Tracked bool `json:"tracked"`
|
||||||
VanityURL string `json:"vanity_url,omitempty"`
|
VanityURL string `json:"vanity_url,omitempty"`
|
||||||
MatchStats *MatchStats `json:"match_stats,omitempty"`
|
MatchStats *MatchStats `json:"match_stats,omitempty"`
|
||||||
Matches []*MatchResponse `json:"matches,omitempty"`
|
Matches []*MatchResponse `json:"matches,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type WeaponResponse struct {
|
type MateResponse struct {
|
||||||
Player *PlayerResponse `json:"player"`
|
Player *PlayerResponse `json:"player"`
|
||||||
Eq map[string][][]int `json:"eq,omitempty"`
|
WinRate float32 `json:"win_rate,omitempty"`
|
||||||
|
TieRate float32 `json:"tie_rate,omitempty"`
|
||||||
|
Total int `json:"total,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WeaponDmg struct {
|
||||||
|
Eq int `json:"eq"`
|
||||||
|
Dmg uint `json:"dmg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetaStatsResponse struct {
|
||||||
|
Player *PlayerResponse `json:"player"`
|
||||||
|
BestMates []*MateResponse `json:"best_mates,omitempty"`
|
||||||
|
MostMates []*MateResponse `json:"most_mates,omitempty"`
|
||||||
|
EqMap map[int]string `json:"eq_map,omitempty"`
|
||||||
|
WeaponDmg []*WeaponDmg `json:"weapon_dmg,omitempty"`
|
||||||
|
WinMaps map[string]float32 `json:"win_maps,omitempty"`
|
||||||
|
TieMaps map[string]float32 `json:"tie_maps,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MatchResponse struct {
|
type MatchResponse struct {
|
||||||
@@ -177,6 +196,8 @@ type (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
shareCodeURLEntry = "https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=%s&steamid=%d&steamidkey=%s&knowncode=%s"
|
shareCodeURLEntry = "https://api.steampowered.com/ICSGOPlayers_730/GetNextMatchSharingCode/v1?key=%s&steamid=%d&steamidkey=%s&knowncode=%s"
|
||||||
|
SideMetaCacheKey = "csgowtfd_side_meta_%d"
|
||||||
|
MatchMetaCacheKey = "csgowtfd_match_meta_%d"
|
||||||
)
|
)
|
||||||
|
|
||||||
//goland:noinspection SpellCheckingInspection
|
//goland:noinspection SpellCheckingInspection
|
||||||
@@ -201,6 +222,180 @@ func SendJSON(data interface{}, w http.ResponseWriter) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetMatchStats(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, error) {
|
func GetMatchStats(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, error) {
|
||||||
|
wins, loss, ties, err := getWinLossTieFromPlayer(dbPlayer, lock)
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return wins, ties, loss, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMetaStats(dbPlayer *ent.Player, lock *sync.RWMutex, limit int) (*MetaStatsResponse, error) {
|
||||||
|
mResponse := new(MetaStatsResponse)
|
||||||
|
mResponse.Player = &PlayerResponse{SteamID64: dbPlayer.ID}
|
||||||
|
|
||||||
|
lock.RLock()
|
||||||
|
tPlayers, err := dbPlayer.QueryMatches().QueryPlayers().All(context.Background())
|
||||||
|
lock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.RLock()
|
||||||
|
matchIDs, err := dbPlayer.QueryMatches().IDs(context.Background())
|
||||||
|
lock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mapWins := map[string]int{}
|
||||||
|
mapTies := map[string]int{}
|
||||||
|
mapMatchTotal := map[string]int{}
|
||||||
|
matchSeen := map[uint64]bool{}
|
||||||
|
mResponse.EqMap = map[int]string{}
|
||||||
|
|
||||||
|
for _, s := range tPlayers {
|
||||||
|
if s.ID == dbPlayer.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mateRes := new(MateResponse)
|
||||||
|
mostRes := new(MateResponse)
|
||||||
|
|
||||||
|
playerRes := &PlayerResponse{
|
||||||
|
SteamID64: s.ID,
|
||||||
|
Name: s.Name,
|
||||||
|
Avatar: s.Avatar,
|
||||||
|
Tracked: s.AuthCode != "",
|
||||||
|
VanityURL: s.VanityURLReal,
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.RLock()
|
||||||
|
pMatches, err := s.QueryMatches().Where(match.IDIn(matchIDs...)).WithStats().Where(match.HasStatsWith(stats.Or(stats.PlayerStats(dbPlayer.ID), stats.PlayerStats(s.ID)))).All(context.Background())
|
||||||
|
lock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mostRes.Player = playerRes
|
||||||
|
var wins int
|
||||||
|
var ties int
|
||||||
|
|
||||||
|
for _, pm := range pMatches {
|
||||||
|
var subjectStats *ent.Stats
|
||||||
|
var currentStats *ent.Stats
|
||||||
|
|
||||||
|
for _, ps := range pm.Edges.Stats {
|
||||||
|
if ps.PlayerStats == dbPlayer.ID {
|
||||||
|
subjectStats = ps
|
||||||
|
} else if ps.PlayerStats == s.ID {
|
||||||
|
currentStats = ps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
win := subjectStats.TeamID == pm.MatchResult
|
||||||
|
tie := pm.MatchResult == 0
|
||||||
|
|
||||||
|
if _, ok := matchSeen[pm.ID]; !ok {
|
||||||
|
mapMatchTotal[pm.Map]++
|
||||||
|
if win {
|
||||||
|
mapWins[pm.Map]++
|
||||||
|
} else if tie {
|
||||||
|
mapTies[pm.Map]++
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.RLock()
|
||||||
|
wSs, err := subjectStats.QueryWeaponStats().All(context.Background())
|
||||||
|
lock.RUnlock()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, weaponStat := range wSs {
|
||||||
|
found := false
|
||||||
|
for _, dmgS := range mResponse.WeaponDmg {
|
||||||
|
if dmgS.Eq == weaponStat.EqType {
|
||||||
|
dmgS.Dmg += weaponStat.Dmg
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
mResponse.WeaponDmg = append(mResponse.WeaponDmg, &WeaponDmg{
|
||||||
|
Eq: weaponStat.EqType,
|
||||||
|
Dmg: weaponStat.Dmg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exist := mResponse.EqMap[weaponStat.EqType]; !exist {
|
||||||
|
mResponse.EqMap[weaponStat.EqType] = common.EquipmentType(weaponStat.EqType).String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matchSeen[pm.ID] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if same team
|
||||||
|
if subjectStats.TeamID == currentStats.TeamID {
|
||||||
|
mostRes.Total++
|
||||||
|
if win {
|
||||||
|
wins++
|
||||||
|
} else if tie {
|
||||||
|
ties++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if mostRes.Total > 0 {
|
||||||
|
mResponse.MostMates = append(mResponse.MostMates, mostRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mostRes.Total > 0 && (wins > 0 || ties > 0) {
|
||||||
|
mateRes.Player = playerRes
|
||||||
|
mateRes.TieRate = float32(ties) / float32(mostRes.Total)
|
||||||
|
mateRes.WinRate = float32(wins) / float32(mostRes.Total)
|
||||||
|
mResponse.BestMates = append(mResponse.BestMates, mateRes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mResponse.TieMaps = map[string]float32{}
|
||||||
|
mResponse.WinMaps = map[string]float32{}
|
||||||
|
|
||||||
|
for tMap, wins := range mapWins {
|
||||||
|
mResponse.WinMaps[tMap] = float32(wins) / float32(mapMatchTotal[tMap])
|
||||||
|
}
|
||||||
|
|
||||||
|
for tMap, ties := range mapTies {
|
||||||
|
mResponse.TieMaps[tMap] = float32(ties) / float32(mapMatchTotal[tMap])
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort all results
|
||||||
|
sort.Slice(mResponse.BestMates, func(i, j int) bool {
|
||||||
|
return mResponse.BestMates[i].WinRate > mResponse.BestMates[j].WinRate
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Slice(mResponse.MostMates, func(i, j int) bool {
|
||||||
|
return mResponse.MostMates[i].Total > mResponse.MostMates[j].Total
|
||||||
|
})
|
||||||
|
|
||||||
|
sort.Slice(mResponse.WeaponDmg, func(i, j int) bool {
|
||||||
|
return mResponse.WeaponDmg[i].Dmg > mResponse.WeaponDmg[j].Dmg
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(mResponse.BestMates) > limit {
|
||||||
|
mResponse.BestMates = mResponse.BestMates[:limit]
|
||||||
|
}
|
||||||
|
if len(mResponse.MostMates) > limit {
|
||||||
|
mResponse.MostMates = mResponse.MostMates[:limit]
|
||||||
|
}
|
||||||
|
if len(mResponse.WeaponDmg) > limit {
|
||||||
|
mResponse.WeaponDmg = mResponse.WeaponDmg[:limit]
|
||||||
|
}
|
||||||
|
|
||||||
|
return mResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWinLossTieFromPlayer(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, error) {
|
||||||
var res []struct {
|
var res []struct {
|
||||||
MatchResult int `json:"match_result"`
|
MatchResult int `json:"match_result"`
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
@@ -209,8 +404,15 @@ func GetMatchStats(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, err
|
|||||||
lock.RLock()
|
lock.RLock()
|
||||||
err := dbPlayer.QueryMatches().GroupBy(match.FieldMatchResult).Aggregate(func(s *sql.Selector) string {
|
err := dbPlayer.QueryMatches().GroupBy(match.FieldMatchResult).Aggregate(func(s *sql.Selector) string {
|
||||||
sT := sql.Table(stats.Table)
|
sT := sql.Table(stats.Table)
|
||||||
|
|
||||||
s.Join(sT).On(s.C(match.FieldID), sT.C(stats.MatchesColumn))
|
s.Join(sT).On(s.C(match.FieldID), sT.C(stats.MatchesColumn))
|
||||||
s.Where(sql.And(sql.Or(sql.ColumnsEQ(match.FieldMatchResult, stats.FieldTeamID), sql.EQ(s.C(match.FieldMatchResult), 0)), sql.EQ(sT.C(stats.PlayersColumn), dbPlayer.ID)))
|
s.Where(sql.And(
|
||||||
|
sql.Or(
|
||||||
|
sql.ColumnsEQ(match.FieldMatchResult, stats.FieldTeamID),
|
||||||
|
sql.EQ(s.C(match.FieldMatchResult), 0),
|
||||||
|
),
|
||||||
|
sql.EQ(sT.C(stats.PlayersColumn), dbPlayer.ID),
|
||||||
|
))
|
||||||
return sql.Count("*")
|
return sql.Count("*")
|
||||||
}).Scan(context.Background(), &res)
|
}).Scan(context.Background(), &res)
|
||||||
lock.RUnlock()
|
lock.RUnlock()
|
||||||
@@ -227,9 +429,6 @@ func GetMatchStats(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, err
|
|||||||
return 0, 0, 0, err
|
return 0, 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res) < 1 {
|
|
||||||
return 0, 0, 0, nil
|
|
||||||
}
|
|
||||||
var (
|
var (
|
||||||
wins int
|
wins int
|
||||||
ties int
|
ties int
|
||||||
@@ -244,7 +443,7 @@ func GetMatchStats(dbPlayer *ent.Player, lock *sync.RWMutex) (int, int, int, err
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return wins, ties, total - wins - ties, nil
|
return wins, total - wins - ties, ties, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsAuthCodeValid(player *ent.Player, lock *sync.RWMutex, apiKey string, shareCode string, authCode string, rl ratelimit.Limiter) (bool, error) {
|
func IsAuthCodeValid(player *ent.Player, lock *sync.RWMutex, apiKey string, shareCode string, authCode string, rl ratelimit.Limiter) (bool, error) {
|
||||||
|
Reference in New Issue
Block a user