diff --git a/main.go b/main.go index 8f89f23..3599cc5 100644 --- a/main.go +++ b/main.go @@ -17,6 +17,7 @@ import ( "github.com/gorilla/handlers" "github.com/gorilla/mux" _ "github.com/lib/pq" + "github.com/markus-wa/demoinfocs-golang/v2/pkg/demoinfocs/common" _ "github.com/mattn/go-sqlite3" log "github.com/sirupsen/logrus" "github.com/wercker/journalhook" @@ -409,6 +410,58 @@ func getMatchParse(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusAccepted) } +func getMatchRounds(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.Infof("[GMR] Error parsing matchID %s: %v", id, err) + w.WriteHeader(http.StatusBadRequest) + return + } + + 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.Infof("[GMR] match %d not found: %+v", matchId, err) + w.WriteHeader(http.StatusNotFound) + return + } + + resp := map[uint]map[string][]uint{} + + for _, stat := range tStats { + db.Lock.RLock() + tRoundStats, err := stat.QueryRoundStats().All(context.Background()) + db.Lock.RUnlock() + if err != nil { + log.Warningf("[GMR] Unable to get RoundStats for player %d: %v", stat.PlayerStats, err) + continue + } + + for _, rStat := range tRoundStats { + if _, ok := resp[rStat.Round]; !ok { + resp[rStat.Round] = map[string][]uint{} + } + + resp[rStat.Round][strconv.FormatUint(stat.PlayerStats, 10)] = []uint{rStat.Equipment, rStat.Spent, rStat.Bank} + } + } + + err = utils.SendJSON(resp, w) + if err != nil { + log.Errorf("[GMR] JSON: %+v", err) + w.WriteHeader(http.StatusInternalServerError) + } +} + func getMatchWeapons(w http.ResponseWriter, r *http.Request) { w.Header().Set("Access-Control-Allow-Origin", conf.Httpd.CORSAllowDomains) id := mux.Vars(r)["id"] @@ -425,8 +478,6 @@ func getMatchWeapons(w http.ResponseWriter, r *http.Request) { return } - mResponse := make([]*utils.WeaponResponse, 0) - db.Lock.RLock() tStats, err := db.Client.Stats.Query().Where(stats.HasMatchesWith(match.ID(matchId))).All(context.Background()) db.Lock.RUnlock() @@ -436,28 +487,39 @@ func getMatchWeapons(w http.ResponseWriter, r *http.Request) { return } + mResponse := struct { + EquipmentMap map[int]string `json:"equipment_map,omitempty"` + Stats []map[string]map[string][][]int `json:"stats,omitempty"` + }{ + EquipmentMap: map[int]string{}, + Stats: []map[string]map[string][][]int{}, + } + 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) + log.Warningf("[GMW] Unable to get WeaponStats for player %d: %v", stat.PlayerStats, err) continue } - mWr := &utils.WeaponResponse{ - Player: &utils.PlayerResponse{SteamID64: stat.PlayerStats}, - } + mWr := map[string]map[string][][]int{} + playerId := strconv.FormatUint(stat.PlayerStats, 10) for _, wr := range mWs { - mWr.Eq = append(mWr.Eq, &utils.EqResponse{ - Victim: wr.Victim, - Type: wr.EqType, - HitGroup: wr.HitGroup, - Dmg: wr.Dmg, - }) + if _, exists := mWr[strconv.FormatUint(stat.PlayerStats, 10)]; !exists { + mWr[playerId] = map[string][][]int{} + } + + victim := strconv.FormatUint(wr.Victim, 10) + mWr[playerId][victim] = append(mWr[playerId][victim], []int{wr.EqType, wr.HitGroup, int(wr.Dmg)}) + + if _, exist := mResponse.EquipmentMap[wr.EqType]; !exist { + mResponse.EquipmentMap[wr.EqType] = common.EquipmentType(wr.EqType).String() + } } - mResponse = append(mResponse, mWr) + mResponse.Stats = append(mResponse.Stats, mWr) } err = utils.SendJSON(mResponse, w) @@ -478,7 +540,7 @@ func getMatch(w http.ResponseWriter, r *http.Request) { matchId, err := strconv.ParseUint(id, 10, 64) if err != nil { - log.Infof("[GM] Unbale to parse matchID %s: %v", id, err) + log.Infof("[GM] Unable to parse matchID %s: %v", id, err) w.WriteHeader(http.StatusBadRequest) return } @@ -601,9 +663,12 @@ func getMatch(w http.ResponseWriter, r *http.Request) { } /* -/player/ GET player internal or if not found: steamAPI data + overall stats -/player/trackme POST id, authcode, [sharecode] -/match/ GET CSGO-GC response + internal data if parsed <- may be big (ALL RELEVANT DATA) +/player/ GET player details (last 10 matches) +/player//track POST Track player FORM_DATA: authcode, [sharecode] +/player//track DELETE Stop tracking player FORM_DATA: authcode +/match/ GET details for match +/match//weapons GET weapon-stats for match +/match//rounds GET round-stats for match /match/parse/ GET parses sharecode provided */ func main() { @@ -685,7 +750,7 @@ func main() { // start housekeeper go housekeeping() - // Define routes + // routes router = mux.NewRouter().StrictSlash(true) router.HandleFunc("/player/{id}", getPlayer).Methods(http.MethodGet, http.MethodOptions) router.HandleFunc("/player/{id}/track", postPlayerTrack).Methods(http.MethodPost, http.MethodOptions) @@ -693,6 +758,7 @@ func main() { 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.HandleFunc("/match/{id:[0-9]{19}}/rounds", getMatchRounds).Methods(http.MethodGet, http.MethodOptions) router.Use(mux.CORSMethodMiddleware(router)) loggedRouter := handlers.LoggingHandler(os.Stdout, router) proxyRouter := handlers.ProxyHeaders(loggedRouter) diff --git a/utils/utils.go b/utils/utils.go index 43cae35..fa45248 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -153,26 +153,19 @@ type StatsResponse struct { type PlayerResponse struct { SteamID64 uint64 `json:"steamid64,string"` - Name string `json:"name"` - Avatar string `json:"avatar"` + Name string `json:"name,omitempty"` + Avatar string `json:"avatar,omitempty"` VAC bool `json:"vac"` VACDate *time.Time `json:"vac_date,omitempty"` Tracked bool `json:"tracked"` VanityURL string `json:"vanity_url,omitempty"` - MatchStats MatchStats `json:"match_stats,omitempty"` + MatchStats *MatchStats `json:"match_stats,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"` + Player *PlayerResponse `json:"player"` + Eq map[string][][]int `json:"eq,omitempty"` } type MatchResponse struct {