mirror of
https://github.com/hykilpikonna/AquaDX.git
synced 2026-09-12 11:25:49 -05:00
[-] Remove ranking cache
This commit is contained in:
@@ -7,12 +7,8 @@ import icu.samnyan.aqua.net.utils.SUCCESS
|
||||
import icu.samnyan.aqua.sega.allnet.UserKeychipRepo
|
||||
import icu.samnyan.aqua.sega.general.model.Card
|
||||
import icu.samnyan.aqua.sega.general.service.CardService
|
||||
import jakarta.annotation.PostConstruct
|
||||
import org.slf4j.LoggerFactory
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.jvm.optionals.getOrNull
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@@ -45,33 +41,8 @@ abstract class GameApiController<T : IUserData>(val name: String, userDataClass:
|
||||
playlogRepo.findByUserCardExtId(card.extId)
|
||||
}
|
||||
|
||||
// List<Pair<should_hide, player>>>
|
||||
private var rankingCache: List<GenericRankingPlayer> = emptyList()
|
||||
private var rankingCacheLock = ReentrantLock()
|
||||
// Sorted index List<Rating> = Rank
|
||||
private var rankingLookupCache: Map<Long, GenericRankingPlayer> = emptyMap()
|
||||
private val pageSize = 100
|
||||
|
||||
@API("ranking")
|
||||
fun ranking(@RP token: String?, @RP page: Int?): List<GenericRankingPlayer> {
|
||||
val time = millis()
|
||||
|
||||
// Check cache validity
|
||||
if (rankingCache.isEmpty()) (500 - "Rank is empty or is currently computing.")
|
||||
|
||||
return page?.let {
|
||||
if (it < 0) (400 - "Invalid page number")
|
||||
rankingCache.drop(it * pageSize).take(pageSize)
|
||||
} ?: rankingCache
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
fun rankingCacheInit() = thread { rankingCacheRun() }
|
||||
|
||||
// Every 20 minutes
|
||||
@Scheduled(fixedRate = 20, initialDelay = 20, timeUnit = TimeUnit.MINUTES)
|
||||
fun rankingCacheRun() = rankingCacheLock.maybeLock { rankingCacheCompute() }
|
||||
|
||||
private val tableName = when (name) { "mai2" -> "maimai2"; "chu3" -> "chusan"; else -> name }
|
||||
private val userDataTable = when (name) {
|
||||
"mai2" -> "maimai2_user_detail"
|
||||
@@ -80,47 +51,66 @@ abstract class GameApiController<T : IUserData>(val name: String, userDataClass:
|
||||
else -> "wacca_user"
|
||||
}
|
||||
private val userCardColumn = if (name == "chu3") "card_id" else "aime_card_id"
|
||||
private val ratingColumn = if (name == "ongeki") "u.new_player_rating" else "u.player_rating"
|
||||
|
||||
fun rankingCacheCompute() {
|
||||
val time = millis()
|
||||
rankingCache = us.em.createNativeQuery(
|
||||
"""
|
||||
SELECT
|
||||
c.id,
|
||||
u.user_name,
|
||||
${if (name == "ongeki") "u.new_player_rating" else "u.player_rating"} AS rating,
|
||||
u.last_play_date,
|
||||
r.achievement_sum / NULLIF(r.play_count, 0) / 10000.0 AS acc,
|
||||
r.full_combo_count AS fc,
|
||||
r.all_perfect_count AS ap,
|
||||
c.ranking_banned or COALESCE(a.opt_out_of_leaderboard, 0) or c.status = 12 AS hide,
|
||||
a.username
|
||||
FROM ${tableName}_user_ranking_cache r
|
||||
JOIN $userDataTable u ON r.user_id = u.id
|
||||
JOIN sega_card c ON u.$userCardColumn = c.id
|
||||
LEFT JOIN aqua_net_user a ON c.net_user_id = a.au_id
|
||||
WHERE r.play_count > 0
|
||||
AND NOT (c.ranking_banned or COALESCE(a.opt_out_of_leaderboard, 0) or c.status = 12)
|
||||
${if (name == "ongeki") "AND u.new_player_rating > 0" else "" /* Hide users on Ongeki 1.45 and below */}
|
||||
ORDER BY rating DESC;
|
||||
"""
|
||||
).setHint("jakarta.persistence.query.timeout", 60_000).exec.mapIndexed { i, it ->
|
||||
private val rankingSelect = """
|
||||
SELECT
|
||||
ROW_NUMBER() OVER (ORDER BY $ratingColumn DESC, c.id) AS player_rank,
|
||||
c.id AS card_id,
|
||||
u.user_name,
|
||||
$ratingColumn AS rating,
|
||||
u.last_play_date,
|
||||
r.achievement_sum / NULLIF(r.play_count, 0) / 10000.0 AS acc,
|
||||
r.full_combo_count AS fc,
|
||||
r.all_perfect_count AS ap,
|
||||
a.username
|
||||
FROM ${tableName}_user_ranking_cache r
|
||||
JOIN $userDataTable u ON r.user_id = u.id
|
||||
JOIN sega_card c ON u.$userCardColumn = c.id
|
||||
LEFT JOIN aqua_net_user a ON c.net_user_id = a.au_id
|
||||
WHERE r.play_count > 0
|
||||
AND NOT (c.ranking_banned or COALESCE(a.opt_out_of_leaderboard, 0) or c.status = 12)
|
||||
${if (name == "ongeki") "AND u.new_player_rating > 0" else "" /* Hide users on Ongeki 1.45 and below */}
|
||||
""".trimIndent()
|
||||
|
||||
@API("ranking")
|
||||
fun ranking(@RP token: String?, @RP page: Int?): List<GenericRankingPlayer> {
|
||||
if (page != null && page < 0) (400 - "Invalid page number")
|
||||
|
||||
val offset = page?.toLong()?.times(pageSize)
|
||||
if (offset != null && offset > Int.MAX_VALUE) return emptyList()
|
||||
|
||||
val query = us.em.createNativeQuery("$rankingSelect ORDER BY player_rank")
|
||||
.setHint("jakarta.persistence.query.timeout", 60_000)
|
||||
if (offset != null) {
|
||||
query.setFirstResult(offset.toInt()).setMaxResults(pageSize)
|
||||
}
|
||||
|
||||
return query.exec.map { row ->
|
||||
GenericRankingPlayer(
|
||||
rank = i + 1,
|
||||
id = it[0]!!.long,
|
||||
name = it[1].toString(),
|
||||
rating = it[2]!!.int,
|
||||
lastSeen = it[3].toString(),
|
||||
accuracy = it[4]!!.double,
|
||||
fullCombo = it[5]!!.int,
|
||||
allPerfect = it[6]!!.int,
|
||||
username = it[8]?.toString() ?: "user${it[0]}"
|
||||
rank = row[0]!!.int,
|
||||
id = row[1]!!.long,
|
||||
name = row[2].toString(),
|
||||
rating = row[3]!!.int,
|
||||
lastSeen = row[4].toString(),
|
||||
accuracy = row[5]!!.double,
|
||||
fullCombo = row[6]!!.int,
|
||||
allPerfect = row[7]!!.int,
|
||||
username = row[8]?.toString() ?: "user${row[1]}"
|
||||
)
|
||||
}
|
||||
rankingLookupCache = rankingCache.associateBy { it.id }
|
||||
logger.info("Ranking for $name computed in ${millis() - time}ms")
|
||||
}
|
||||
|
||||
private fun rankingPosition(cardId: Long): String = us.em.createNativeQuery(
|
||||
"""
|
||||
SELECT player_rank
|
||||
FROM ($rankingSelect) ranking
|
||||
WHERE card_id = :cardId
|
||||
""".trimIndent()
|
||||
).setParameter("cardId", cardId)
|
||||
.setHint("jakarta.persistence.query.timeout", 60_000)
|
||||
.resultList.singleOrNull()?.let { (it as Number).toInt().str } ?: "-"
|
||||
|
||||
@API("playlog")
|
||||
fun playlog(@RP id: Long): IGenericGamePlaylog = playlogRepo.findById(id)() ?: (404 - "Playlog not found")
|
||||
|
||||
@@ -188,7 +178,7 @@ abstract class GameApiController<T : IUserData>(val name: String, userDataClass:
|
||||
return GenericGameSummary(
|
||||
name = user.userName,
|
||||
aquaUser = card.aquaUser?.publicFields,
|
||||
serverRank = rankingLookupCache[user.card!!.id]?.rank?.str ?: "-",
|
||||
serverRank = rankingPosition(user.card!!.id),
|
||||
accuracy = plays.acc(),
|
||||
rating = getRating(user, false),
|
||||
ratingHighest = getRating(user, true),
|
||||
|
||||
Reference in New Issue
Block a user