ripple-api/app/websockets/scores.go

174 lines
3.2 KiB
Go

package websockets
import (
"encoding/json"
"fmt"
"strings"
"sync"
"gopkg.in/thehowl/go-osuapi.v1"
"github.com/osuyozora/api/app/v1"
"github.com/osuyozora/api/common"
"zxq.co/x/getrank"
)
type subscribeScoresUser struct {
User int `json:"user"`
Modes []int `json:"modes"`
}
// SubscribeScores subscribes a connection to score updates.
func SubscribeScores(c *conn, message incomingMessage) {
var ssu []subscribeScoresUser
err := json.Unmarshal(message.Data, &ssu)
if err != nil {
c.WriteJSON(TypeInvalidMessage, err.Error())
return
}
scoreSubscriptionsMtx.Lock()
var found bool
for idx, el := range scoreSubscriptions {
// already exists, change the users
if el.Conn.ID == c.ID {
found = true
scoreSubscriptions[idx].Users = ssu
}
}
// if it was not found, we need to add it
if !found {
scoreSubscriptions = append(scoreSubscriptions, scoreSubscription{c, ssu})
}
scoreSubscriptionsMtx.Unlock()
c.WriteJSON(TypeSubscribedToScores, ssu)
}
type scoreSubscription struct {
Conn *conn
Users []subscribeScoresUser
}
var scoreSubscriptions []scoreSubscription
var scoreSubscriptionsMtx = new(sync.RWMutex)
func scoreRetriever() {
ps, err := red.Subscribe("api:score_submission")
if err != nil {
fmt.Println(err)
}
for {
msg, err := ps.ReceiveMessage()
if err != nil {
fmt.Println(err.Error())
return
}
go handleNewScore(msg.Payload)
}
}
type scoreUser struct {
UserID int `json:"id"`
Username string `json:"username"`
Privileges uint64 `json:"privileges"`
}
type score struct {
v1.Score
scoreUser
}
type scoreJSON struct {
v1.Score
UserID int `json:"user_id"`
User scoreUser `json:"user"`
}
func handleNewScore(id string) {
defer catchPanic()
var s score
err := db.Get(&s, `
SELECT
s.id, s.beatmap_md5, s.score, s.max_combo, s.full_combo, s.mods,
s.300_count, s.100_count, s.50_count, s.gekis_count, s.katus_count, s.misses_count,
s.time, s.play_mode, s.accuracy, s.pp, s.completed, s.userid AS user_id,
u.username, u.privileges
FROM scores s
INNER JOIN users u ON s.userid = u.id
WHERE s.id = ?`, id)
if err != nil {
fmt.Println(err)
return
}
s.Rank = strings.ToUpper(getrank.GetRank(
osuapi.Mode(s.PlayMode),
osuapi.Mods(s.Mods),
s.Accuracy,
s.Count300,
s.Count100,
s.Count50,
s.CountMiss,
))
sj := scoreJSON{
Score: s.Score,
UserID: s.UserID,
User: s.scoreUser,
}
scoreSubscriptionsMtx.RLock()
cp := make([]scoreSubscription, len(scoreSubscriptions))
copy(cp, scoreSubscriptions)
scoreSubscriptionsMtx.RUnlock()
for _, el := range cp {
if len(el.Users) > 0 && !scoreUserValid(el.Users, sj) {
continue
}
if sj.User.Privileges&3 != 3 && !el.Conn.RestrictedVisible {
continue
}
el.Conn.WriteJSON(TypeNewScore, sj)
}
}
func scoreUserValid(users []subscribeScoresUser, s scoreJSON) bool {
for _, u := range users {
if u.User == s.UserID {
if len(u.Modes) > 0 {
if !inModes(u.Modes, s.PlayMode) {
return false
}
}
return true
}
}
return false
}
func inModes(modes []int, i int) bool {
for _, m := range modes {
if m == i {
return true
}
}
return false
}
func catchPanic() {
r := recover()
if r != nil {
switch r := r.(type) {
case error:
common.WSErr(r)
default:
fmt.Println("PANIC", r)
}
}
}