added weapon-stats endpoint

This commit is contained in:
2021-10-13 17:30:17 +02:00
parent ddd9b79ebb
commit d74b8e3447
3 changed files with 207 additions and 100 deletions

View File

@@ -317,6 +317,10 @@ func (p *DemoParser) parseWorker() {
} }
for _, tMatchPlayer := range tStats { for _, tMatchPlayer := range tStats {
if tMatchPlayer.Color == "" {
tMatchPlayer.Color = stats.ColorGrey
}
p.lock.Lock() p.lock.Lock()
nMatchPLayer, err := tMatchPlayer.Update(). nMatchPLayer, err := tMatchPlayer.Update().
SetDmgTeam(tMatchPlayer.DmgTeam). SetDmgTeam(tMatchPlayer.DmgTeam).

236
main.go
View File

@@ -57,6 +57,18 @@ type PlayerResponse struct {
Matches []*MatchResponse `json:"matches,omitempty"` Matches []*MatchResponse `json:"matches,omitempty"`
} }
type EqResponse struct {
Victim uint64 `json:"victim"`
Type int `json:"type"`
HitGroup int `json:"hit_group"`
Dmg uint `json:"dmg"`
}
type WeaponResponse struct {
Player *PlayerResponse `json:"player"`
Eq []*EqResponse `json:"eq,omitempty"`
}
type MatchResponse struct { type MatchResponse struct {
MatchId uint64 `json:"match_id,string"` MatchId uint64 `json:"match_id,string"`
ShareCode string `json:"share_code,omitempty"` ShareCode string `json:"share_code,omitempty"`
@@ -70,63 +82,6 @@ type MatchResponse struct {
Stats interface{} `json:"stats,omitempty"` Stats interface{} `json:"stats,omitempty"`
} }
type StatsResponse struct {
TeamID int `json:"team_id"`
Kills int `json:"kills"`
Deaths int `json:"deaths"`
Assists int `json:"assists"`
Headshot int `json:"headshot"`
MVP uint `json:"mvp"`
Score int `json:"score"`
Player interface{} `json:"player,omitempty"`
Rank struct {
Old int `json:"old,omitempty"`
New int `json:"new,omitempty"`
} `json:"rank,omitempty"`
MultiKills struct {
Duo uint `json:"duo,omitempty"`
Triple uint `json:"triple,omitempty"`
Quad uint `json:"quad,omitempty"`
Pent uint `json:"pent,omitempty"`
} `json:"multi_kills,omitempty"`
Dmg struct {
Enemy uint `json:"enemy,omitempty"`
Team uint `json:"team,omitempty"`
UD struct {
HE uint `json:"he,omitempty"`
Flames uint `json:"flames,omitempty"`
Flash uint `json:"flash,omitempty"`
Decoy uint `json:"decoy,omitempty"`
Smoke uint `json:"smoke,omitempty"`
} `json:"ud,omitempty"`
HitGroup struct {
Head uint `json:"head,omitempty"`
Chest uint `json:"chest,omitempty"`
Stomach uint `json:"stomach,omitempty"`
LeftArm uint `json:"left_arm,omitempty"`
RightArm uint `json:"right_arm,omitempty"`
LeftLeg uint `json:"left_leg,omitempty"`
RightLeg uint `json:"right_leg,omitempty"`
Gear uint `json:"gear,omitempty"`
} `json:"hit_group,omitempty"`
} `json:"dmg,omitempty"`
Flash struct {
Duration struct {
Self float32 `json:"self,omitempty"`
Team float32 `json:"team,omitempty"`
Enemy float32 `json:"enemy,omitempty"`
} `json:"duration,omitempty"`
Total struct {
Team uint `json:"team,omitempty"`
Enemy uint `json:"enemy,omitempty"`
Self uint `json:"self,omitempty"`
} `json:"total,omitempty"`
} `json:"flash,omitempty"`
Crosshair string `json:"crosshair,omitempty"`
Color string `json:"color,omitempty"`
KAST int `json:"kast,omitempty"`
}
func housekeeping() { func housekeeping() {
for { for {
if !firstHK { if !firstHK {
@@ -301,7 +256,7 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
continue continue
} }
sResponse := &StatsResponse{ sResponse := &utils.StatsResponse{
TeamID: tStats.TeamID, TeamID: tStats.TeamID,
Kills: tStats.Kills, Kills: tStats.Kills,
Deaths: tStats.Deaths, Deaths: tStats.Deaths,
@@ -311,16 +266,22 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
Score: tStats.Score, Score: tStats.Score,
} }
sResponse.MultiKills.Duo = tStats.Mk2 sResponse.MultiKills = &utils.MultiKills{
sResponse.MultiKills.Triple = tStats.Mk3 Duo: tStats.Mk2,
sResponse.MultiKills.Quad = tStats.Mk4 Triple: tStats.Mk3,
sResponse.MultiKills.Pent = tStats.Mk5 Quad: tStats.Mk4,
Pent: tStats.Mk5,
}
sResponse.Rank.Old = tStats.RankOld sResponse.Rank = &utils.Rank{
sResponse.Rank.New = tStats.RankNew Old: tStats.RankOld,
New: tStats.RankNew,
}
sResponse.Dmg.Enemy = tStats.DmgEnemy sResponse.Dmg = &utils.Damage{
sResponse.Dmg.Team = tStats.DmgTeam Enemy: tStats.DmgEnemy,
Team: tStats.DmgTeam,
}
mResponse.Stats = sResponse mResponse.Stats = sResponse
response.Matches = append(response.Matches, mResponse) response.Matches = append(response.Matches, mResponse)
@@ -409,6 +370,64 @@ func getMatchParse(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusAccepted) w.WriteHeader(http.StatusAccepted)
} }
func getMatchWeapons(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains)
id := mux.Vars(r)["id"]
if id == "" {
w.WriteHeader(http.StatusBadRequest)
return
}
matchId, err := strconv.ParseUint(id, 10, 64)
if err != nil {
log.Warningf("[GM] Error parsing matchID %s: %v", id, err)
w.WriteHeader(http.StatusBadRequest)
return
}
mResponse := make([]*WeaponResponse, 0)
db.Lock.RLock()
tStats, err := db.Client.Stats.Query().Where(stats.HasMatchesWith(match.ID(matchId))).All(context.Background())
db.Lock.RUnlock()
if err != nil {
log.Warningf("[GMW] match %d not found: %+v", matchId, err)
w.WriteHeader(http.StatusNotFound)
return
}
for _, stat := range tStats {
db.Lock.RLock()
mWs, err := stat.QueryWeaponStats().All(context.Background())
db.Lock.RUnlock()
if err != nil {
log.Warningf("[GMW] Unbale to get WeaponStats for player %d: %v", stat.PlayerStats, err)
continue
}
mWr := &WeaponResponse{
Player: &PlayerResponse{SteamID64: stat.PlayerStats},
}
for _, wr := range mWs {
mWr.Eq = append(mWr.Eq, &EqResponse{
Victim: wr.Victim,
Type: wr.EqType,
HitGroup: wr.HitGroup,
Dmg: wr.Dmg,
})
}
mResponse = append(mResponse, mWr)
}
err = utils.SendJSON(mResponse, w)
if err != nil {
log.Errorf("[GM] JSON: %+v", err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func getMatch(w http.ResponseWriter, r *http.Request) { func getMatch(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"]
@@ -444,7 +463,7 @@ func getMatch(w http.ResponseWriter, r *http.Request) {
MatchResult: tMatch.MatchResult, MatchResult: tMatch.MatchResult,
MaxRounds: tMatch.MaxRounds, MaxRounds: tMatch.MaxRounds,
Parsed: tMatch.DemoParsed, Parsed: tMatch.DemoParsed,
Stats: []*StatsResponse{}, Stats: []*utils.StatsResponse{},
} }
db.Lock.RLock() db.Lock.RLock()
@@ -456,10 +475,10 @@ func getMatch(w http.ResponseWriter, r *http.Request) {
return return
} }
tmpStats := make([]*StatsResponse, 0) tmpStats := make([]*utils.StatsResponse, 0)
for _, iStats := range tStats { for _, iStats := range tStats {
sResponse := &StatsResponse{ sResponse := &utils.StatsResponse{
Player: PlayerResponse{ Player: PlayerResponse{
SteamID64: iStats.Edges.Players.ID, SteamID64: iStats.Edges.Players.ID,
Name: iStats.Edges.Players.Name, Name: iStats.Edges.Players.Name,
@@ -475,38 +494,54 @@ func getMatch(w http.ResponseWriter, r *http.Request) {
Headshot: iStats.Headshot, Headshot: iStats.Headshot,
MVP: iStats.Mvp, MVP: iStats.Mvp,
Score: iStats.Score, Score: iStats.Score,
}
sResponse.Color = iStats.Color.String() Dmg: &utils.Damage{
sResponse.Crosshair = iStats.Crosshair Team: iStats.DmgTeam,
sResponse.KAST = iStats.Kast Enemy: iStats.DmgEnemy,
sResponse.Dmg.Team = iStats.DmgTeam UD: &utils.UD{
sResponse.Dmg.Enemy = iStats.DmgEnemy HE: iStats.UdHe,
sResponse.Dmg.UD.HE = iStats.UdHe Smoke: iStats.UdSmoke,
sResponse.Dmg.UD.Smoke = iStats.UdSmoke Flash: iStats.UdFlash,
sResponse.Dmg.UD.Flash = iStats.UdFlash Decoy: iStats.UdDecoy,
sResponse.Dmg.UD.Decoy = iStats.UdDecoy Flames: iStats.UdFlames,
sResponse.Dmg.UD.Flames = iStats.UdFlames },
sResponse.Dmg.HitGroup.Gear = iStats.HitGroupGear HitGroup: &utils.HitGroup{
sResponse.Dmg.HitGroup.LeftLeg = iStats.HitGroupLeftLeg Gear: iStats.HitGroupGear,
sResponse.Dmg.HitGroup.RightLeg = iStats.HitGroupRightLeg LeftLeg: iStats.HitGroupLeftLeg,
sResponse.Dmg.HitGroup.RightArm = iStats.HitGroupRightArm RightLeg: iStats.HitGroupRightLeg,
sResponse.Dmg.HitGroup.LeftArm = iStats.HitGroupLeftArm RightArm: iStats.HitGroupRightArm,
sResponse.Dmg.HitGroup.Stomach = iStats.HitGroupStomach LeftArm: iStats.HitGroupLeftArm,
sResponse.Dmg.HitGroup.Chest = iStats.HitGroupChest Stomach: iStats.HitGroupStomach,
sResponse.Dmg.HitGroup.Head = iStats.HitGroupHead Chest: iStats.HitGroupChest,
sResponse.Rank.Old = iStats.RankOld Head: iStats.HitGroupHead,
sResponse.Rank.New = iStats.RankNew },
sResponse.Flash.Total.Enemy = iStats.FlashTotalEnemy },
sResponse.Flash.Total.Team = iStats.FlashTotalTeam Color: iStats.Color.String(),
sResponse.Flash.Total.Self = iStats.FlashTotalSelf Crosshair: iStats.Crosshair,
sResponse.Flash.Duration.Enemy = iStats.FlashDurationEnemy KAST: iStats.Kast,
sResponse.Flash.Duration.Team = iStats.FlashDurationTeam Rank: &utils.Rank{
sResponse.Flash.Duration.Self = iStats.FlashDurationSelf Old: iStats.RankOld,
sResponse.MultiKills.Duo = iStats.Mk2 New: iStats.RankNew,
sResponse.MultiKills.Triple = iStats.Mk3 },
sResponse.MultiKills.Quad = iStats.Mk4 Flash: &utils.Flash{
sResponse.MultiKills.Pent = iStats.Mk5 Total: &utils.SelfTeamEnemy{
Enemy: iStats.FlashTotalEnemy,
Team: iStats.FlashTotalTeam,
Self: iStats.FlashTotalSelf,
},
Duration: &utils.SelfTeamEnemy{
Enemy: iStats.FlashDurationEnemy,
Team: iStats.FlashDurationTeam,
Self: iStats.FlashDurationSelf,
},
},
MultiKills: &utils.MultiKills{
Duo: iStats.Mk2,
Triple: iStats.Mk3,
Quad: iStats.Mk4,
Pent: iStats.Mk5,
},
}
if !iStats.Edges.Players.VacDate.IsZero() { if !iStats.Edges.Players.VacDate.IsZero() {
switch s := sResponse.Player.(type) { switch s := sResponse.Player.(type) {
@@ -622,6 +657,7 @@ func main() {
router.HandleFunc("/player/trackme", postPlayerTrackMe).Methods(http.MethodPost, http.MethodOptions) router.HandleFunc("/player/trackme", postPlayerTrackMe).Methods(http.MethodPost, http.MethodOptions)
router.HandleFunc("/match/parse/{sharecode}", getMatchParse).Methods(http.MethodGet, http.MethodOptions) router.HandleFunc("/match/parse/{sharecode}", getMatchParse).Methods(http.MethodGet, http.MethodOptions)
router.HandleFunc("/match/{id:[0-9]{19}}", getMatch).Methods(http.MethodGet, http.MethodOptions) router.HandleFunc("/match/{id:[0-9]{19}}", getMatch).Methods(http.MethodGet, http.MethodOptions)
router.HandleFunc("/match/{id:[0-9]{19}}/weapons", getMatchWeapons).Methods(http.MethodGet, http.MethodOptions)
router.Use(mux.CORSMethodMiddleware(router)) router.Use(mux.CORSMethodMiddleware(router))
loggedRouter := handlers.LoggingHandler(os.Stdout, router) loggedRouter := handlers.LoggingHandler(os.Stdout, router)
proxyRouter := handlers.ProxyHeaders(loggedRouter) proxyRouter := handlers.ProxyHeaders(loggedRouter)

View File

@@ -83,6 +83,73 @@ type MatchStats struct {
Loss int `json:"loss,omitempty"` Loss int `json:"loss,omitempty"`
} }
type MultiKills struct {
Duo uint `json:"duo,omitempty"`
Triple uint `json:"triple,omitempty"`
Quad uint `json:"quad,omitempty"`
Pent uint `json:"pent,omitempty"`
}
type Rank struct {
Old int `json:"old,omitempty"`
New int `json:"new,omitempty"`
}
type HitGroup struct {
Head uint `json:"head,omitempty"`
Chest uint `json:"chest,omitempty"`
Stomach uint `json:"stomach,omitempty"`
LeftArm uint `json:"left_arm,omitempty"`
RightArm uint `json:"right_arm,omitempty"`
LeftLeg uint `json:"left_leg,omitempty"`
RightLeg uint `json:"right_leg,omitempty"`
Gear uint `json:"gear,omitempty"`
}
type UD struct {
HE uint `json:"he,omitempty"`
Flames uint `json:"flames,omitempty"`
Flash uint `json:"flash,omitempty"`
Decoy uint `json:"decoy,omitempty"`
Smoke uint `json:"smoke,omitempty"`
}
type Damage struct {
Enemy uint `json:"enemy,omitempty"`
Team uint `json:"team,omitempty"`
UD *UD `json:"ud,omitempty"`
HitGroup *HitGroup `json:"hit_group,omitempty"`
}
type SelfTeamEnemy struct {
Self interface{} `json:"self,omitempty"`
Team interface{} `json:"team,omitempty"`
Enemy interface{} `json:"enemy,omitempty"`
}
type Flash struct {
Duration *SelfTeamEnemy `json:"duration,omitempty"`
Total *SelfTeamEnemy `json:"total,omitempty"`
}
type StatsResponse struct {
TeamID int `json:"team_id"`
Kills int `json:"kills"`
Deaths int `json:"deaths"`
Assists int `json:"assists"`
Headshot int `json:"headshot"`
MVP uint `json:"mvp"`
Score int `json:"score"`
Player interface{} `json:"player,omitempty"`
Rank *Rank `json:"rank,omitempty"`
MultiKills *MultiKills `json:"multi_kills,omitempty"`
Dmg *Damage `json:"dmg,omitempty"`
Flash *Flash `json:"flash,omitempty"`
Crosshair string `json:"crosshair,omitempty"`
Color string `json:"color,omitempty"`
KAST int `json:"kast,omitempty"`
}
const ( const (
steamID64Entry = "https://steamcommunity.com/profiles/%d?xml=1" steamID64Entry = "https://steamcommunity.com/profiles/%d?xml=1"
steamVanityURLEntry = "https://steamcommunity.com/id/%s?xml=1" steamVanityURLEntry = "https://steamcommunity.com/id/%s?xml=1"