diff --git a/src/main/java/ext/Ext.kt b/src/main/java/ext/Ext.kt index a4b7ec22..507c9ead 100644 --- a/src/main/java/ext/Ext.kt +++ b/src/main/java/ext/Ext.kt @@ -117,6 +117,9 @@ catch (e: Exception) { null } } val Calendar.year get() = get(Calendar.YEAR) val Calendar.month get() = get(Calendar.MONTH) + 1 val Calendar.day get() = get(Calendar.DAY_OF_MONTH) +fun cal() = Calendar.getInstance() +fun Date.cal() = Calendar.getInstance().apply { time = this@cal } +operator fun Calendar.invoke(field: Int) = get(field) val Date.sec get() = time / 1000 // Encodings @@ -128,6 +131,8 @@ fun Any.long() = when (this) { is String -> toLong() else -> 400 - "Invalid number: $this" } +fun Any.int() = long().toInt() +operator fun Bool.unaryPlus() = if (this) 1 else 0 // Collections fun ls(vararg args: T) = args.toList() @@ -163,4 +168,14 @@ fun Str.path() = Path.of(this) operator fun Path.div(part: Str) = resolve(part) fun Str.ensureEndingSlash() = if (endsWith('/')) this else "$this/" -fun T.logger() = LoggerFactory.getLogger(this::class.java) \ No newline at end of file +fun T.logger() = LoggerFactory.getLogger(this::class.java) + +// I hate this ;-; +operator fun List.component6(): E = get(5) +operator fun List.component7(): E = get(6) +operator fun List.component8(): E = get(7) +operator fun List.component9(): E = get(8) +operator fun List.component10(): E = get(9) +operator fun List.component11(): E = get(10) +operator fun List.component12(): E = get(11) +operator fun List.component13(): E = get(12) diff --git a/src/main/java/ext/Json.kt b/src/main/java/ext/Json.kt index 302e8231..8ba8f722 100644 --- a/src/main/java/ext/Json.kt +++ b/src/main/java/ext/Json.kt @@ -33,6 +33,7 @@ val JACKSON = jacksonObjectMapper().apply { setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE); } inline fun ObjectMapper.parse(str: Str) = readValue(str, T::class.java) +inline fun ObjectMapper.parse(map: Map<*, *>) = convertValue(map, T::class.java) // TODO: https://stackoverflow.com/q/78197784/7346633 inline fun Str.parseJackson() = if (contains("null")) { val map = JACKSON.parse>(this) diff --git a/src/main/java/icu/samnyan/aqua/sega/general/IntegerListConverter.kt b/src/main/java/icu/samnyan/aqua/sega/general/IntegerListConverter.kt new file mode 100644 index 00000000..2309a179 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/general/IntegerListConverter.kt @@ -0,0 +1,10 @@ +package icu.samnyan.aqua.sega.general + +import jakarta.persistence.AttributeConverter +import jakarta.persistence.Converter + +@Converter +class IntegerListConverter : AttributeConverter, String> { + override fun convertToDatabaseColumn(lst: List?) = lst?.joinToString(";") ?: "" + override fun convertToEntityAttribute(str: String?) = str?.split(';')?.map { it.toInt() }?.toMutableList() ?: mutableListOf() +} diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/Mai2UserDetail.kt b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/Mai2UserDetail.kt index 7c379bb2..7f18314b 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/Mai2UserDetail.kt +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/Mai2UserDetail.kt @@ -7,7 +7,7 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize import icu.samnyan.aqua.net.games.BaseEntity import icu.samnyan.aqua.net.games.IGenericUserData import icu.samnyan.aqua.sega.general.model.Card -import icu.samnyan.aqua.sega.maimai2.util.IntegerListConverter +import icu.samnyan.aqua.sega.general.IntegerListConverter import icu.samnyan.aqua.sega.util.jackson.AccessCodeSerializer import jakarta.persistence.* diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/UserEntities.kt b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/UserEntities.kt index 33da9e92..f900e254 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/UserEntities.kt +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/model/userdata/UserEntities.kt @@ -8,7 +8,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.fasterxml.jackson.annotation.JsonPropertyOrder import icu.samnyan.aqua.net.games.BaseEntity import icu.samnyan.aqua.net.games.IGenericGamePlaylog -import icu.samnyan.aqua.sega.maimai2.util.IntegerListConverter +import icu.samnyan.aqua.sega.general.IntegerListConverter import jakarta.persistence.* import lombok.AllArgsConstructor import lombok.Data diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/util/IntegerListConverter.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/util/IntegerListConverter.java deleted file mode 100644 index 729a32ce..00000000 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/util/IntegerListConverter.java +++ /dev/null @@ -1,47 +0,0 @@ -package icu.samnyan.aqua.sega.maimai2.util; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; - -import jakarta.persistence.AttributeConverter; -import jakarta.persistence.Converter; - -import static java.util.Collections.*; - -import java.util.ArrayList; - -@Converter -public class IntegerListConverter implements AttributeConverter, String> { - private static final String SPLIT_CHAR = ";"; - - @Override - public String convertToDatabaseColumn(List integerList) { - if (integerList != null && !integerList.isEmpty()) { - StringBuilder str = new StringBuilder(); - Iterator iter = integerList.iterator(); - while(iter.hasNext()) { - str.append(iter.next()); - if(iter.hasNext()){ - str.append(SPLIT_CHAR); - } - } - return str.toString(); - } else { - return ""; - } - } - - @Override - public List convertToEntityAttribute(String string) { - if (string != null && !string.isEmpty()) { - List iList = new ArrayList<>(); - for (String s : string.split(SPLIT_CHAR)) { - iList.add(Integer.parseInt(s)); - } - return iList; - } else { - return emptyList(); - } - } -} \ No newline at end of file diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/Constants.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/Constants.kt index 822fcb99..7ef4f534 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/Constants.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/Constants.kt @@ -92,4 +92,23 @@ enum class WaccaOptionType(val id: Int, val default: Int) { SET_ICON_ID(1003, 102001), // ID SET_NAV_ID(1004, 210001), // ID SET_PLATE_ID(1005, 211001), // ID +} + +val waccaRatingMult = linkedMapOf( + 990_000 to 4.0, + 980_000 to 3.75, + 970_000 to 3.5, + 960_000 to 3.25, + 950_000 to 3.0, + 940_000 to 2.75, + 930_000 to 2.5, + 920_000 to 2.25, + 910_000 to 2.0, + 900_000 to 1.0, + 0 to 0.0 +) + +fun waccaRating(score: Int, level: Double): Int { + val mult = waccaRatingMult.entries.firstOrNull { score >= it.key }?.value ?: 0.0 + return (level * mult * 10).toInt() } \ No newline at end of file diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt index 32f40bc2..b223e59e 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/Repos.kt @@ -18,12 +18,18 @@ interface IWaccaUserLinked : JpaRepository { } interface WcUserOptionRepo : IWaccaUserLinked -interface WcUserBingoRepo : IWaccaUserLinked +interface WcUserBingoRepo : IWaccaUserLinked { + fun findByUserAndPageNumber(user: WaccaUser, pageNumber: Int): WcUserBingo? +} interface WcUserFriendRepo : IWaccaUserLinked interface WcUserFavoriteSongRepo : IWaccaUserLinked -interface WcUserGateRepo : IWaccaUserLinked +interface WcUserGateRepo : IWaccaUserLinked { + fun findByUserAndGateId(user: WaccaUser, gateId: Int): WcUserGate? +} interface WcUserItemRepo : IWaccaUserLinked -interface WcUserBestScoreRepo : IWaccaUserLinked +interface WcUserBestScoreRepo : IWaccaUserLinked { + fun findByUserAndSongIdAndDifficulty(user: WaccaUser, songId: Int, difficulty: Int): WcUserScore? +} interface WcUserPlayLogRepo : IWaccaUserLinked interface WcUserStageUpRepo : IWaccaUserLinked diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt index 4b1288ea..1f42f5ba 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUser.kt @@ -55,6 +55,8 @@ class WaccaUser : BaseEntity() { var lastFolderId = 0 var lastSongOrder = 0 @Temporal(TemporalType.TIMESTAMP) + var lastConsecDate: Date = Date(0) + @Temporal(TemporalType.TIMESTAMP) var lastLoginDate: Date = Date() var gateTutorialFlags: String = "[[1, 0], [2, 0], [3, 0], [4, 0], [5, 0]]" diff --git a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt index cfaa8c96..aa9ecef2 100644 --- a/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt +++ b/src/main/java/icu/samnyan/aqua/sega/wacca/model/db/WaccaUserModels.kt @@ -1,9 +1,9 @@ package icu.samnyan.aqua.sega.wacca.model.db import com.fasterxml.jackson.annotation.JsonIgnore -import ext.ls -import ext.sec +import ext.* import icu.samnyan.aqua.net.games.BaseEntity +import icu.samnyan.aqua.sega.general.IntegerListConverter import icu.samnyan.aqua.sega.wacca.WaccaItemType import icu.samnyan.aqua.sega.wacca.WaccaItemType.* import jakarta.persistence.* @@ -60,7 +60,7 @@ class WcUserGate : WaccaUserEntity() { var progress = 0 var loops = 0 @Temporal(TemporalType.TIMESTAMP) - var lastUsed = 0 + var lastUsed = Date(0) var missionFlag = 0 var totalPoints = 0 @@ -98,53 +98,50 @@ class WcUserItem( @Entity @Table(name = "wacca_user_score", uniqueConstraints = [UC("", ["user_id", "song_id", "chart_id"])]) class WcUserScore : WaccaUserEntity() { var songId = 0 - var chartId = 0 + var difficulty = 0 // aka difficulty var score = 0 - var playCt = 0 - var clearCt = 0 - var misslessCt = 0 - var fullcomboCt = 0 - var allmarvCt = 0 - var gradeDCt = 0 - var gradeCCt = 0 - var gradeBCt = 0 - var gradeACt = 0 - var gradeAACt = 0 - var gradeAAACt = 0 - var gradeSCt = 0 - var gradeSSCt = 0 - var gradeSSSCt = 0 - var gradeMasterCt = 0 - var gradeSpCt = 0 - var gradeSspCt = 0 - var gradeSsspCt = 0 + + @Convert(converter = IntegerListConverter::class) + var clears: MutableList = mutableListOf(0, 0, 0, 0, 0) // Played, Clear, Full Combo, Missless, All Marv + + @Convert(converter = IntegerListConverter::class) + var grades: MutableList = (1..13).map { 0 }.toMutableList() // From D to SSS+ var bestCombo = 0 - var lowestMissCt = 0 + var lowestMissCt = Int.MAX_VALUE var rating = 0 - fun ls() = ls(songId, chartId, - ls(playCt, clearCt, misslessCt, fullcomboCt, allmarvCt), - ls(playCt, clearCt, misslessCt, fullcomboCt, allmarvCt), - ls(gradeDCt, gradeCCt, gradeBCt, gradeACt, gradeAACt, gradeAAACt, gradeSCt, gradeSSCt, gradeSSSCt, gradeMasterCt), - score, bestCombo, lowestMissCt, 1, rating) + fun ls() = ls(songId, difficulty, clears, clears, grades, score, lowestMissCt, 0, 1, rating) } @Entity @Table(name = "wacca_user_playlog", uniqueConstraints = [UC("", ["user_id", "song_id", "chart_id", "date_scored"])]) class WcUserPlayLog : WaccaUserEntity() { var songId = 0 - var chartId = 0 + var difficulty = 0 + var level = 0.0 var score = 0 - var clear = 0 - var grade = 0 + @Convert(converter = IntegerListConverter::class) + var judgements: MutableList = mutableListOf(0, 0, 0, 0) // Marv, Great, Good, Miss var maxCombo = 0 - var marvCt = 0 - var greatCt = 0 - var goodCt = 0 - var missCt = 0 + var grade = 0 + var clear = false + var missless = false + var fullCombo = false + var allMarv = false + var giveUp = false + var skillPt = 0 var fastCt = 0 var lateCt = 0 - var season = 0 - var dateScored = "" + var newRecord = false + + @Temporal(TemporalType.TIMESTAMP) + var dateScored = Date() + + fun clears() = ls(1, +clear, +fullCombo, +missless, +allMarv) + + companion object { + val keys = ls("songId", "difficulty", "level", "score", "judgements", "maxCombo", "grade", "clear", "missless", "fullCombo", "allMarv", "giveUp", "skillPt", "fastCt", "lateCt", "newRecord") + fun parse(l: List<*>) = JACKSON.parse(keys.zip(l).toMap()) + } } @Entity @Table(name = "wacca_user_stageup", uniqueConstraints = [UC("", ["user_id", "stage_id"])]) diff --git a/src/main/resources/db/migration/mariadb/V1000_12__wacca_db_revision.sql b/src/main/resources/db/migration/mariadb/V1000_12__wacca_db_revision.sql index 100e3336..946480cd 100644 --- a/src/main/resources/db/migration/mariadb/V1000_12__wacca_db_revision.sql +++ b/src/main/resources/db/migration/mariadb/V1000_12__wacca_db_revision.sql @@ -32,4 +32,5 @@ ALTER TABLE wacca_user DROP COLUMN last_login_date, DROP COLUMN vip_expire_time, ADD last_login_date datetime NOT NULL, - ADD vip_expire_time datetime NOT NULL; + ADD vip_expire_time datetime NOT NULL, + ADD last_consec_date datetime NOT NULL; diff --git a/src/main/resources/db/migration/mariadb/V1000_13__wacca_db.sql b/src/main/resources/db/migration/mariadb/V1000_13__wacca_db.sql new file mode 100644 index 00000000..929b8217 --- /dev/null +++ b/src/main/resources/db/migration/mariadb/V1000_13__wacca_db.sql @@ -0,0 +1,120 @@ +ALTER TABLE wacca_user_playlog + ADD all_marv BIT(1) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD full_combo BIT(1) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD give_up BIT(1) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD judgements VARCHAR(255) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD level DOUBLE NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD missless BIT(1) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD new_record BIT(1) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD skill_pt INT NOT NULL; + +ALTER TABLE wacca_user_playlog + MODIFY all_marv BIT(1) NOT NULL; + +ALTER TABLE wacca_user_score + ADD clears VARCHAR(255) NOT NULL; + +ALTER TABLE wacca_user_score + ADD grades VARCHAR(255) NOT NULL; + +ALTER TABLE wacca_user_score + DROP COLUMN allmarv_ct; + +ALTER TABLE wacca_user_score RENAME COLUMN chart_id TO difficulty; + +ALTER TABLE wacca_user_score + DROP COLUMN clear_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN fullcombo_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN grade_master_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN grade_sp_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN grade_ssp_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN grade_sssp_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN gradeaaact; + +ALTER TABLE wacca_user_score + DROP COLUMN gradeaact; + +ALTER TABLE wacca_user_score + DROP COLUMN gradeact; + +ALTER TABLE wacca_user_score + DROP COLUMN gradebct; + +ALTER TABLE wacca_user_score + DROP COLUMN gradecct; + +ALTER TABLE wacca_user_score + DROP COLUMN gradedct; + +ALTER TABLE wacca_user_score + DROP COLUMN gradesct; + +ALTER TABLE wacca_user_score + DROP COLUMN gradessct; + +ALTER TABLE wacca_user_score + DROP COLUMN gradesssct; + +ALTER TABLE wacca_user_score + DROP COLUMN missless_ct; + +ALTER TABLE wacca_user_score + DROP COLUMN play_ct; + +ALTER TABLE wacca_user_playlog RENAME COLUMN chart_id TO difficulty; + +ALTER TABLE wacca_user_playlog + DROP COLUMN good_ct; + +ALTER TABLE wacca_user_playlog + DROP COLUMN great_ct; + +ALTER TABLE wacca_user_playlog + DROP COLUMN marv_ct; + +ALTER TABLE wacca_user_playlog + DROP COLUMN miss_ct; + +ALTER TABLE wacca_user_playlog + DROP COLUMN season; + +ALTER TABLE wacca_user_playlog + DROP COLUMN clear; + +ALTER TABLE wacca_user_playlog + DROP COLUMN date_scored; + +ALTER TABLE wacca_user_item + MODIFY acquired_date datetime NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD clear BIT(1) NOT NULL; + +ALTER TABLE wacca_user_playlog + ADD date_scored datetime NOT NULL; \ No newline at end of file