refactored some functions, added demo download retry
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user