From 3576d02c656acee4515f85f77972147efce439cd Mon Sep 17 00:00:00 2001 From: petap0w Date: Thu, 27 Mar 2025 20:34:46 +0100 Subject: [PATCH 1/3] Addition of live stats api endpoint --- decoder/api_pokemon.go | 35 +++++++++++++++++++++++++++++++++++ decoder/pokemonRtree.go | 4 +++- main.go | 1 + routes.go | 5 +++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/decoder/api_pokemon.go b/decoder/api_pokemon.go index 8117f052..3161b9c0 100644 --- a/decoder/api_pokemon.go +++ b/decoder/api_pokemon.go @@ -157,3 +157,38 @@ func GetOnePokemon(pokemonId uint64) *Pokemon { } return nil } + +type ApiPokemonLiveStatsResult struct { + PokemonActive int `json:"pokemon_active"` + PokemonActiveIv int `json:"pokemon_active_iv"` + PokemonActive100iv int `json:"pokemon_active_100iv"` + PokemonActiveShiny int `json:"pokemon_active_shiny"` +} + +func GetLiveStatsPokemon() *ApiPokemonLiveStatsResult { + start := time.Now() + + liveStats := &ApiPokemonLiveStatsResult{ + 0, + 0, + 0, + 0, + } + + pokemonLookupCache.Range(func(key uint64, pokemon PokemonLookupCacheItem) bool { + liveStats.PokemonActive++ + if pokemon.PokemonLookup.Iv > -1 { + liveStats.PokemonActiveIv++ + } + if pokemon.PokemonLookup.Shiny { + liveStats.PokemonActiveShiny++ + } + if pokemon.PokemonLookup.Iv == 100 { + liveStats.PokemonActive100iv++ + } + return true + }) + + log.Infof("apiLiveStats - %d pokemon_active, %d pokemon_active_iv, %d pokemon_active_100iv, %d pokemon_active_shiny, total time %s", liveStats.PokemonActive, liveStats.PokemonActiveIv, liveStats.PokemonActive100iv, liveStats.PokemonActiveShiny, time.Since(start)) + return liveStats +} diff --git a/decoder/pokemonRtree.go b/decoder/pokemonRtree.go index ff293fa0..22c7fddc 100644 --- a/decoder/pokemonRtree.go +++ b/decoder/pokemonRtree.go @@ -35,6 +35,7 @@ type PokemonLookup struct { Xxl bool Iv int8 Size int8 + Shiny bool } type PokemonPvpLookup struct { @@ -97,7 +98,8 @@ func updatePokemonLookup(pokemon *Pokemon, changePvp bool, pvpResults map[string } return -1 }(), - Size: int8(valueOrMinus1(pokemon.Size)), + Size: int8(valueOrMinus1(pokemon.Size)), + Shiny: bool(pokemon.Shiny.ValueOrZero()), } if !pokemon.IsDitto { pokemonLookupCacheItem.PokemonLookup.Form = int16(pokemon.Form.ValueOrZero()) diff --git a/main.go b/main.go index f7b82a35..8f96b75e 100644 --- a/main.go +++ b/main.go @@ -271,6 +271,7 @@ func main() { apiGroup.POST("/pokemon/scan", PokemonScan) apiGroup.POST("/pokemon/v2/scan", PokemonScan2) apiGroup.POST("/pokemon/search", PokemonSearch) + apiGroup.GET("/pokemon/livestats", PokemonLiveStats) apiGroup.GET("/devices/all", GetDevices) diff --git a/routes.go b/routes.go index 94df24c1..e2d99c13 100644 --- a/routes.go +++ b/routes.go @@ -478,3 +478,8 @@ func GetPokestop(c *gin.Context) { func GetDevices(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"devices": GetAllDevices()}) } + +func PokemonLiveStats(c *gin.Context) { + res := decoder.GetLiveStatsPokemon() + c.JSON(http.StatusAccepted, res) +} From 4fc24454fd673c14f9698416c301e28c6e6d81e0 Mon Sep 17 00:00:00 2001 From: petap0w Date: Fri, 28 Mar 2025 11:34:44 +0100 Subject: [PATCH 2/3] Use ExpireTimestamp in lookupCache as it doesn't clear expired entries --- decoder/api_pokemon.go | 26 +++++++++++++++----------- decoder/pokemonRtree.go | 15 +++++++++------ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/decoder/api_pokemon.go b/decoder/api_pokemon.go index 3161b9c0..93b8d3ef 100644 --- a/decoder/api_pokemon.go +++ b/decoder/api_pokemon.go @@ -2,13 +2,14 @@ package decoder import ( "fmt" - "golbat/config" - "golbat/geo" "math" "slices" "strconv" "time" + "golbat/config" + "golbat/geo" + log "github.com/sirupsen/logrus" "github.com/tidwall/rtree" ) @@ -167,6 +168,7 @@ type ApiPokemonLiveStatsResult struct { func GetLiveStatsPokemon() *ApiPokemonLiveStatsResult { start := time.Now() + now := time.Now().Unix() liveStats := &ApiPokemonLiveStatsResult{ 0, @@ -176,15 +178,17 @@ func GetLiveStatsPokemon() *ApiPokemonLiveStatsResult { } pokemonLookupCache.Range(func(key uint64, pokemon PokemonLookupCacheItem) bool { - liveStats.PokemonActive++ - if pokemon.PokemonLookup.Iv > -1 { - liveStats.PokemonActiveIv++ - } - if pokemon.PokemonLookup.Shiny { - liveStats.PokemonActiveShiny++ - } - if pokemon.PokemonLookup.Iv == 100 { - liveStats.PokemonActive100iv++ + if pokemon.PokemonLookup.ExpireTimestamp > now { + liveStats.PokemonActive++ + if pokemon.PokemonLookup.Iv > -1 { + liveStats.PokemonActiveIv++ + } + if pokemon.PokemonLookup.Shiny { + liveStats.PokemonActiveShiny++ + } + if pokemon.PokemonLookup.Iv == 100 { + liveStats.PokemonActive100iv++ + } } return true }) diff --git a/decoder/pokemonRtree.go b/decoder/pokemonRtree.go index 22c7fddc..b23da2b3 100644 --- a/decoder/pokemonRtree.go +++ b/decoder/pokemonRtree.go @@ -36,6 +36,7 @@ type PokemonLookup struct { Iv int8 Size int8 Shiny bool + ExpireTimestamp int64 } type PokemonPvpLookup struct { @@ -44,9 +45,11 @@ type PokemonPvpLookup struct { Ultra int16 } -var pokemonLookupCache *xsync.MapOf[uint64, PokemonLookupCacheItem] -var pokemonTreeMutex sync.RWMutex -var pokemonTree rtree.RTreeG[uint64] +var ( + pokemonLookupCache *xsync.MapOf[uint64, PokemonLookupCacheItem] + pokemonTreeMutex sync.RWMutex + pokemonTree rtree.RTreeG[uint64] +) func initPokemonRtree() { pokemonLookupCache = xsync.NewMapOf[uint64, PokemonLookupCacheItem]() @@ -56,7 +59,6 @@ func initPokemonRtree() { removePokemonFromTree(&r) // Rely on the pokemon pvp lookup caches to remove themselves rather than trying to synchronise }) - } func pokemonRtreeUpdatePokemonOnGet(pokemon *Pokemon) { @@ -98,8 +100,9 @@ func updatePokemonLookup(pokemon *Pokemon, changePvp bool, pvpResults map[string } return -1 }(), - Size: int8(valueOrMinus1(pokemon.Size)), - Shiny: bool(pokemon.Shiny.ValueOrZero()), + Size: int8(valueOrMinus1(pokemon.Size)), + Shiny: bool(pokemon.Shiny.ValueOrZero()), + ExpireTimestamp: int64(valueOrMinus1(pokemon.ExpireTimestamp)), } if !pokemon.IsDitto { pokemonLookupCacheItem.PokemonLookup.Form = int16(pokemon.Form.ValueOrZero()) From dfbf66bf72d4602b4c06c24145db7e1f8a21b92c Mon Sep 17 00:00:00 2001 From: petap0w Date: Fri, 28 Mar 2025 14:45:09 +0100 Subject: [PATCH 3/3] Switch to PokemonCache --- decoder/api_pokemon.go | 12 +++++++----- decoder/pokemonRtree.go | 15 +++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/decoder/api_pokemon.go b/decoder/api_pokemon.go index 93b8d3ef..ae66dfca 100644 --- a/decoder/api_pokemon.go +++ b/decoder/api_pokemon.go @@ -12,6 +12,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/tidwall/rtree" + "github.com/jellydator/ttlcache/v3" ) const earthRadiusKm = 6371 @@ -177,16 +178,17 @@ func GetLiveStatsPokemon() *ApiPokemonLiveStatsResult { 0, } - pokemonLookupCache.Range(func(key uint64, pokemon PokemonLookupCacheItem) bool { - if pokemon.PokemonLookup.ExpireTimestamp > now { + pokemonCache.Range(func(pokemonCacheEntry *ttlcache.Item[string, Pokemon]) bool { + pokemon := pokemonCacheEntry.Value() + if int64(valueOrMinus1(pokemon.ExpireTimestamp)) > now { liveStats.PokemonActive++ - if pokemon.PokemonLookup.Iv > -1 { + if !pokemon.Iv.IsZero() { liveStats.PokemonActiveIv++ } - if pokemon.PokemonLookup.Shiny { + if bool(pokemon.Shiny.ValueOrZero()) { liveStats.PokemonActiveShiny++ } - if pokemon.PokemonLookup.Iv == 100 { + if int(pokemon.Iv.ValueOrZero()) == 100 { liveStats.PokemonActive100iv++ } } diff --git a/decoder/pokemonRtree.go b/decoder/pokemonRtree.go index b23da2b3..ff293fa0 100644 --- a/decoder/pokemonRtree.go +++ b/decoder/pokemonRtree.go @@ -35,8 +35,6 @@ type PokemonLookup struct { Xxl bool Iv int8 Size int8 - Shiny bool - ExpireTimestamp int64 } type PokemonPvpLookup struct { @@ -45,11 +43,9 @@ type PokemonPvpLookup struct { Ultra int16 } -var ( - pokemonLookupCache *xsync.MapOf[uint64, PokemonLookupCacheItem] - pokemonTreeMutex sync.RWMutex - pokemonTree rtree.RTreeG[uint64] -) +var pokemonLookupCache *xsync.MapOf[uint64, PokemonLookupCacheItem] +var pokemonTreeMutex sync.RWMutex +var pokemonTree rtree.RTreeG[uint64] func initPokemonRtree() { pokemonLookupCache = xsync.NewMapOf[uint64, PokemonLookupCacheItem]() @@ -59,6 +55,7 @@ func initPokemonRtree() { removePokemonFromTree(&r) // Rely on the pokemon pvp lookup caches to remove themselves rather than trying to synchronise }) + } func pokemonRtreeUpdatePokemonOnGet(pokemon *Pokemon) { @@ -100,9 +97,7 @@ func updatePokemonLookup(pokemon *Pokemon, changePvp bool, pvpResults map[string } return -1 }(), - Size: int8(valueOrMinus1(pokemon.Size)), - Shiny: bool(pokemon.Shiny.ValueOrZero()), - ExpireTimestamp: int64(valueOrMinus1(pokemon.ExpireTimestamp)), + Size: int8(valueOrMinus1(pokemon.Size)), } if !pokemon.IsDitto { pokemonLookupCacheItem.PokemonLookup.Form = int16(pokemon.Form.ValueOrZero())