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 {
if tMatchPlayer.Color == "" {
tMatchPlayer.Color = stats.ColorGrey
}
p.lock.Lock()
nMatchPLayer, err := tMatchPlayer.Update().
SetDmgTeam(tMatchPlayer.DmgTeam).

236
main.go
View File

@@ -57,6 +57,18 @@ type PlayerResponse struct {
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 {
MatchId uint64 `json:"match_id,string"`
ShareCode string `json:"share_code,omitempty"`
@@ -70,63 +82,6 @@ type MatchResponse struct {
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() {
for {
if !firstHK {
@@ -301,7 +256,7 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
continue
}
sResponse := &StatsResponse{
sResponse := &utils.StatsResponse{
TeamID: tStats.TeamID,
Kills: tStats.Kills,
Deaths: tStats.Deaths,
@@ -311,16 +266,22 @@ func getPlayer(w http.ResponseWriter, r *http.Request) {
Score: tStats.Score,
}
sResponse.MultiKills.Duo = tStats.Mk2
sResponse.MultiKills.Triple = tStats.Mk3
sResponse.MultiKills.Quad = tStats.Mk4
sResponse.MultiKills.Pent = tStats.Mk5
sResponse.MultiKills = &utils.MultiKills{
Duo: tStats.Mk2,
Triple: tStats.Mk3,
Quad: tStats.Mk4,
Pent: tStats.Mk5,
}
sResponse.Rank.Old = tStats.RankOld
sResponse.Rank.New = tStats.RankNew
sResponse.Rank = &utils.Rank{
Old: tStats.RankOld,
New: tStats.RankNew,
}
sResponse.Dmg.Enemy = tStats.DmgEnemy
sResponse.Dmg.Team = tStats.DmgTeam
sResponse.Dmg = &utils.Damage{
Enemy: tStats.DmgEnemy,
Team: tStats.DmgTeam,
}
mResponse.Stats = sResponse
response.Matches = append(response.Matches, mResponse)
@@ -409,6 +370,64 @@ func getMatchParse(w http.ResponseWriter, r *http.Request) {
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) {
w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains)
id := mux.Vars(r)["id"]
@@ -444,7 +463,7 @@ func getMatch(w http.ResponseWriter, r *http.Request) {
MatchResult: tMatch.MatchResult,
MaxRounds: tMatch.MaxRounds,
Parsed: tMatch.DemoParsed,
Stats: []*StatsResponse{},
Stats: []*utils.StatsResponse{},
}
db.Lock.RLock()
@@ -456,10 +475,10 @@ func getMatch(w http.ResponseWriter, r *http.Request) {
return
}
tmpStats := make([]*StatsResponse, 0)
tmpStats := make([]*utils.StatsResponse, 0)
for _, iStats := range tStats {
sResponse := &StatsResponse{
sResponse := &utils.StatsResponse{
Player: PlayerResponse{
SteamID64: iStats.Edges.Players.ID,
Name: iStats.Edges.Players.Name,
@@ -475,38 +494,54 @@ func getMatch(w http.ResponseWriter, r *http.Request) {
Headshot: iStats.Headshot,
MVP: iStats.Mvp,
Score: iStats.Score,
}
sResponse.Color = iStats.Color.String()
sResponse.Crosshair = iStats.Crosshair
sResponse.KAST = iStats.Kast
sResponse.Dmg.Team = iStats.DmgTeam
sResponse.Dmg.Enemy = iStats.DmgEnemy
sResponse.Dmg.UD.HE = iStats.UdHe
sResponse.Dmg.UD.Smoke = iStats.UdSmoke
sResponse.Dmg.UD.Flash = iStats.UdFlash
sResponse.Dmg.UD.Decoy = iStats.UdDecoy
sResponse.Dmg.UD.Flames = iStats.UdFlames
sResponse.Dmg.HitGroup.Gear = iStats.HitGroupGear
sResponse.Dmg.HitGroup.LeftLeg = iStats.HitGroupLeftLeg
sResponse.Dmg.HitGroup.RightLeg = iStats.HitGroupRightLeg
sResponse.Dmg.HitGroup.RightArm = iStats.HitGroupRightArm
sResponse.Dmg.HitGroup.LeftArm = iStats.HitGroupLeftArm
sResponse.Dmg.HitGroup.Stomach = iStats.HitGroupStomach
sResponse.Dmg.HitGroup.Chest = iStats.HitGroupChest
sResponse.Dmg.HitGroup.Head = iStats.HitGroupHead
sResponse.Rank.Old = iStats.RankOld
sResponse.Rank.New = iStats.RankNew
sResponse.Flash.Total.Enemy = iStats.FlashTotalEnemy
sResponse.Flash.Total.Team = iStats.FlashTotalTeam
sResponse.Flash.Total.Self = iStats.FlashTotalSelf
sResponse.Flash.Duration.Enemy = iStats.FlashDurationEnemy
sResponse.Flash.Duration.Team = iStats.FlashDurationTeam
sResponse.Flash.Duration.Self = iStats.FlashDurationSelf
sResponse.MultiKills.Duo = iStats.Mk2
sResponse.MultiKills.Triple = iStats.Mk3
sResponse.MultiKills.Quad = iStats.Mk4
sResponse.MultiKills.Pent = iStats.Mk5
Dmg: &utils.Damage{
Team: iStats.DmgTeam,
Enemy: iStats.DmgEnemy,
UD: &utils.UD{
HE: iStats.UdHe,
Smoke: iStats.UdSmoke,
Flash: iStats.UdFlash,
Decoy: iStats.UdDecoy,
Flames: iStats.UdFlames,
},
HitGroup: &utils.HitGroup{
Gear: iStats.HitGroupGear,
LeftLeg: iStats.HitGroupLeftLeg,
RightLeg: iStats.HitGroupRightLeg,
RightArm: iStats.HitGroupRightArm,
LeftArm: iStats.HitGroupLeftArm,
Stomach: iStats.HitGroupStomach,
Chest: iStats.HitGroupChest,
Head: iStats.HitGroupHead,
},
},
Color: iStats.Color.String(),
Crosshair: iStats.Crosshair,
KAST: iStats.Kast,
Rank: &utils.Rank{
Old: iStats.RankOld,
New: iStats.RankNew,
},
Flash: &utils.Flash{
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() {
switch s := sResponse.Player.(type) {
@@ -622,6 +657,7 @@ func main() {
router.HandleFunc("/player/trackme", postPlayerTrackMe).Methods(http.MethodPost, 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}}/weapons", getMatchWeapons).Methods(http.MethodGet, http.MethodOptions)
router.Use(mux.CORSMethodMiddleware(router))
loggedRouter := handlers.LoggingHandler(os.Stdout, router)
proxyRouter := handlers.ProxyHeaders(loggedRouter)

View File

@@ -83,6 +83,73 @@ type MatchStats struct {
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 (
steamID64Entry = "https://steamcommunity.com/profiles/%d?xml=1"
steamVanityURLEntry = "https://steamcommunity.com/id/%s?xml=1"