mirror of https://github.com/hykilpikonna/AquaDX
[+] Counter measure
parent
501bf06ada
commit
355c9e2a3d
|
@ -184,7 +184,7 @@ class UserRegistrar(
|
|||
@API("/keychip")
|
||||
@Doc("Get a Keychip ID so that the user can connect to the server.", "Success message")
|
||||
suspend fun setupConnection(@RP token: Str) = jwt.auth(token) { u ->
|
||||
if (u.keychip != null) return mapOf("keychip" to u.keychip)
|
||||
u.keychip?.let { return mapOf("keychip" to it) }
|
||||
log.info("Net: /user/keychip setup: ${u.auId} for ${u.username}")
|
||||
|
||||
// Generate a keychip id with 10 digits (e.g. A1234567890)
|
||||
|
|
|
@ -73,5 +73,5 @@ class JWT(
|
|||
|
||||
fun auth(token: Str) = parse(token) ?: (400 - "Invalid token")
|
||||
|
||||
final inline fun auth(token: Str, block: (AquaNetUser) -> Any) = block(auth(token))
|
||||
final inline fun <T> auth(token: Str, block: (AquaNetUser) -> T) = block(auth(token))
|
||||
}
|
|
@ -76,7 +76,7 @@ class AquaNetUser(
|
|||
|
||||
@OneToOne(cascade = [CascadeType.ALL])
|
||||
@JoinColumn(name = "gameOptions", unique = true, nullable = true)
|
||||
var gameOptions: AquaGameOptions? = null
|
||||
var gameOptions: AquaGameOptions? = null,
|
||||
) : Serializable {
|
||||
val computedName get() = displayName.ifEmpty { username }
|
||||
|
||||
|
|
|
@ -31,16 +31,26 @@ abstract class GameApiController<T : IUserData>(name: String, userDataClass: KCl
|
|||
playlogRepo.findByUserCardExtId(card.extId)
|
||||
}
|
||||
|
||||
private var rankingCache: Pair<Long, List<GenericRankingPlayer>>? = null
|
||||
private var rankingCache: MutableMap<Long, Pair<Long, List<GenericRankingPlayer>>> = mutableMapOf()
|
||||
private val rankingCacheDuration = 240_000
|
||||
@API("ranking")
|
||||
fun ranking(): List<GenericRankingPlayer> {
|
||||
// Read from cache if we just computed it less than 2 minutes ago
|
||||
rankingCache?.let { (t, r) ->
|
||||
if (millis() - t < 120_000) return r
|
||||
fun ranking(@RP token: String?): List<GenericRankingPlayer> {
|
||||
val reqUser = token?.let { us.jwt.auth(it) { u ->
|
||||
// Optimization: If the user is not banned, we don't need to process user information
|
||||
if (!u.ghostCard.rankingBanned && !u.cards.any { it.rankingBanned }) null
|
||||
else u
|
||||
} }
|
||||
val cacheKey = reqUser?.auId ?: -1
|
||||
|
||||
// Read from cache if we just computed it less than duration ago
|
||||
rankingCache[cacheKey]?.let { (t, r) ->
|
||||
if (millis() - t < rankingCacheDuration) return r
|
||||
}
|
||||
|
||||
// TODO: pagination
|
||||
// Shadow-ban: Do not show banned cards in the ranking except for the user who owns the card
|
||||
val players = userDataRepo.findAll().sortedByDescending { it.playerRating }
|
||||
.filter { it.card?.rankingBanned != true || it.card?.aquaUser?.let { it == reqUser } ?: false }
|
||||
return players.filter { it.card != null }.mapIndexed { i, user ->
|
||||
val plays = playlogRepo.findByUserCardExtId(user.card!!.extId)
|
||||
|
||||
|
@ -54,7 +64,7 @@ abstract class GameApiController<T : IUserData>(name: String, userDataClass: KCl
|
|||
lastSeen = user.lastPlayDate.toString(),
|
||||
username = user.card!!.aquaUser?.username ?: "user${user.card!!.id}"
|
||||
)
|
||||
}.also { rankingCache = millis() to it } // Update the cache
|
||||
}.also { rankingCache[cacheKey] = millis() to it } // Update cache
|
||||
}
|
||||
|
||||
@API("playlog")
|
||||
|
|
|
@ -125,7 +125,7 @@ open class UserDataEntity : BaseEntity() {
|
|||
interface GenericUserDataRepo<T : IUserData> : JpaRepository<T, Long> {
|
||||
fun findByCard(card: Card): T?
|
||||
fun findByCard_ExtId(extId: Long): Optional<T>
|
||||
@Query("select count(*) from #{#entityName} where playerRating > :rating")
|
||||
@Query("select count(*) from #{#entityName} e where e.playerRating > :rating and e.card.rankingBanned = false")
|
||||
fun getRanking(rating: Int): Long
|
||||
}
|
||||
|
||||
|
|
|
@ -39,8 +39,11 @@ class Card(
|
|||
var aquaUser: AquaNetUser? = null,
|
||||
|
||||
// Whether the card is a ghost card
|
||||
@Column(name = "is_ghost")
|
||||
var isGhost: Boolean = false,
|
||||
|
||||
// Unfortunately some people decide to cheat and upload all perfect scores :(
|
||||
// This will not affect gameplay behavior, but will hide the user from ranking
|
||||
var rankingBanned: Boolean = false,
|
||||
) {
|
||||
@Suppress("unused") // Used by serialization
|
||||
val isLinked get() = aquaUser != null
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE sega_card ADD COLUMN `ranking_banned` BIT(1) NOT NULL DEFAULT 0;
|
Loading…
Reference in New Issue