refactored some functions, added demo download retry

This commit is contained in:
2021-10-09 19:10:38 +02:00
parent 15b273f052
commit e938b05f52
13 changed files with 242 additions and 322 deletions

View File

@@ -1,6 +1,10 @@
package csgo
import (
"context"
"csgowtfd/ent"
"csgowtfd/ent/match"
"csgowtfd/utils"
"encoding/json"
"fmt"
"github.com/an0nfunc/go-steam/v3"
@@ -9,10 +13,12 @@ import (
"github.com/an0nfunc/go-steam/v3/protocol/gamecoordinator"
"github.com/an0nfunc/go-steam/v3/protocol/steamlang"
log "github.com/sirupsen/logrus"
"go.uber.org/ratelimit"
"google.golang.org/protobuf/proto"
"io/ioutil"
"math/rand"
"os"
"sync"
"time"
)
@@ -21,6 +27,20 @@ const (
APPID = 730
)
type DemoMatchLoaderConfig struct {
Username string
Password string
AuthCode string
Sentry string
LoginKey string
ServerList string
Db *ent.Client
Lock *sync.RWMutex
Worker int
ApiKey string
RateLimit ratelimit.Limiter
}
type DemoMatchLoader struct {
client *steam.Client
GCReady bool
@@ -30,6 +50,10 @@ type DemoMatchLoader struct {
sentryFile string
loginKey string
serverList string
db *ent.Client
lock *sync.RWMutex
dp *DemoParser
parseDemo chan *Demo
}
func AccountId2SteamId(accId uint32) uint64 {
@@ -82,7 +106,7 @@ func (d *DemoMatchLoader) HandleGCPacket(pkg *gamecoordinator.GCPacket) {
}
}
func (d *DemoMatchLoader) GetMatchDetails(sharecode string) (*protobuf.CMsgGCCStrike15V2_MatchList, error) {
func (d *DemoMatchLoader) getMatchDetails(sharecode string) (*protobuf.CMsgGCCStrike15V2_MatchList, error) {
if !d.GCReady {
return nil, fmt.Errorf("gc not ready")
}
@@ -91,18 +115,18 @@ func (d *DemoMatchLoader) GetMatchDetails(sharecode string) (*protobuf.CMsgGCCSt
if err != nil {
return nil, err
}
err = d.RequestDemoInfo(matchId, outcomeId, tokenId)
err = d.requestDemoInfo(matchId, outcomeId, tokenId)
if err != nil {
return nil, err
}
for {
select {
case match := <-d.matchRecv:
if *match.Matches[0].Matchid == matchId {
return match, nil
case matchDetails := <-d.matchRecv:
if *matchDetails.Matches[0].Matchid == matchId {
return matchDetails, nil
} else {
d.matchRecv <- match
d.matchRecv <- matchDetails
}
}
}
@@ -127,15 +151,15 @@ func (d *DemoMatchLoader) connectToSteam() error {
return nil
}
func (d *DemoMatchLoader) Setup(username string, password string, authCode string, sentry string, loginKey string, serverList string) error {
d.loginKey = loginKey
d.sentryFile = sentry
d.serverList = serverList
func (d *DemoMatchLoader) Setup(config *DemoMatchLoaderConfig) error {
d.loginKey = config.LoginKey
d.sentryFile = config.Sentry
d.serverList = config.ServerList
d.steamLogin = new(steam.LogOnDetails)
d.steamLogin.Username = username
d.steamLogin.Password = password
d.steamLogin.AuthCode = authCode
d.steamLogin.Username = config.Username
d.steamLogin.Password = config.Password
d.steamLogin.AuthCode = config.AuthCode
d.steamLogin.ShouldRememberPassword = true
if _, err := os.Stat(d.sentryFile); err == nil {
@@ -166,14 +190,28 @@ func (d *DemoMatchLoader) Setup(username string, password string, authCode strin
}
d.client = steam.NewClient()
d.matchRecv = make(chan *protobuf.CMsgGCCStrike15V2_MatchList, 500)
d.matchRecv = make(chan *protobuf.CMsgGCCStrike15V2_MatchList, 1000)
d.parseDemo = make(chan *Demo, 1000)
go d.connectLoop()
go d.steamEventHandler()
for i := 0; i < config.Worker; i++ {
go d.gcWorker(config.ApiKey, config.RateLimit)
}
return nil
}
func (d DemoMatchLoader) LoadDemo(demo *Demo) error {
select {
case d.parseDemo <- demo:
return nil
default:
return fmt.Errorf("queue full")
}
}
func (d DemoMatchLoader) connectLoop() {
for d.connectToSteam() != nil {
log.Infof("Retrying connecting to steam")
@@ -207,15 +245,15 @@ func (d *DemoMatchLoader) steamEventHandler() {
case *steam.LoggedOnEvent:
log.Debug("[DL] Login successfully!")
d.client.Social.SetPersonaState(steamlang.EPersonaState_Online)
go d.SetPlaying()
go d.setPlaying()
case *steam.LogOnFailedEvent:
log.Warningf("[DL] Steam login denied: %+v", e)
switch e.Result {
case steamlang.EResult_AccountLogonDenied:
log.Fatalf("[DL] Please provide AuthCode with --authcode")
case steamlang.EResult_InvalidPassword:
os.Remove(d.sentryFile)
os.Remove(d.loginKey)
_ = os.Remove(d.sentryFile)
_ = os.Remove(d.loginKey)
log.Fatalf("[DL] Steam login wrong")
case steamlang.EResult_InvalidLoginAuthCode:
log.Fatalf("[DL] Steam auth code wrong")
@@ -242,7 +280,7 @@ func (d *DemoMatchLoader) steamEventHandler() {
}
}
func (d *DemoMatchLoader) SetPlaying() {
func (d *DemoMatchLoader) setPlaying() {
d.client.GC.SetGamesPlayed(APPID)
d.client.GC.RegisterPacketHandler(d)
go d.greetGC()
@@ -257,7 +295,7 @@ func (d *DemoMatchLoader) greetGC() {
}
}
func (d *DemoMatchLoader) RequestDemoInfo(matchId uint64, conclusionId uint64, tokenId uint32) error {
func (d *DemoMatchLoader) requestDemoInfo(matchId uint64, conclusionId uint64, tokenId uint32) error {
if !d.GCReady {
return fmt.Errorf("gc not ready")
}
@@ -270,3 +308,121 @@ func (d *DemoMatchLoader) RequestDemoInfo(matchId uint64, conclusionId uint64, t
return nil
}
func (d *DemoMatchLoader) gcWorker(apiKey string, rl ratelimit.Limiter) {
for {
select {
case demo := <-d.parseDemo:
if !d.GCReady {
time.Sleep(5 * time.Second)
d.parseDemo <- demo
continue
}
matchId, _, _, err := DecodeSharecode(demo.ShareCode)
if err != nil || matchId == 0 {
log.Warningf("[DL] Can't parse match with sharecode %s: %v", demo.ShareCode, err)
continue
}
d.lock.RLock()
iMatch, err := d.db.Match.Query().Where(match.ID(matchId)).Only(context.Background())
d.lock.RUnlock()
if err != nil {
switch e := err.(type) {
case *ent.NotFoundError:
break
default:
log.Errorf("[DL] Failure trying to find match %d in db: %v", matchId, e)
}
} else {
if iMatch.DemoParsed == false && iMatch.Date.Before(time.Now().UTC().AddDate(0, 0, -30)) {
log.Infof("[DL] Match %d is loaded, but not parsed. Try parsing.", demo.MatchId)
demo.MatchId = matchId
demo.Url = iMatch.ReplayURL
err := d.dp.ParseDemo(demo)
if err != nil {
log.Warningf("[DL] Parsing demo from match %d failed: %v", demo.MatchId, err)
}
continue
}
log.Debugf("[DL] Skipped match %d: already parsed", matchId)
continue
}
matchDetails, err := d.getMatchDetails(demo.ShareCode)
if err != nil {
log.Warningf("[DL] Failure to get match-details for %d from GC: %v", demo.MatchId, err)
continue
}
matchZero := matchDetails.GetMatches()[0]
lastRound := matchZero.GetRoundstatsall()[len(matchZero.Roundstatsall)-1]
var players []*ent.Player
for _, accountId := range lastRound.GetReservation().GetAccountIds() {
tPlayer, err := utils.GetPlayer(&utils.DBWithLock{
Client: d.db,
Lock: d.lock,
}, AccountId2SteamId(accountId), apiKey, rl)
if err != nil {
log.Warningf("[DL] Unable to get player for steamid %d: %v", AccountId2SteamId(accountId), err)
continue
}
players = append(players, tPlayer)
}
demo.Url = lastRound.GetMap()
demo.MatchId = matchZero.GetMatchid()
d.lock.Lock()
tMatch, err := d.db.Match.Create().
SetID(matchZero.GetMatchid()).
AddPlayers(players...).
SetDate(time.Unix(int64(matchZero.GetMatchtime()), 0).UTC()).
SetMaxRounds(int(lastRound.GetMaxRounds())).
SetDuration(int(lastRound.GetMatchDuration())).
SetShareCode(demo.ShareCode).
SetReplayURL(lastRound.GetMap()).
SetScoreTeamA(int(lastRound.GetTeamScores()[0])).
SetScoreTeamB(int(lastRound.GetTeamScores()[1])).
SetMatchResult(int(lastRound.GetMatchResult())).
Save(context.Background())
d.lock.Unlock()
if err != nil {
log.Warningf("[DL] Unable to create match %d: %v", matchZero.GetMatchid(), err)
continue
}
for i, mPlayer := range players {
var teamId int
if i > 4 {
teamId = 2
} else {
teamId = 1
}
d.lock.Lock()
err := d.db.Stats.Create().
SetMatches(tMatch).
SetPlayers(mPlayer).
SetTeamID(teamId).
SetKills(int(lastRound.GetKills()[i])).
SetDeaths(int(lastRound.GetDeaths()[i])).
SetAssists(int(lastRound.GetAssists()[i])).
SetMvp(int(lastRound.GetMvps()[i])).
SetScore(int(lastRound.GetScores()[i])).
SetHeadshot(int(lastRound.GetEnemyHeadshots()[i])).
Exec(context.Background())
d.lock.Unlock()
if err != nil {
log.Warningf("[DL] Unable to create stats for player %d in match %d: %v", mPlayer.ID, tMatch.ID, err)
}
}
err = d.dp.ParseDemo(demo)
if err != nil {
log.Warningf("[DL] Can't queue demo %d for parsing: %v", demo.MatchId, err)
}
}
}
}