From fa5728fba29210e9c3e29c9c7364cbf8865e95c4 Mon Sep 17 00:00:00 2001 From: MaxJa4 <74194322+MaxJa4@users.noreply.github.com> Date: Tue, 23 Jan 2024 18:40:39 +0100 Subject: [PATCH] Remove game tags and persona/nucleus ids everywhere. Forward score-fetch statuscode. Fix login issue. --- controllers/cache_controller.go | 27 ++-- controllers/player_controller.go | 18 +-- controllers/tracker_controller.go | 165 +++++++++--------------- controllers/user_controller.go | 7 +- controllers/user_settings_controller.go | 28 ---- internal/cache/cache.go | 22 ++-- models/player.go | 10 +- models/tracker_json.go | 7 - templates/modals/add_player.html | 2 +- 9 files changed, 91 insertions(+), 195 deletions(-) diff --git a/controllers/cache_controller.go b/controllers/cache_controller.go index 1204232..f4e2c36 100644 --- a/controllers/cache_controller.go +++ b/controllers/cache_controller.go @@ -21,14 +21,7 @@ type UpdateCacheInput struct { func GetCacheByPlayerID(c *gin.Context) { playerId := utils.StringToUint(c.Param("player_id")) - game, err := GetActiveGame(c) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"}) - utils.Logger.Warnf("[CACHE] No active game available! Error: %s", err.Error()) - return - } - - val, err := models.PlayerCache.GetScore(playerId, game.Tag) + val, err := models.PlayerCache.GetScore(playerId) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"}) utils.Logger.Warnf("[CACHE] Record not found! Error: %s", err.Error()) @@ -54,7 +47,7 @@ func AddCache(c *gin.Context) { return } - err := models.PlayerCache.SetScore(input.PlayerID, input.GameTag, input.Score) + err := models.PlayerCache.SetScore(input.PlayerID, input.Score) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Cache update failed! Error: " + err.Error()}) utils.Logger.Warnf("[CACHE] Cache update failed! Error: %s", err.Error()) @@ -64,13 +57,12 @@ func AddCache(c *gin.Context) { c.JSON(http.StatusOK, nil) } -// UpdateCacheByPlayerID PATCH /cache/:id?game=TAG +// UpdateCacheByPlayerID PATCH /cache/:id func UpdateCacheByPlayerID(c *gin.Context) { playerID := utils.StringToUint(c.Param("id")) - gameTag := c.Request.URL.Query().Get("game") score := utils.StringToFloat(c.PostForm("score")) - err := models.PlayerCache.SetScore(playerID, gameTag, score) + err := models.PlayerCache.SetScore(playerID, score) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Cache update failed! Error: " + err.Error()}) utils.Logger.Warnf("[CACHE] Cache update failed! Error: %s", err.Error()) @@ -79,12 +71,11 @@ func UpdateCacheByPlayerID(c *gin.Context) { } } -// DeleteCacheByPlayerID DELETE /cache/:id?game=TAG +// DeleteCacheByPlayerID DELETE /cache/:id func DeleteCacheByPlayerID(c *gin.Context) { playerID := utils.StringToUint(c.Param("id")) - gameTag := c.Request.URL.Query().Get("game") - if err := models.PlayerCache.DeleteScore(playerID, gameTag); err != nil { + if err := models.PlayerCache.DeleteScore(playerID); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) utils.Logger.Warnf("[CACHE] Cache deletion failed! Error: %s", err.Error()) } else { @@ -121,15 +112,15 @@ func UpdateCacheAfterExpiry(key string) { } if player.Clan.KeepUpdated { - score := CalcPlayerScore(player.Name, "BF2042") + score, statusCode := GetPlayerScore(player.Name) if score == score && score != -1 { // not NaN - if err := models.PlayerCache.SetScore(player.ID, "BF2042", score); err != nil { + if err := models.PlayerCache.SetScore(player.ID, score); err != nil { utils.Logger.Warnf("[KeepUpdated] Failed to update cache for player %s! Error: %s", player.Name, err.Error()) } else { utils.Logger.Infof("[KeepUpdated] Updated cache for player %s", player.Name) } } else { - utils.Logger.Warnf("[KeepUpdated] Failed to calculate score for player %s", player.Name) + utils.Logger.Warnf("[KeepUpdated] Failed to calculate score for player %s! Status code: %d", player.Name, statusCode) } } } diff --git a/controllers/player_controller.go b/controllers/player_controller.go index 164e297..37dd13a 100644 --- a/controllers/player_controller.go +++ b/controllers/player_controller.go @@ -50,19 +50,12 @@ func GetPlayersByClanHTML(c *gin.Context) { return } - game, err := GetActiveGame(c) - if err != nil { - c.String(http.StatusBadRequest, "") - utils.Logger.Errorf("[PLAYER] No active game not found! Error: %s", err.Error()) - return - } - var playerIDs []uint for _, player := range players { playerIDs = append(playerIDs, player.ID) } - scores, err := models.PlayerCache.GetScores(playerIDs, game.Tag) + scores, err := models.PlayerCache.GetScores(playerIDs) if err != nil { c.String(http.StatusBadRequest, "") utils.Logger.Errorf("[PLAYER] Could not get scores! Error: %s", err.Error()) @@ -107,14 +100,7 @@ func AddPlayer(c *gin.Context) { return } - personaID, nucleusID, statusCode := GetPlayerIds(input.Name) - if personaID == 0 || nucleusID == 0 { - c.JSON(statusCode, gin.H{"error": "Player not found!"}) - utils.Logger.Errorf("[PLAYER] Could not find player! Name: %s, Status: %d", input.Name, statusCode) - return - } - - player = models.Player{Name: input.Name, ClanID: input.ClanID, PersonaID: personaID, NucleusID: nucleusID} + player = models.Player{Name: input.Name, ClanID: input.ClanID} if err := models.DB.Create(&player); err.Error != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error.Error()}) utils.Logger.Errorf("[PLAYER] Could not create player! Error: %s", err.Error.Error()) diff --git a/controllers/tracker_controller.go b/controllers/tracker_controller.go index 145e7ae..630e8ed 100644 --- a/controllers/tracker_controller.go +++ b/controllers/tracker_controller.go @@ -22,77 +22,65 @@ func GetScoreByPlayerID(c *gin.Context) { return } - game, err := GetActiveGame(c) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"}) - utils.Logger.Errorf("[SCORE] No active game available: %s", err.Error()) - return - } - - score, err := models.PlayerCache.GetScore(player.ID, game.Tag) + score, err := models.PlayerCache.GetScore(player.ID) + var statusCode int = http.StatusOK if err != nil || score == -1 { - score = CalcPlayerScore(player.Name, game.Tag) + score, statusCode = GetPlayerScore(player.Name) if score == score && score != -1 { // not NaN - if err := models.PlayerCache.SetScore(player.ID, game.Tag, score); err != nil { + if err := models.PlayerCache.SetScore(player.ID, score); err != nil { utils.Logger.Errorf("[SCORE] Failed to cache player score: %s", err.Error()) return } } } - if score != score || score == -1 { // NaN - c.String(http.StatusOK, "") - } else if score != -1 { - c.String(http.StatusOK, fmt.Sprintf("%.2f", score)) - } else { - c.JSON(http.StatusBadRequest, "Invalid request!") - utils.Logger.Warnf("[SCORE] Invalid request! Player: %s, Game: %s | Score: %f", player.Name, game.Tag, score) + switch statusCode { + case 200: + c.String(200, fmt.Sprintf("%.2f", score)) + case 404: + c.String(200, "") + case 503, 504: + c.String(statusCode, "") + default: + c.String(statusCode, "") + utils.Logger.Warnf("[SCORE] Invalid request! Player: %s, Score: %f", player.Name, score) } } // GetScoreByPlayerName POST /score/:player_name func GetScoreByPlayerName(c *gin.Context) { - game, err := GetActiveGame(c) - if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"}) - utils.Logger.Errorf("[SCORE] No active game available: %s", err.Error()) - return - } + playerName := c.Param("player_name") + score, statusCode := GetPlayerScore(playerName) - score := CalcPlayerScore(c.Param("player_name"), game.Tag) - - if score != score || score == -1 { // NaN - c.String(http.StatusNotFound, "Spieler nicht gefunden!") - } else if score != -1 { - c.String(http.StatusOK, fmt.Sprintf("%.2f", score)) - } else { - c.String(http.StatusBadRequest, "Ungültige Abfrage!") - utils.Logger.Warnf("[SCORE] Invalid request! Player: %s, Game: %s | Score: %f", c.Param("player_name"), game.Tag, score) + switch statusCode { + case 200: + c.String(200, fmt.Sprintf("%.2f", score)) + case 404: + c.String(200, "Spieler nicht gefunden!") + case 503, 504: + c.String(statusCode, "Tracker nicht erreichbar!") + default: + c.String(statusCode, "Ungültige Abfrage!") + utils.Logger.Warnf("[SCORE] Invalid request! Player: %s, Score: %f", playerName, score) } } -func CalcPlayerScore(playerName string, gameTag string) float32 { - if gameTag != "BF2042" { - utils.Logger.Errorf("[SCORE] Invalid game tag '%s'", gameTag) - return -1 +func GetPlayerScore(playerName string) (float32, int) { + playerData, statusCode := getPlayerData(playerName) + if statusCode != 200 { + return -1, statusCode } + return calcPlayerScore(playerData), statusCode +} - gameMetrics := GetGameMetric(gameTag) - if gameMetrics == nil { - utils.Logger.Errorf("[SCORE] No game metrics specified for '%s'", gameTag) - return -1 - } - - normalizeFactor := gameMetrics.NormalizeFactor - topWeaponCount := gameMetrics.TopWeaponCount - +func getPlayerData(playerName string) (*models.TrackerWeaponJSON, int) { c := http.Client{} reqUri := "https://api.gametools.network/bf2042/stats/?raw=false&format_values=false&name=" + playerName + "&platform=pc" req, err := http.NewRequest("GET", reqUri, nil) if err != nil { utils.Logger.Errorf("[SCORE] Failed to create request: %s", err.Error()) - return -1 + return nil, 0 } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") @@ -100,39 +88,52 @@ func CalcPlayerScore(playerName string, gameTag string) float32 { res, err := c.Do(req) if err != nil { utils.Logger.Errorf("[SCORE] Failed to send request: %s", err.Error()) - return -1 + return nil, 0 } defer func(Body io.ReadCloser) { _ = Body.Close() }(res.Body) if res.StatusCode == 404 { - utils.Logger.Errorf("[SCORE] User '%s' does not exist!", playerName) - return -1 + utils.Logger.Errorf("[SCORE] User does not exist!") + return nil, res.StatusCode + } else if res.StatusCode == 503 || res.StatusCode == 504 { + utils.Logger.Errorf("[SCORE] Service unavailable!") + return nil, res.StatusCode } else if res.StatusCode != 200 { utils.Logger.Errorf("[SCORE] Status code error: %d %s", res.StatusCode, res.Status) - return -1 + return nil, res.StatusCode } - var data models.TrackerWeaponJSON + var response models.TrackerWeaponJSON body, err := io.ReadAll(res.Body) if err != nil { utils.Logger.Errorf("[SCORE] Failed to read response body: %s", err.Error()) - return -1 + return nil, 0 } - if err := json.Unmarshal(body, &data); err != nil { + if err := json.Unmarshal(body, &response); err != nil { utils.Logger.Errorf("[SCORE] Failed to deserialize tracker API response: %s", err) + return nil, 0 + } + + return &response, 200 +} + +func calcPlayerScore(playerData *models.TrackerWeaponJSON) float32 { + gameMetrics := GetGameMetric("BF2042") + if gameMetrics == nil { + utils.Logger.Errorf("[SCORE] No game metrics specified for '%s'", "BF2042") return -1 } - sort.SliceStable(data.Weapons, func(i, j int) bool { return data.Weapons[i].Kills > data.Weapons[j].Kills }) + normalizeFactor := gameMetrics.NormalizeFactor + topWeaponCount := gameMetrics.TopWeaponCount + + sort.SliceStable(playerData.Weapons, func(i, j int) bool { return playerData.Weapons[i].Kills > playerData.Weapons[j].Kills }) var top []models.Weapon - for _, weapon := range data.Weapons { - if len(top) >= topWeaponCount { - break - } - if gameTag == "BF2042" && weapon.Kills < 100 { + for _, weapon := range playerData.Weapons { + if len(top) >= topWeaponCount || weapon.Kills < 100 { break } @@ -169,49 +170,3 @@ func CalcPlayerScore(playerName string, gameTag string) float32 { return float32(score) } - -// GetPlayerIds Returns: PersonaID, NucleusID, StatusCode -func GetPlayerIds(playerName string) (uint, uint, int) { - c := http.Client{} - - req, err := http.NewRequest("GET", "https://api.gametools.network/bf2042/player/?name="+playerName, nil) - if err != nil { - utils.Logger.Errorf("[TRK-PID] Failed to create request: %s", err.Error()) - return 0, 0, 0 - } - - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36") - - res, err := c.Do(req) - if err != nil { - utils.Logger.Errorf("[TRK-PID] Failed to send request: %s", err.Error()) - return 0, 0, 0 - } - - defer func(Body io.ReadCloser) { - _ = Body.Close() - }(res.Body) - if res.StatusCode == 404 { - utils.Logger.Errorf("[TRK-PID] User '%s' does not exist!", playerName) - return 0, 0, res.StatusCode - } else if res.StatusCode == 503 { - utils.Logger.Errorf("[TRK-PID] Service unavailable!") - return 0, 0, res.StatusCode - } else if res.StatusCode != 200 { - utils.Logger.Errorf("[TRK-PID] Status code error: %d %s", res.StatusCode, res.Status) - return 0, 0, res.StatusCode - } - - var data models.PlayerIDsJSON - body, err := io.ReadAll(res.Body) - if err != nil { - utils.Logger.Errorf("[TRK-PID] Failed to read response body: %s", err.Error()) - return 0, 0, 0 - } - if err := json.Unmarshal(body, &data); err != nil { - utils.Logger.Errorf("[TRK-PID] Failed to deserialize tracker API response: %s", err) - return 0, 0, 0 - } - - return data.Results[0].PersonaID, data.Results[0].NucleusID, 0 -} diff --git a/controllers/user_controller.go b/controllers/user_controller.go index 45bd855..03747dd 100644 --- a/controllers/user_controller.go +++ b/controllers/user_controller.go @@ -53,7 +53,8 @@ func IsUserEnabled(username string) bool { var user models.User if err := models.DB.Where("username = ?", username).First(&user).Error; err != nil { - utils.Logger.Fatalf("[USER] Error while getting user: %s", err.Error()) + utils.Logger.Warnf("[USER] IsUserEnabled: Error while getting user: %s", err.Error()) + return false } return user.Enabled @@ -63,7 +64,7 @@ func GetUserRole(username string) models.Role { var user models.User if err := models.DB.Where("username = ?", username).First(&user).Error; err != nil { - utils.Logger.Fatalf("[USER] Error while getting user: %s", err.Error()) + utils.Logger.Fatalf("[USER] GetUserRole: Error while getting user: %s", err.Error()) } return user.UserRole @@ -83,7 +84,7 @@ func IsUserAdmin(username string) bool { var user models.User if err := models.DB.Where("username = ?", username).First(&user).Error; err != nil { - utils.Logger.Fatalf("[USER] Error while getting user: %s", err.Error()) + utils.Logger.Fatalf("[USER] IsUserAdmin: Error while getting user: %s", err.Error()) } return user.UserRole == models.AdminRole diff --git a/controllers/user_settings_controller.go b/controllers/user_settings_controller.go index 741aaac..e4d4a64 100644 --- a/controllers/user_settings_controller.go +++ b/controllers/user_settings_controller.go @@ -3,7 +3,6 @@ package controllers import ( "InfantrySkillCalculator/models" "InfantrySkillCalculator/utils" - "errors" "github.com/gin-gonic/gin" "net/http" "session" @@ -43,33 +42,6 @@ func GetSettings(c *gin.Context) { c.JSON(http.StatusOK, sanitizedSettings) } -func GetActiveGame(c *gin.Context) (models.Game, error) { - var settings models.UserSettings - var game models.Game - username, ok := session.GetUsername(c) - - if !ok { - err := errors.New("not logged in") - c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - utils.Logger.Errorf("[SETTINGS] User not logged in: %s", username) - return models.Game{}, err - } - - if err := models.DB.Where("username = ?", username).First(&settings).Error; err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "No settings available!"}) - utils.Logger.Errorf("[SETTINGS] No settings available for user %s", username) - return models.Game{}, err - } - - if err := models.DB.Where("id = ?", settings.ActiveGameID).First(&game).Error; err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"}) - utils.Logger.Errorf("[SETTINGS] No active game available for user %s", username) - return models.Game{}, err - } - - return game, nil -} - // UpdateSettings PATCH /settings func UpdateSettings(c *gin.Context) { var settings models.UserSettings diff --git a/internal/cache/cache.go b/internal/cache/cache.go index aabf2a8..c4f9814 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -65,13 +65,13 @@ func (pc *PlayerCache) SetValue(key string, value interface{}, lifetime time.Dur return pc.cache.Set(ctx, key, value, lifetime).Err() } -func (pc *PlayerCache) SetScore(playerId uint, gameTag string, score float32) error { - key := getPlayerCacheKey(playerId, gameTag) +func (pc *PlayerCache) SetScore(playerId uint, score float32) error { + key := getPlayerCacheKey(playerId) return pc.SetValue(key, score, pc.lifetime) } -func (pc *PlayerCache) GetScore(playerId uint, gameTag string) (float32, error) { - key := getPlayerCacheKey(playerId, gameTag) +func (pc *PlayerCache) GetScore(playerId uint) (float32, error) { + key := getPlayerCacheKey(playerId) val, err := pc.GetValue(key) if errors.Is(err, redis.Nil) { return -1.0, nil // cache miss @@ -83,10 +83,10 @@ func (pc *PlayerCache) GetScore(playerId uint, gameTag string) (float32, error) } } -func (pc *PlayerCache) GetScores(playerIds []uint, gameTag string) ([]float32, error) { +func (pc *PlayerCache) GetScores(playerIds []uint) ([]float32, error) { vals, err := pc.cache.Pipelined(ctx, func(pipe redis.Pipeliner) error { for _, id := range playerIds { - key := getPlayerCacheKey(id, gameTag) + key := getPlayerCacheKey(id) pipe.Get(ctx, key) } return nil @@ -111,8 +111,8 @@ func (pc *PlayerCache) GetScores(playerIds []uint, gameTag string) ([]float32, e return scores, nil } -func (pc *PlayerCache) DeleteScore(playerId uint, gameTag string) error { - key := getPlayerCacheKey(playerId, gameTag) +func (pc *PlayerCache) DeleteScore(playerId uint) error { + key := getPlayerCacheKey(playerId) return pc.cache.Del(ctx, key).Err() } @@ -120,12 +120,12 @@ func (pc *PlayerCache) PurgeCache() error { return pc.cache.FlushAll(ctx).Err() } -func getPlayerCacheKey(playerId uint, gameTag string) string { - return fmt.Sprintf("player:%d:game:%s", playerId, gameTag) +func getPlayerCacheKey(playerId uint) string { + return fmt.Sprintf("player:%d", playerId) } func (pc *PlayerCache) GetPlayerIdFromCacheKey(key string) (uint, error) { var playerId uint - _, err := fmt.Sscanf(key, "player:%d:game:", &playerId) + _, err := fmt.Sscanf(key, "player:%d", &playerId) return playerId, err } diff --git a/models/player.go b/models/player.go index 37aaf41..e4bebb9 100644 --- a/models/player.go +++ b/models/player.go @@ -1,10 +1,8 @@ package models type Player struct { - ID uint `json:"id" gorm:"primary_key"` - Name string `json:"name" gorm:"not null"` - PersonaID uint `json:"persona_id" gorm:"not null"` - NucleusID uint `json:"nucleus_id" gorm:"not null"` - ClanID uint `json:"clan_id" gorm:"not null"` - Clan Clan `gorm:"references:ID"` + ID uint `json:"id" gorm:"primary_key"` + Name string `json:"name" gorm:"not null"` + ClanID uint `json:"clan_id" gorm:"not null"` + Clan Clan `gorm:"references:ID"` } diff --git a/models/tracker_json.go b/models/tracker_json.go index 5f806c4..4e9ec9d 100644 --- a/models/tracker_json.go +++ b/models/tracker_json.go @@ -14,10 +14,3 @@ type Weapon struct { ShotsHit int `json:"shotsHit"` Accuracy float64 `json:"-"` } - -type PlayerIDsJSON struct { - Results []struct { - PersonaID uint `json:"personaId"` - NucleusID uint `json:"nucleusId"` - } `json:"results"` -} diff --git a/templates/modals/add_player.html b/templates/modals/add_player.html index 28d96be..d883d4f 100644 --- a/templates/modals/add_player.html +++ b/templates/modals/add_player.html @@ -75,7 +75,7 @@ .then(response => { if (response.status === 404) { throw new Error('Hinzufügen fehlgeschlagen!\nSpielername existiert nicht.'); - } else if (response.status === 503) { + } else if (response.status === 503 || response.status === 504) { throw new Error('Hinzufügen fehlgeschlagen!\nTracker überlastet. Versuche es später erneut.'); } else if (!response.ok) { throw new Error('Hinzufügen fehlgeschlagen!\nSpielername existiert möglicherweise bereits.');