Skip to content

Commit

Permalink
Create demostats package. Remove some legacy stats functionality.
Browse files Browse the repository at this point in the history
  • Loading branch information
leighmacdonald committed Jan 10, 2025
1 parent d153ab1 commit 470b2b7
Show file tree
Hide file tree
Showing 9 changed files with 4,734 additions and 254 deletions.
81 changes: 7 additions & 74 deletions internal/demo/demo_usecase.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
package demo

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"mime/multipart"
"net/http"
"os"
"strings"
"time"

"github.com/dustin/go-humanize"
"github.com/gin-gonic/gin"
"github.com/leighmacdonald/gbans/internal/domain"
"github.com/leighmacdonald/gbans/pkg/demostats"
"github.com/leighmacdonald/gbans/pkg/fs"
"github.com/leighmacdonald/gbans/pkg/log"
"github.com/ricochet2200/go-disk-usage/du"
Expand Down Expand Up @@ -205,69 +200,6 @@ func (d demoUsecase) GetDemos(ctx context.Context) ([]domain.DemoFile, error) {
return d.repository.GetDemos(ctx)
}

func (d demoUsecase) SendAndParseDemo(ctx context.Context, path string) (*domain.DemoDetails, error) {
fileHandle, errDF := os.Open(path)
if errDF != nil {
return nil, errors.Join(errDF, domain.ErrDemoLoad)
}

content, errContent := io.ReadAll(fileHandle)
if errContent != nil {
return nil, errors.Join(errDF, domain.ErrDemoLoad)
}

info, errInfo := fileHandle.Stat()
if errInfo != nil {
return nil, errors.Join(errInfo, domain.ErrDemoLoad)
}

log.Closer(fileHandle)

body := new(bytes.Buffer)
writer := multipart.NewWriter(body)

part, errCreate := writer.CreateFormFile("file", info.Name())
if errCreate != nil {
return nil, errors.Join(errCreate, domain.ErrDemoLoad)
}

if _, err := part.Write(content); err != nil {
return nil, errors.Join(errCreate, domain.ErrDemoLoad)
}

if errClose := writer.Close(); errClose != nil {
return nil, errors.Join(errClose, domain.ErrDemoLoad)
}

req, errReq := http.NewRequestWithContext(ctx, http.MethodPost, d.config.Config().Demo.DemoParserURL, body)
if errReq != nil {
return nil, errors.Join(errReq, domain.ErrDemoLoad)
}
req.Header.Set("Content-Type", writer.FormDataContentType())

client := &http.Client{}
resp, errSend := client.Do(req)
if errSend != nil {
return nil, errors.Join(errSend, domain.ErrDemoLoad)
}

defer resp.Body.Close()

var demo domain.DemoDetails

// TODO remove this extra copy once this feature doesnt have much need for debugging/inspection.
rawBody, errRead := io.ReadAll(resp.Body)
if errRead != nil {
return nil, errors.Join(errRead, domain.ErrDemoLoad)
}

if errDecode := json.NewDecoder(bytes.NewReader(rawBody)).Decode(&demo); errDecode != nil {
return nil, errors.Join(errDecode, domain.ErrDemoLoad)
}

return &demo, nil
}

