diff --git a/AquaNet/src/components/RatingComposition.svelte b/AquaNet/src/components/RatingComposition.svelte index dd285f21..a32041da 100644 --- a/AquaNet/src/components/RatingComposition.svelte +++ b/AquaNet/src/components/RatingComposition.svelte @@ -16,19 +16,25 @@ if (top) split = split?.toSorted((a, b) => b.score - a.score).slice(0, top) if (split) console.log("Split", split) + + let mustResize = split && split.length > 12 -{#if split && comp} -
-

{title}

-
- {#each split as p} -
- -
- {/each} +{#if split && split.length > 0 && comp} +
+ {#if mustResize} + + {/if} + +

{title}

+
+ {#each split as p} +
+ +
+ {/each} +
-
{/if} diff --git a/AquaNet/src/libs/generalTypes.ts b/AquaNet/src/libs/generalTypes.ts index 069b8e91..da82d445 100644 --- a/AquaNet/src/libs/generalTypes.ts +++ b/AquaNet/src/libs/generalTypes.ts @@ -79,7 +79,8 @@ export interface GenericRanking { rating: number fullCombo: number allPerfect: number - lastSeen: string + lastSeen: string, + modernRating: number } export interface RankCount { diff --git a/AquaNet/src/libs/i18n/en_ref.ts b/AquaNet/src/libs/i18n/en_ref.ts index e68513b0..8a119cc0 100644 --- a/AquaNet/src/libs/i18n/en_ref.ts +++ b/AquaNet/src/libs/i18n/en_ref.ts @@ -2,6 +2,8 @@ export const EN_REF_USER = { 'UserHome.ServerRank': 'Server Rank', 'UserHome.DXRating': 'DX Rating', 'UserHome.Rating': 'Rating', + 'UserHome.LegacyRating': 'Legacy Rating', + 'UserHome.ModernRating': 'Modern Rating', 'UserHome.Statistics': 'Statistics', 'UserHome.Accuracy': 'Accuracy', 'UserHome.MaxCombo': 'Max Combo', @@ -24,6 +26,16 @@ export const EN_REF_USER = { 'UserHome.RankDetail.Title': 'Achievement Details', 'UserHome.RankDetail.Level': 'Level', 'UserHome.B50': 'B50', + 'UserHome.RatingComposition.Best50': 'Best 50', + 'UserHome.RatingComposition.Best35': 'Best 35', + 'UserHome.RatingComposition.Best30': 'Best 30', + 'UserHome.RatingComposition.Best15': 'Best 15', + 'UserHome.RatingComposition.New15': 'New 15', + 'UserHome.RatingComposition.New10': 'New 10', + 'UserHome.RatingComposition.Best25Candidates': 'Best 25 (Candidates)', + 'UserHome.RatingComposition.Platinum': 'Platinum', + 'UserHome.RatingComposition.New20': 'New 20', + 'UserHome.RatingComposition.Recent10': 'Recent 10', 'UserHome.AddRival': "Add to Rival", 'UserHome.RemoveRival': "Remove from Rival", 'UserHome.InvalidGame': "Game ${game} is not supported on the web UI yet. We only support maimai, chunithm, wacca, and ongeki for now.", @@ -67,6 +79,8 @@ export const EN_REF_LEADERBOARD = { 'Leaderboard.Title': 'Server Leaderboard', 'Leaderboard.Rank': 'Rank', 'Leaderboard.Rating': 'Rating', + 'Leaderboard.LegacyRating': 'Legacy Rating', + 'Leaderboard.ModernRating': 'Modern Rating', 'Leaderboard.Accuracy': 'Accuracy', 'Leaderboard.FC': 'FC', 'Leaderboard.AP': 'AP', diff --git a/AquaNet/src/libs/i18n/zh.ts b/AquaNet/src/libs/i18n/zh.ts index dee56fb8..bdb58cad 100644 --- a/AquaNet/src/libs/i18n/zh.ts +++ b/AquaNet/src/libs/i18n/zh.ts @@ -15,6 +15,8 @@ const zhUser: typeof EN_REF_USER = { 'UserHome.ServerRank': '服务器排名', 'UserHome.DXRating': 'DX B50', 'UserHome.Rating': '评分', + 'UserHome.LegacyRating': '旧评分', + 'UserHome.ModernRating': '新评分', 'UserHome.Statistics': '统计数据', 'UserHome.Accuracy': '准确率', 'UserHome.MaxCombo': '最大连击', @@ -37,6 +39,16 @@ const zhUser: typeof EN_REF_USER = { 'UserHome.RankDetail.Title': '评分详细', 'UserHome.RankDetail.Level': "等级", 'UserHome.B50': "B50", + 'UserHome.RatingComposition.Best50': '最佳 50', + 'UserHome.RatingComposition.Best35': '最佳 35', + 'UserHome.RatingComposition.Best30': '最佳 30', + 'UserHome.RatingComposition.Best15': '最佳 15', + 'UserHome.RatingComposition.New15': '新曲 15', + 'UserHome.RatingComposition.New10': '新曲 10', + 'UserHome.RatingComposition.Best25Candidates': '最佳 25(候选)', + 'UserHome.RatingComposition.Platinum': '白金分', + 'UserHome.RatingComposition.New20': '新曲 20', + 'UserHome.RatingComposition.Recent10': '最近 10', 'UserHome.AddRival': "添加劲敌", 'UserHome.RemoveRival': "移除劲敌", 'UserHome.InvalidGame': "游戏 ${game} 还不支持网页端查看。我们目前只支持舞萌、中二、华卡和音击。", @@ -73,12 +85,15 @@ const zhWelcome: typeof EN_REF_Welcome = { 'welcome.verified': '您的邮箱已经验证成功!您现在可以登录了', 'welcome.verification-failed': '验证失败:${message}。请重试', 'welcome.password-reset-done': '您的密码已更新!请重新登录', + 'welcome.login_link': '登录或注册以绑定卡片。', } const zhLeaderboard: typeof EN_REF_LEADERBOARD = { 'Leaderboard.Title': '排行榜', 'Leaderboard.Rank': '排名', 'Leaderboard.Rating': '评分', + 'Leaderboard.LegacyRating': '旧评分', + 'Leaderboard.ModernRating': '新评分', 'Leaderboard.Accuracy': '准确率', 'Leaderboard.FC': 'FC', 'Leaderboard.AP': 'AP', diff --git a/AquaNet/src/libs/ongekiTypes.ts b/AquaNet/src/libs/ongekiTypes.ts index 5f107fe2..a5981dbd 100644 --- a/AquaNet/src/libs/ongekiTypes.ts +++ b/AquaNet/src/libs/ongekiTypes.ts @@ -22,3 +22,8 @@ export interface OngekiUserSummaryEntry { recent10: string recent: GenericGamePlaylog[] } + +export interface OngekiRefreshData { + playerRating: number, + highestRating: number +} \ No newline at end of file diff --git a/AquaNet/src/libs/sdk.ts b/AquaNet/src/libs/sdk.ts index 1c04775c..2ad3ac98 100644 --- a/AquaNet/src/libs/sdk.ts +++ b/AquaNet/src/libs/sdk.ts @@ -12,6 +12,7 @@ import type { Dict, GameUserOption } from './generalTypes' +import type { OngekiRefreshData } from './ongekiTypes' import type { GameName } from './scoring' interface ExtReqInit extends RequestInit { @@ -244,6 +245,8 @@ export const GAME = { post(`/api/v2/game/${game}/import-music-detail`, {}, { json: data }), setRival: (game: GameName, rivalUserName: string, isAdd: boolean) => post(`/api/v2/game/${game}/set-rival`, { rivalUserName, isAdd }), + refreshData: (username: string): Promise => + post(`/api/v2/game/ongeki/refresh-data`, { username }) } export const DATA = { diff --git a/AquaNet/src/pages/Ranking.svelte b/AquaNet/src/pages/Ranking.svelte index 52269922..543da201 100644 --- a/AquaNet/src/pages/Ranking.svelte +++ b/AquaNet/src/pages/Ranking.svelte @@ -52,7 +52,12 @@
hoveringUser = ""} role="heading" aria-level="2"> {t("Leaderboard.Rank")} - {t("Leaderboard.Rating")} + {#if game == "ongeki"} + {t("Leaderboard.ModernRating")} + {t("Leaderboard.LegacyRating")} + {:else} + {t("Leaderboard.Rating")} + {/if} {t("Leaderboard.Accuracy")} {t("Leaderboard.FC")} {t("Leaderboard.AP")} @@ -70,11 +75,23 @@ {user.name} {/if} - { - game === 'chu3' || game === 'ongeki' ? - (user.rating / 100).toFixed(2) : - user.rating.toLocaleString() - } + {#if game == 'ongeki'} + {#if user.modernRating > 0} + { + user.modernRating.toLocaleString() + } + {/if} + { + (user.rating / 100).toFixed(2) + } + {:else} + { + game === 'chu3' ? + (user.rating / 100).toFixed(2) : + user.rating.toLocaleString() + } + {/if} + {(+user.accuracy).toFixed(2)}% {user.fullCombo} {user.allPerfect} @@ -141,6 +158,14 @@ .accuracy display: none + .modern-rating + display: none + .legacy-rating + /* this is officially the worst css i've ever written dear god */ + white-space: nowrap + overflow: hidden + text-overflow: clip + min-width: 3.5em !important &.alternate background-color: vars.$ov-light diff --git a/AquaNet/src/pages/UserHome.svelte b/AquaNet/src/pages/UserHome.svelte index d9f25442..f48232d3 100644 --- a/AquaNet/src/pages/UserHome.svelte +++ b/AquaNet/src/pages/UserHome.svelte @@ -24,6 +24,7 @@ import useLocalStorage from "../libs/hooks/useLocalStorage.svelte"; import Line from "../components/chart/Line.svelte"; import ChuniUserboxDisplay from "../components/settings/userbox/ChuniUserboxDisplay.svelte"; + import type { OngekiRefreshData } from "../libs/ongekiTypes"; const TREND_DAYS = 60 @@ -51,6 +52,8 @@ let isLoading = false let showMoreRecent = false + let ongekiData: OngekiRefreshData + function init() { USER.isLoggedIn() && USER.me().then(u => me = u) @@ -77,7 +80,7 @@ GAME.userSummary(username, game), GAME.trend(username, game), DATA.allMusic(game), - ]).then(([user, trend, music]) => { + ]).then(async ([user, trend, music]) => { console.log(user) console.log(trend) console.log(games) @@ -92,6 +95,11 @@ }) } + if (game == 'ongeki') { + let ogkData = await GAME.refreshData(username).catch(err => {}); + if(ogkData) ongekiData = ogkData + } + // Set beforeRating in recent to the last play's afterRating user.recent.forEach((it, i) => { if (i < user.recent.length - 1) { @@ -187,14 +195,28 @@
-
- {game === 'mai2' ? t("UserHome.DXRating"): t("UserHome.Rating")} - { - game === 'chu3' || game === 'ongeki' ? - (d.user.rating / 100).toFixed(2) : - d.user.rating.toLocaleString() - } -
+ + + {#if game == "ongeki" && ongekiData} + +
+ {t("UserHome.ModernRating")} + {ongekiData.playerRating.toLocaleString()} +
+
+ {t("UserHome.LegacyRating")} + {(d.user.rating / 100).toFixed(2)} +
+ {:else} +
+ {game === 'mai2' ? t("UserHome.DXRating"): t("UserHome.Rating")} + { + game === 'chu3' ? + (d.user.rating / 100).toFixed(2) : + d.user.rating.toLocaleString() + } +
+ {/if}
{t('UserHome.ServerRank')} @@ -306,18 +328,20 @@
- + + + + + + + + - - - - - {#if d.user.ratingComposition.new} - + {:else} - + {/if}
diff --git a/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt b/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt index 496757c6..1a28cb37 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt @@ -57,7 +57,7 @@ abstract class GameApiController(val name: String, userDataClass: val time = millis() // Check cache validity - if (rankingCache.isEmpty()) (500 - "Rank is empty or is currently computing. Please come back later.") + if (rankingCache.isEmpty()) (500 - "Rank is empty or is currently computing.") val reqUser = token?.let { us.jwt.auth(it) }?.let { u -> // Optimization: If the user is not banned, we don't need to process user information @@ -98,7 +98,7 @@ abstract class GameApiController(val name: String, userDataClass: SUM(p.is_full_combo) AS fc, SUM(p.is_all_perfect) AS ap, c.ranking_banned or a.opt_out_of_leaderboard or c.status = 12 AS hide, - a.username + a.username ${if (name == "ongeki") ", u.new_player_rating" else ""} FROM ${tableName}_user_playlog_view p JOIN ${tableName}_user_data_view u ON p.user_id = u.id JOIN sega_card c ON u.aime_card_id = c.id @@ -115,7 +115,8 @@ abstract class GameApiController(val name: String, userDataClass: accuracy = it[4]!!.double, fullCombo = it[5]!!.int, allPerfect = it[6]!!.int, - username = it[8]?.toString() ?: "user${it[0]}" + username = it[8]?.toString() ?: "user${it[0]}", + modernRating = if (name == "ongeki") it[9]?.int() ?: 0 else 0 ) } rankingSortedIndex = rankingCache.filter { !it.l }.map { it.r.rating }.reversed() diff --git a/src/main/java/icu/samnyan/aqua/net/games/Models.kt b/src/main/java/icu/samnyan/aqua/net/games/Models.kt index 5fefbb11..a4bcc14e 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/Models.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/Models.kt @@ -59,7 +59,8 @@ data class GenericRankingPlayer( val rating: Int, val allPerfect: Int, val fullCombo: Int, - val lastSeen: String + val lastSeen: String, + val modernRating: Int = 0 ) @Serializable diff --git a/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt b/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt index 8c95ef54..92b45676 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt @@ -60,7 +60,7 @@ class Maimai2( val ratingComposition = mapOf( "best35" to (extra["recent_rating"] ?: ""), - "best15" to (extra["recent_rating_new"] ?: "") + "new15" to (extra["recent_rating_new"] ?: "") ) // if isLogin than boolean or null diff --git a/src/main/java/icu/samnyan/aqua/net/games/ongeki/Ongeki.kt b/src/main/java/icu/samnyan/aqua/net/games/ongeki/Ongeki.kt index 57e3fe37..f03d2fc1 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/ongeki/Ongeki.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/ongeki/Ongeki.kt @@ -42,16 +42,35 @@ class Ongeki( ) } override suspend fun userSummary(username: String, token: String?) = us.cardByName(username) { card -> + val user = userDataRepo.findByCard_ExtId(card.extId) ?: (404 - "User not found") val extra = userGeneralDataRepository.findByUser_Card_ExtId(card.extId) .associate { it.propertyKey to it.propertyValue } - val ratingComposition = mapOf( - "best30" to (extra["rating_base_best"] ?: ""), - "best15" to (extra["rating_base_new_best"] ?: ""), - "recent10" to (extra["rating_base_hot_best"] ?: "") - ) + val isRefresh = user.newHighestRating > 0 + if (isRefresh) + genericUserSummary(card, mapOf( + "best50" to (extra["new_rating_base_best"] ?: ""), + "new10" to (extra["new_rating_base_new_best"] ?: ""), + "best25_candidates" to (extra["new_rating_base_next_best"] ?: ""), + "pscore" to (extra["new_rating_base_pscore"] ?: "") + )) + else + genericUserSummary(card, mapOf( + "best30" to (extra["rating_base_best"] ?: ""), + "new15" to (extra["rating_base_new_best"] ?: ""), + "recent10" to (extra["rating_base_hot_best"] ?: "") + )) + } - genericUserSummary(card, ratingComposition) + @API("refresh-data") + suspend fun refreshData(@RP username: String) = us.cardByName(username) { card -> + val user = userDataRepo.findByCard_ExtId(card.extId) ?: (404 - "User not found") + if (user.newHighestRating > 0) + mapOf( + "playerRating" to user.newPlayerRating, + "highestRating" to user.newHighestRating + ) + else (400 - "User has not played Refresh") } @API("user-option") diff --git a/src/main/resources/db/80/V1000_79__ongeki_fk_2.sql b/src/main/resources/db/80/V1000_79__ongeki_fk_2.sql new file mode 100644 index 00000000..11cef5dc --- /dev/null +++ b/src/main/resources/db/80/V1000_79__ongeki_fk_2.sql @@ -0,0 +1,14 @@ +ALTER TABLE ongeki_user_regions +DROP FOREIGN KEY IF EXISTS fk_ongekiregions_on_ongeki_user_data; + +ALTER TABLE ongeki_user_regions +DROP FOREIGN KEY IF EXISTS fk_ongekiregions_on_aqua_net_user; + +ALTER TABLE ongeki_user_regions + ADD CONSTRAINT fk_ongekiregions_on_ongeki_user_data + FOREIGN KEY (user_id) REFERENCES ongeki_user_data (id) + ON DELETE CASCADE ON UPDATE CASCADE; + +DROP VIEW IF EXISTS ongeki_user_data_view; + CREATE VIEW ongeki_user_data_view AS + SELECT * FROM ongeki_user_data; \ No newline at end of file