From ff98047caab81efc1d64aa354e43515d11a38432 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 17 Mar 2024 13:18:51 -0400 Subject: [PATCH] Add daily run rankings --- api/daily.go | 19 +++ api/generic.go | 2 + api/savedata-helper.go | 11 +- api/savedata.go | 7 +- db/daily.go | 27 +++ defs/daily.go | 7 + api/savedata-defs.go => defs/savedata.go | 206 +++++++++++------------ 7 files changed, 168 insertions(+), 111 deletions(-) create mode 100644 defs/daily.go rename api/savedata-defs.go => defs/savedata.go (96%) diff --git a/api/daily.go b/api/daily.go index c275a46..ceb422d 100644 --- a/api/daily.go +++ b/api/daily.go @@ -2,6 +2,8 @@ package api import ( "encoding/base64" + "encoding/json" + "fmt" "log" "net/http" "time" @@ -34,3 +36,20 @@ func InitDailyRun() { func (s *Server) HandleSeed(w http.ResponseWriter, r *http.Request) { w.Write([]byte(dailyRunSeed)) } + +// /daily/rankings - fetch daily rankings + +func (s *Server) HandleRankings(w http.ResponseWriter, r *http.Request) { + rankings, err := db.GetRankings() + if err != nil { + log.Print("failed to retrieve rankings") + } + + response, err := json.Marshal(rankings) + if err != nil { + http.Error(w, fmt.Sprintf("failed to marshal response json: %s", err), http.StatusInternalServerError) + return + } + + w.Write(response) +} diff --git a/api/generic.go b/api/generic.go index 7a9cce6..0879eaf 100644 --- a/api/generic.go +++ b/api/generic.go @@ -52,6 +52,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { case "/daily/seed": s.HandleSeed(w, r) + case "/daily/rankings": + s.HandleRankings(w, r) } } diff --git a/api/savedata-helper.go b/api/savedata-helper.go index 8c11381..182631d 100644 --- a/api/savedata-helper.go +++ b/api/savedata-helper.go @@ -8,11 +8,12 @@ import ( "os" "strconv" + "github.com/Flashfyre/pokerogue-server/defs" "github.com/klauspost/compress/zstd" ) -func GetSystemSaveData(uuid []byte) (SystemSaveData, error) { - var system SystemSaveData +func GetSystemSaveData(uuid []byte) (defs.SystemSaveData, error) { + var system defs.SystemSaveData save, err := os.ReadFile("userdata/" + hex.EncodeToString(uuid) + "/system.pzs") if err != nil { @@ -39,8 +40,8 @@ func GetSystemSaveData(uuid []byte) (SystemSaveData, error) { return system, nil } -func GetSessionSaveData(uuid []byte, slotId int) (SessionSaveData, error) { - var session SessionSaveData +func GetSessionSaveData(uuid []byte, slotId int) (defs.SessionSaveData, error) { + var session defs.SessionSaveData fileName := "session" if slotId != 0 { @@ -72,7 +73,7 @@ func GetSessionSaveData(uuid []byte, slotId int) (SessionSaveData, error) { return session, nil } -func ValidateSessionCompleted(session SessionSaveData) bool { +func ValidateSessionCompleted(session defs.SessionSaveData) bool { switch session.GameMode { case 0: return session.WaveIndex == 200 diff --git a/api/savedata.go b/api/savedata.go index 400d194..e0272a4 100644 --- a/api/savedata.go +++ b/api/savedata.go @@ -12,6 +12,7 @@ import ( "strconv" "github.com/Flashfyre/pokerogue-server/db" + "github.com/Flashfyre/pokerogue-server/defs" "github.com/klauspost/compress/zstd" ) @@ -90,7 +91,7 @@ func (s *Server) HandleSavedataUpdate(w http.ResponseWriter, r *http.Request) { switch r.URL.Query().Get("datatype") { case "0": // System - var system SystemSaveData + var system defs.SystemSaveData err = json.NewDecoder(r.Body).Decode(&system) if err != nil { http.Error(w, fmt.Sprintf("failed to decode request body: %s", err), http.StatusBadRequest) @@ -145,7 +146,7 @@ func (s *Server) HandleSavedataUpdate(w http.ResponseWriter, r *http.Request) { fileName += strconv.Itoa(slotId) } - var session SessionSaveData + var session defs.SessionSaveData err = json.NewDecoder(r.Body).Decode(&session) if err != nil { http.Error(w, fmt.Sprintf("failed to decode request body: %s", err), http.StatusBadRequest) @@ -268,7 +269,7 @@ func (s *Server) HandleSavedataClear(w http.ResponseWriter, r *http.Request) { return } - var session SessionSaveData + var session defs.SessionSaveData err = json.NewDecoder(r.Body).Decode(&session) if err != nil { http.Error(w, fmt.Sprintf("failed to decode request body: %s", err), http.StatusBadRequest) diff --git a/db/daily.go b/db/daily.go index 6d8506a..1372649 100644 --- a/db/daily.go +++ b/db/daily.go @@ -1,5 +1,9 @@ package db +import ( + "github.com/Flashfyre/pokerogue-server/defs" +) + func TryAddDailyRun(seed string) error { _, err := handle.Exec("INSERT INTO dailyRuns (seed, date) VALUES (?, UTC_DATE()) ON DUPLICATE KEY UPDATE date = date", seed) if err != nil { @@ -8,3 +12,26 @@ func TryAddDailyRun(seed string) error { return nil } + +func GetRankings() ([]defs.DailyRanking, error) { + var rankings []defs.DailyRanking + + results, err := handle.Query("SELECT RANK() OVER (ORDER BY sc.score DESC, sc.timestamp), a.username, sc.score FROM seedCompletions sc JOIN dailyRuns dr ON dr.seed = sc.seed JOIN accounts a ON sc.uuid = a.uuid WHERE dr.date = UTC_DATE()") + if err != nil { + return rankings, err + } + + defer results.Close() + + for results.Next() { + ranking := defs.DailyRanking{} + err = results.Scan(&ranking.Rank, &ranking.Username, &ranking.Score) + if err != nil { + return rankings, err + } + + rankings = append(rankings, ranking) + } + + return rankings, nil +} diff --git a/defs/daily.go b/defs/daily.go new file mode 100644 index 0000000..fa10f46 --- /dev/null +++ b/defs/daily.go @@ -0,0 +1,7 @@ +package defs + +type DailyRanking struct { + Rank int `json:"rank"` + Username string `json:"username"` + Score int `json:"score"` +} diff --git a/api/savedata-defs.go b/defs/savedata.go similarity index 96% rename from api/savedata-defs.go rename to defs/savedata.go index e4ffa7b..7360f76 100644 --- a/api/savedata-defs.go +++ b/defs/savedata.go @@ -1,103 +1,103 @@ -package api - -type SystemSaveData struct { - TrainerId int `json:"trainerId"` - SecretId int `json:"secretId"` - Gender int `json:"gender"` - DexData DexData `json:"dexData"` - StarterMoveData StarterMoveData `json:"starterMoveData"` - StarterEggMoveData StarterEggMoveData `json:"starterEggMoveData"` - GameStats GameStats `json:"gameStats"` - Unlocks Unlocks `json:"unlocks"` - AchvUnlocks AchvUnlocks `json:"achvUnlocks"` - VoucherUnlocks VoucherUnlocks `json:"voucherUnlocks"` - VoucherCounts VoucherCounts `json:"voucherCounts"` - Eggs []EggData `json:"eggs"` - GameVersion string `json:"gameVersion"` - Timestamp int `json:"timestamp"` -} - -type DexData map[int]DexEntry - -type DexEntry struct { - SeenAttr interface{} `json:"seenAttr"` // integer or string - CaughtAttr interface{} `json:"caughtAttr"` // integer or string - NatureAttr int `json:"natureAttr"` - SeenCount int `json:"seenCount"` - CaughtCount int `json:"caughtCount"` - HatchedCount int `json:"hatchedCount"` - Ivs []int `json:"ivs"` -} - -type StarterMoveData map[int]interface{} - -type StarterEggMoveData map[int]int - -type GameStats interface{} - -type Unlocks map[int]bool - -type AchvUnlocks map[string]int - -type VoucherUnlocks map[string]int - -type VoucherCounts map[string]int - -type EggData struct { - Id int `json:"id"` - GachaType GachaType `json:"gachaType"` - HatchWaves int `json:"hatchWaves"` - Timestamp int `json:"timestamp"` -} - -type GachaType int - -type SessionSaveData struct { - Seed string `json:"seed"` - PlayTime int `json:"playTime"` - GameMode GameMode `json:"gameMode"` - Party []PokemonData `json:"party"` - EnemyParty []PokemonData `json:"enemyParty"` - Modifiers []PersistentModifierData `json:"modifiers"` - EnemyModifiers []PersistentModifierData `json:"enemyModifiers"` - Arena ArenaData `json:"arena"` - PokeballCounts PokeballCounts `json:"pokeballCounts"` - Money int `json:"money"` - Score int `json:"score"` - WaveIndex int `json:"waveIndex"` - BattleType BattleType `json:"battleType"` - Trainer TrainerData `json:"trainer"` - GameVersion string `json:"gameVersion"` - Timestamp int `json:"timestamp"` -} - -type GameMode int - -type PokemonData interface{} - -type PersistentModifierData interface{} - -type ArenaData interface{} - -type PokeballCounts map[string]int - -type BattleType int - -type TrainerData interface{} - -type SessionHistoryData struct { - Seed string `json:"seed"` - PlayTime int `json:"playTime"` - Result SessionHistoryResult `json:"sessionHistoryResult"` - GameMode GameMode `json:"gameMode"` - Party []PokemonData `json:"party"` - Modifiers []PersistentModifierData `json:"modifiers"` - Money int `json:"money"` - Score int `json:"score"` - WaveIndex int `json:"waveIndex"` - BattleType BattleType `json:"battleType"` - GameVersion string `json:"gameVersion"` - Timestamp int `json:"timestamp"` -} - -type SessionHistoryResult int +package defs + +type SystemSaveData struct { + TrainerId int `json:"trainerId"` + SecretId int `json:"secretId"` + Gender int `json:"gender"` + DexData DexData `json:"dexData"` + StarterMoveData StarterMoveData `json:"starterMoveData"` + StarterEggMoveData StarterEggMoveData `json:"starterEggMoveData"` + GameStats GameStats `json:"gameStats"` + Unlocks Unlocks `json:"unlocks"` + AchvUnlocks AchvUnlocks `json:"achvUnlocks"` + VoucherUnlocks VoucherUnlocks `json:"voucherUnlocks"` + VoucherCounts VoucherCounts `json:"voucherCounts"` + Eggs []EggData `json:"eggs"` + GameVersion string `json:"gameVersion"` + Timestamp int `json:"timestamp"` +} + +type DexData map[int]DexEntry + +type DexEntry struct { + SeenAttr interface{} `json:"seenAttr"` // integer or string + CaughtAttr interface{} `json:"caughtAttr"` // integer or string + NatureAttr int `json:"natureAttr"` + SeenCount int `json:"seenCount"` + CaughtCount int `json:"caughtCount"` + HatchedCount int `json:"hatchedCount"` + Ivs []int `json:"ivs"` +} + +type StarterMoveData map[int]interface{} + +type StarterEggMoveData map[int]int + +type GameStats interface{} + +type Unlocks map[int]bool + +type AchvUnlocks map[string]int + +type VoucherUnlocks map[string]int + +type VoucherCounts map[string]int + +type EggData struct { + Id int `json:"id"` + GachaType GachaType `json:"gachaType"` + HatchWaves int `json:"hatchWaves"` + Timestamp int `json:"timestamp"` +} + +type GachaType int + +type SessionSaveData struct { + Seed string `json:"seed"` + PlayTime int `json:"playTime"` + GameMode GameMode `json:"gameMode"` + Party []PokemonData `json:"party"` + EnemyParty []PokemonData `json:"enemyParty"` + Modifiers []PersistentModifierData `json:"modifiers"` + EnemyModifiers []PersistentModifierData `json:"enemyModifiers"` + Arena ArenaData `json:"arena"` + PokeballCounts PokeballCounts `json:"pokeballCounts"` + Money int `json:"money"` + Score int `json:"score"` + WaveIndex int `json:"waveIndex"` + BattleType BattleType `json:"battleType"` + Trainer TrainerData `json:"trainer"` + GameVersion string `json:"gameVersion"` + Timestamp int `json:"timestamp"` +} + +type GameMode int + +type PokemonData interface{} + +type PersistentModifierData interface{} + +type ArenaData interface{} + +type PokeballCounts map[string]int + +type BattleType int + +type TrainerData interface{} + +type SessionHistoryData struct { + Seed string `json:"seed"` + PlayTime int `json:"playTime"` + Result SessionHistoryResult `json:"sessionHistoryResult"` + GameMode GameMode `json:"gameMode"` + Party []PokemonData `json:"party"` + Modifiers []PersistentModifierData `json:"modifiers"` + Money int `json:"money"` + Score int `json:"score"` + WaveIndex int `json:"waveIndex"` + BattleType BattleType `json:"battleType"` + GameVersion string `json:"gameVersion"` + Timestamp int `json:"timestamp"` +} + +type SessionHistoryResult int