func (d demoUsecase) CreateFromAsset(ctx context.Context, asset domain.Asset, serverID int) (*domain.DemoFile, error) {
_, errGetServer := d.servers.Server(ctx, serverID)
if errGetServer != nil {
Expand All @@ -290,13 +222,14 @@ func (d demoUsecase) CreateFromAsset(ctx context.Context, asset domain.Asset, se
// TODO change this data shape as we have not needed this in a long time. Only keys the are used.
intStats := map[string]gin.H{}

demoDetail, errDetail := d.SendAndParseDemo(ctx, asset.LocalPath)
if errDetail != nil {
return nil, errDetail
demoStats, errStats := demostats.Submit(ctx, d.config.Config().Demo.DemoParserURL, asset.LocalPath)
if errStats != nil {
return nil, errStats
}

for key := range demoDetail.State.Users {
intStats[key] = gin.H{}
for key := range demoStats.PlayerSummaries {
sid := demoStats.PlayerSummaries[key].SteamID
intStats[string(sid)] = gin.H{}
}

timeStr := fmt.Sprintf("%s-%s", namePartsAll[0], namePartsAll[1])
Expand Down
36 changes: 0 additions & 36 deletions internal/domain/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ type DemoUsecase interface {
GetDemos(ctx context.Context) ([]DemoFile, error)
CreateFromAsset(ctx context.Context, asset Asset, serverID int) (*DemoFile, error)
Cleanup(ctx context.Context)
SendAndParseDemo(ctx context.Context, path string) (*DemoDetails, error)
}

type DemoRepository interface {
Expand All @@ -34,11 +33,6 @@ type DemoPlayerStats struct {
Deaths int `json:"deaths"`
}

type DemoMetaData struct {
MapName string `json:"map_name"`
Scores map[string]DemoPlayerStats `json:"scores"`
}

type DemoFile struct {
DemoID int64 `json:"demo_id"`
ServerID int `json:"server_id"`
Expand All @@ -59,33 +53,3 @@ type DemoInfo struct {
Title string
AssetID uuid.UUID
}

type DemoPlayer struct {
Classes struct{} `json:"classes"`
Name string `json:"name"`
UserID int `json:"userId"` //nolint:tagliatelle
SteamID string `json:"steamId"` //nolint:tagliatelle
Team string `json:"team"`
}

type DemoHeader struct {
DemoType string `json:"demo_type"`
Version int `json:"version"`
Protocol int `json:"protocol"`
Server string `json:"server"`
Nick string `json:"nick"`
Map string `json:"map"`
Game string `json:"game"`
Duration float64 `json:"duration"`
Ticks int `json:"ticks"`
Frames int `json:"frames"`
Signon int `json:"signon"`
}

type DemoDetails struct {
State struct {
PlayerSummaries struct{} `json:"player_summaries"`
Users map[string]DemoPlayer `json:"users"`
} `json:"state"`
Header DemoHeader `json:"header"`
}
8 changes: 2 additions & 6 deletions internal/domain/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"time"

"github.com/gofrs/uuid/v5"
"github.com/leighmacdonald/gbans/pkg/demostats"
"github.com/leighmacdonald/gbans/pkg/fp"
"github.com/leighmacdonald/gbans/pkg/logparse"
"github.com/leighmacdonald/steamid/v4/steamid"
Expand All @@ -29,9 +30,6 @@ type MatchTrigger struct {
}

type MatchRepository interface {
Start(ctx context.Context)
StartMatch(startTrigger MatchTrigger)
EndMatch(endTrigger MatchTrigger)
Matches(ctx context.Context, opts MatchesQueryOpts) ([]MatchSummary, int64, error)
MatchGetByID(ctx context.Context, matchID uuid.UUID, match *MatchResult) error
MatchSave(ctx context.Context, match *logparse.Match, weaponMap fp.MutexMap[logparse.Weapon, int]) error
Expand All @@ -56,12 +54,10 @@ type MatchRepository interface {
GetMatchIDFromServerID(serverID int) (uuid.UUID, bool)
}
type MatchUsecase interface {
StartMatch(server Server, mapName string, demoName string) (uuid.UUID, error)
EndMatch(ctx context.Context, serverID int) (uuid.UUID, error)
GetMatchIDFromServerID(serverID int) (uuid.UUID, bool)
Matches(ctx context.Context, opts MatchesQueryOpts) ([]MatchSummary, int64, error)
MatchGetByID(ctx context.Context, matchID uuid.UUID, match *MatchResult) error
MatchSave(ctx context.Context, match *logparse.Match, weaponMap fp.MutexMap[logparse.Weapon, int]) error
MatchSaveFromDemo(ctx context.Context, demo demostats.Stats, weaponMap fp.MutexMap[logparse.Weapon, int]) (logparse.Match, error)
StatsPlayerClass(ctx context.Context, sid64 steamid.SteamID) (PlayerClassStatsCollection, error)
StatsPlayerWeapons(ctx context.Context, sid64 steamid.SteamID) ([]PlayerWeaponStats, error)
StatsPlayerKillstreaks(ctx context.Context, sid64 steamid.SteamID) ([]PlayerKillstreakStats, error)
Expand Down
15 changes: 0 additions & 15 deletions internal/match/match_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ type matchRepository struct {
notifications domain.NotificationUsecase
servers domain.ServersUsecase
state domain.StateUsecase
summarizer *Summarizer
wm fp.MutexMap[logparse.Weapon, int]
events chan logparse.ServerEvent
broadcaster *fp.Broadcaster[logparse.EventType, logparse.ServerEvent]
Expand All @@ -44,19 +43,9 @@ func NewMatchRepository(broadcaster *fp.Broadcaster[logparse.EventType, logparse
events: make(chan logparse.ServerEvent),
}

matchRepo.summarizer = newMatchSummarizer(matchRepo.events, matchRepo.onMatchComplete)

return matchRepo
}

func (r *matchRepository) StartMatch(startTrigger domain.MatchTrigger) {
r.summarizer.triggers <- startTrigger
}

func (r *matchRepository) EndMatch(endTrigger domain.MatchTrigger) {
r.summarizer.triggers <- endTrigger
}

func (r *matchRepository) onMatchComplete(ctx context.Context, matchContext *activeMatchContext) error {

Check failure on line 49 in internal/match/match_repository.go

View workflow job for this annotation

GitHub Actions / staticcheck

func (*matchRepository).onMatchComplete is unused (U1000)
const minPlayers = 6

Expand Down Expand Up @@ -103,10 +92,6 @@ func (r *matchRepository) onMatchComplete(ctx context.Context, matchContext *act
return nil
}

func (r *matchRepository) Start(ctx context.Context) {
r.summarizer.Start(ctx)
}

func (r *matchRepository) GetMatchIDFromServerID(serverID int) (uuid.UUID, bool) {
return r.matchUUIDMap.Get(serverID)
}
Expand Down
73 changes: 0 additions & 73 deletions internal/match/match_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"time"

"github.com/gin-gonic/gin"
"github.com/gofrs/uuid/v5"
"github.com/leighmacdonald/gbans/internal/domain"
"github.com/leighmacdonald/gbans/internal/httphelper"
"github.com/leighmacdonald/gbans/pkg/log"
Expand Down Expand Up @@ -41,78 +40,6 @@ func NewMatchHandler(ctx context.Context, engine *gin.Engine, matches domain.Mat
authed.GET("/api/stats/player/:steam_id/weapons", handler.onAPIGetPlayerWeaponStatsOverall())
authed.GET("/api/stats/player/:steam_id/classes", handler.onAPIGetPlayerClassStatsOverall())
authed.GET("/api/stats/player/:steam_id/overall", handler.onAPIGetPlayerStatsOverall())
authed.POST("/api/sm/match/start", handler.onAPIPostMatchStart())
authed.GET("/api/sm/match/end", handler.onAPIPostMatchEnd())
}
}

func (h matchHandler) onAPIPostMatchEnd() gin.HandlerFunc {
type endMatchResponse struct {
URL string `json:"url"`
}

return func(ctx *gin.Context) {
serverID, errServerID := httphelper.GetIntParam(ctx, "server_id")
if errServerID != nil {
httphelper.HandleErrInternal(ctx)
slog.Warn("Failed to get server_id", log.ErrAttr(errServerID))

return
}

matchUUID, errEnd := h.matches.EndMatch(ctx, serverID)
if errEnd != nil {
httphelper.ResponseAPIErr(ctx, http.StatusInternalServerError, domain.ErrUnknownServerID)
slog.Error("Failed to end match", log.ErrAttr(errEnd))

return
}

ctx.JSON(http.StatusOK, endMatchResponse{URL: h.config.ExtURLRaw("/match/%s", matchUUID.String())})
}
}

func (h matchHandler) onAPIPostMatchStart() gin.HandlerFunc {
type matchStartRequest struct {
MapName string `json:"map_name"`
DemoName string `json:"demo_name"`
}

type matchStartResponse struct {
MatchID uuid.UUID `json:"match_id"`
}

return func(ctx *gin.Context) {
var req matchStartRequest
if !httphelper.Bind(ctx, &req) {
return
}

serverID, errServerID := httphelper.GetIntParam(ctx, "server_id")
if errServerID != nil {
httphelper.ResponseAPIErr(ctx, http.StatusInternalServerError, domain.ErrUnknownServerID)
slog.Warn("Failed to get server_id", log.ErrAttr(errServerID))

return
}

server, errServer := h.servers.Server(ctx, serverID)
if errServer != nil {
httphelper.ResponseAPIErr(ctx, http.StatusInternalServerError, domain.ErrUnknownServerID)
slog.Error("Failed to get server", log.ErrAttr(errServer))

return
}

matchUUID, errMatch := h.matches.StartMatch(server, req.MapName, req.DemoName)
if errMatch != nil {
httphelper.ResponseAPIErr(ctx, http.StatusInternalServerError, domain.ErrUnknownServerID)
slog.Error("Failed to start match", log.ErrAttr(errMatch))

return
}

ctx.JSON(http.StatusOK, matchStartResponse{MatchID: matchUUID})
}
}

Expand Down
Loading

0 comments on commit 470b2b7

Please sign in to comment.