added weapon-stats endpoint
This commit is contained in:
@@ -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
236
main.go
@@ -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)
|
||||||
|
@@ -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"
|
||||||
|
Reference in New Issue
Block a user