Author SHA1 Message Date
asterisk727 c1d3f61b00 feat: no longer stub maimai magical 2026-09-25 12:49:14 -05:00
27 changed files with 1658 additions and 3756 deletions
+9 -9
View File
@@ -32,15 +32,15 @@ Multipurpose game server for ALL.Net games.
> [!WARNING]
> CHUNITHM pre-NEW!! and maimai pre-DX are no longer supported after February 6th, 2026 and all associated data will be removed.
| Game | Latest Ver. | Initial Ver. | Notes | Web UI | Import | AES |
|------------------------|-----------------------|---------------------|-------------------------------------------------------------|--------|-------------|-----|
| SDHD: CHUNITHM | 2.50 (Mate) | 2.00 (NEW) | | ✅ | ✅ | ✅ |
| SDEZ: maimai DX | 1.65 (CiRCLE PLUS) | 1.00 (DX) | Thanks [@Menci](https://github.com/Menci) | ✅ | ✅ | ✅ |
| SDGA: maimai DX (Intl) | 1.60 (CiRCLE) | 1.00 (DX) | Thanks [@Clansty](https://github.com/clansty) | ✅ | ✅ | ✅ |
| SDED: Card Maker | 1.39 | N/A | Thanks [@Becods](https://github.com/Becods) | ❌ | ❌ | ❌ |
| SDDT: O.N.G.E.K.I. | 1.60 (Re:Fresh Act 2) | N/A | Thanks [@PenguinCaptain](https://github.com/PenguinCaptain) | ✅ | ✅ | ❌ |
| SBZV: Project DIVA | 7.10 | N/A | Minimal support | ❌ | ❌ | 🔓 |
| SDFE: Wacca | 3.07 (REVERSE) | N/A | Later versions are EOS patches | ✅ | ❌ | 🔓 |
| Game | Latest Ver. | Initial Ver. | Notes | Web UI | Import | AES |
|------------------------|--------------------|---------------------|-------------------------------------------------------------|--------|-------------|---------|
| SDHD: CHUNITHM | 2.50 (MATE) | 2.00 (NEW) | | ✅ | ✅ | ✅ |
| SDEZ: maimai DX | 1.65 (CiRCLE PLUS) | 1.00 (DX) | Thanks [@Menci](https://github.com/Menci) | ✅ | ✅ | ✅ |
| SDGA: maimai DX (Intl) | 1.60 (CiRCLE) | 1.00 (DX) | Thanks [@Clansty](https://github.com/clansty) | ✅ | ✅ | ✅ |
| SDED: Card Maker | 1.39 | N/A | Thanks [@Becods](https://github.com/Becods) | ❌ | ❌ | ❌ |
| SDDT: O.N.G.E.K.I. | 1.50 (Re:Fresh) | N/A | Thanks [@PenguinCaptain](https://github.com/PenguinCaptain) | ✅ | ✅ | ✅ |
| SBZV: Project DIVA | 7.10 | N/A | Minimal support | ❌ | ❌ | 🔓 |
| SDFE: Wacca | 3.07 (REVERSE) | N/A | Later versions are EOS patches | ✅ | ❌ | 🔓 |
<!-- A majority of them have been left as N/A for initial version as they do not appear to have restrictions -->
+1 -6
View File
@@ -32,7 +32,6 @@ import java.time.ZoneOffset.UTC
import java.time.format.DateTimeFormatter
import java.util.*
import java.util.concurrent.locks.Lock
import kotlin.math.floor
import kotlin.reflect.KCallable
import kotlin.reflect.KClass
import kotlin.reflect.KMutableProperty1
@@ -200,10 +199,6 @@ val Any?.truthy get() = when (this) {
}
val Any?.str get() = toString()
fun truncateVersion(version: Number): Number {
return (floor(version.toFloat() / 5.0) * 5).toInt();
}
// Collections
fun <T> ls(vararg args: T) = args.toList()
inline fun <reified T> arr(vararg args: T) = arrayOf(*args)
@@ -282,4 +277,4 @@ fun List<List<Any?>>.numCsv(vararg head: Str) = head.joinToString(",") + "\n" +
joinToString("\n") { it.joinToString(",") }
// DI
inline fun <reified T : Any> ApplicationContext.lazy() = kotlin.lazy { getBean(T::class.java) }
inline fun <reified T : Any> ApplicationContext.lazy() = kotlin.lazy { getBean(T::class.java) }
@@ -3,7 +3,6 @@ package icu.samnyan.aqua.sega.general.filter
import ext.details
import ext.logger
import ext.toJson
import ext.truncateVersion
import icu.samnyan.aqua.net.components.GeoIP
import icu.samnyan.aqua.sega.allnet.TokenChecker
import icu.samnyan.aqua.sega.general.model.GameEncryptionKey
@@ -37,6 +36,9 @@ class CompressionFilter(
}
fun truncateVersion(version: Number): Number {
return (floor(version.toFloat() / 5.0) * 5).toInt();
}
fun getVersion(req: HttpServletRequest, game: String): Int {
// NOTE: this should help with some issues regarding mismatched data
val expectedVersion = (req.getHeader("Mai-Encoding") ?:
@@ -7,6 +7,11 @@ import icu.samnyan.aqua.sega.general.model.CardStatus
import icu.samnyan.aqua.sega.maimai2.model.UserRivalMusic
import icu.samnyan.aqua.sega.maimai2.model.UserRivalMusicDetail
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserKaleidx
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2ItemKind
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserItem
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserPass
import icu.samnyan.aqua.sega.maimai2.model.userdata.Mai2UserTicketLimitDate
import icu.samnyan.aqua.sega.maimai2.model.userdata.MAIMAI_DATETIME
import icu.samnyan.aqua.sega.maimai2.model.userdata.UserRegions
import java.time.LocalDate
import kotlin.random.Random
@@ -55,13 +60,82 @@ fun Maimai2ServletController.initApis() {
// Maimai only request for event type 1
"GetGameEvent" static { mapOf("type" to 1, "gameEventList" to db.gameEvent.findAll()) }
"GetGameCharge" static {
db.gameCharge.findAll().let {
mapOf("length" to it.size, "gameChargeList" to it, "gamePassChargeList" to empty)
mapOf(
"length" to it.size,
"gameChargeList" to it,
"gamePassChargeList" to db.gamePassCharge.findAll()
)
}
}
"GetGameSellingPassPack" static {
mapOf("gameSellingPassPackList" to db.gameSellingPassPack.findAll())
}
"GetUserPass" {
db.userData.findByCardExtId(uid) ?: (404 - "User not found")
mapOf("userId" to uid, "userPassList" to db.userPass.findByUser_Card_ExtId(uid))
}
"GetUserTicketLimitDate" {
db.userData.findByCardExtId(uid) ?: (404 - "User not found")
mapOf("userTicketLimitDateList" to db.userTicketLimitDate.findByUser_Card_ExtId(uid))
}
"UserPassModeEnter" static { mapOf("returnCode" to 1) }
"UserPassModeExit" static { mapOf("returnCode" to 1) }
"UpsertUserPassMode" {
val user = db.userData.findByCardExtId(uid) ?: (404 - "User not found")
val pass = (data["userPassList"] as List<*>).first() as Map<*, *>
val passTypeId = (pass["passTypeId"] as Number).toInt()
val passPackId = (pass["passPackId"] as Number).toInt()
val passCharaId = (pass["passCharaId"] as Number).toInt()
val now = jstNow()
val startDate = now.format(MAIMAI_DATETIME)
val endDate = now.plusDays(14).format(MAIMAI_DATETIME)
// store the new pass
val storedPass = (db.userPass.findByUserAndPassTypeId(user, passTypeId) ?: Mai2UserPass()).apply {
this.user = user
this.passTypeId = passTypeId
this.passPackId = passPackId
this.passCharaId = passCharaId
this.startDate = startDate
this.endDate = endDate
}
db.userPass.save(storedPass)
// attempt to give user ticket 40001 (+1 track ticket) up to a max of 10
val ticketId = 40001
val ticket = (db.userItem.findByUserAndItemKindAndItemId(user, Mai2ItemKind.ticket.id, ticketId)
?: Mai2UserItem().apply {
this.user = user
itemKind = Mai2ItemKind.ticket.id
itemId = ticketId
}).apply {
stock = (stock + 1).coerceAtMost(10)
isValid = true
}
db.userItem.save(ticket)
// set expiry date for newly granted ticket
val limit = (db.userTicketLimitDate.findByUserAndItemId(user, ticketId)
?: Mai2UserTicketLimitDate().apply { this.user = user; itemId = ticketId }).apply {
limitDate = endDate
}
db.userTicketLimitDate.save(limit)
mapOf(
"returnCode" to 1,
"userPassList" to db.userPass.findByUser_Card_ExtId(uid),
"userItemList" to listOf(ticket),
"userTicketLimitDateList" to db.userTicketLimitDate.findByUser_Card_ExtId(uid)
)
}
"GetUserOption" { mapOf(
"userId" to uid,
"userOption" to (db.userOption.findSingleByUser_Card_ExtId(uid) ?: (404 - "User not found"))
@@ -388,14 +462,19 @@ fun Maimai2ServletController.initApis() {
"aggrDate" to ""
) }
"GetUserCircleData" static { mapOf(
"GetUserCircleData" {
mapOf(
"userId" to uid,
"circleId" to 0,
"circleName" to "一緒に歌おう!",
"isPlace" to false,
"circleClass" to 0,
"lastLoginDate" to "",
"mapBonusId" to 0,
"lastMapBonusDate" to (db.userData.findByCardExtId(uid)?.lastMapBonusDate ?: ""),
"circlePointRankingList" to empty
) }
)
}
"GetUserCircleChallenge" { mapOf(
"userId" to uid,
@@ -443,12 +522,6 @@ fun Maimai2ServletController.initApis() {
)
) }
"GetGameSellingPassPack" static { mapOf("gameSellingPassPackList" to empty) }
"GetUserPass" { mapOf("userId" to uid, "userPassList" to empty) }
"GetUserTicketLimitDate" static { mapOf("userTicketLimitDateList" to empty) }
// NOTE: no-op APIs moved to respect encryption
"UpsertUserPlaceCircleRegist" static { mapOf( "returnCode" to 0, "apiName" to "com.sega.maimai2servlet.api.UpsertUserPlaceCircleRegistApi") }
"GetUserScoreRanking" static { """{"returnCode":1, "apiName":"com.sega.maimai2servlet.api.GetUserScoreRankingApi"}""" }
@@ -75,7 +75,7 @@ class UpsertUserAllHandler(
listOfNotNull(
userExtend, userOption, userCharacterList, userMapList, userLoginBonusList, userItemList,
userMusicDetailList, userCourseList, userFriendSeasonRankingList, userFavoriteList,
userKaleidxScopeList, userIntimateList, upsertUserAll.userPlaylogList
userKaleidxScopeList, userIntimateList, userTicketLimitDateList, upsertUserAll.userPlaylogList
)
}.flatten().forEach { it.user = u }
@@ -142,6 +142,17 @@ class UpsertUserAllHandler(
repos.userIntimate.saveAll(lst.mapApply {
id = repos.userIntimate.findByUserAndPartnerId(u, partnerId)?.id ?: 0 }) }
req.userTicketLimitDateList?.unique { it.itemId }?.let { lst ->
repos.userTicketLimitDate.saveAll(lst.mapApply {
id = repos.userTicketLimitDate.findByUserAndItemId(u, itemId)?.id ?: 0
})
}
req.userCircleData?.lastMapBonusDate?.let {
u.lastMapBonusDate = it
repos.userData.save(u)
}
// Added on SDGA 1.65 / SDGB 1.53
upsertUserAll.userPlaylogList?.let(repos.userPlaylog::saveAll)
@@ -20,6 +20,22 @@ class Mai2GameCharge {
var endDate: String? = "2029-01-01 00:00:00.000000"
}
class Mai2GamePassCharge {
var orderId: Long = 0
var passTypeId = 0
var price = 0
var startDate: String? = "2019-01-01 00:00:00.000000"
var endDate: String? = "2099-01-01 00:00:00.000000"
}
class Mai2GameSellingPassPack {
var passPackId = 0
var startDate: String? = "2019-01-01 00:00:00.000000"
var endDate: String? = "2099-01-01 00:00:00.000000"
var noticeStartDate: String? = "2019-01-01 00:00:00.000000"
var noticeEndDate: String? = "2099-01-01 00:00:00.000000"
}
class Mai2GameSellingCard {
var cardId = 0L
var startDate: String? = "2019-01-01 00:00:00.000000"
@@ -42,6 +42,14 @@ interface Mai2UserCharacterRepo : Mai2UserLinked<Mai2UserCharacter> {
interface Mai2UserChargeRepo : Mai2UserLinked<Mai2UserCharge>
interface Mai2UserPassRepo : Mai2UserLinked<Mai2UserPass> {
fun findByUserAndPassTypeId(user: Mai2UserDetail, passTypeId: Int): Mai2UserPass?
}
interface Mai2UserTicketLimitDateRepo : Mai2UserLinked<Mai2UserTicketLimitDate> {
fun findByUserAndItemId(user: Mai2UserDetail, itemId: Int): Mai2UserTicketLimitDate?
}
interface Mai2UserCourseRepo : Mai2UserLinked<Mai2UserCourse> {
fun findByUserAndCourseId(user: Mai2UserDetail, courseId: Int): Mai2UserCourse?
}
@@ -130,6 +138,8 @@ class Mai2Repos(
val userCard: Mai2UserCardRepo,
val userCharacter: Mai2UserCharacterRepo,
val userCharge: Mai2UserChargeRepo,
val userPass: Mai2UserPassRepo,
val userTicketLimitDate: Mai2UserTicketLimitDateRepo,
val userCourse: Mai2UserCourseRepo,
val userData: Mai2UserDataRepo,
val userExtend: Mai2UserExtendRepo,
@@ -150,6 +160,8 @@ class Mai2Repos(
val gameData: GameDataService
) {
val gameCharge = StaticRepo(gameData.mai2Charges) { it.orderId }
val gamePassCharge = StaticRepo(gameData.mai2PassCharges) { it.passTypeId.toLong() }
val gameSellingPassPack = StaticRepo(gameData.mai2SellingPassPacks) { it.passPackId.toLong() }
val gameEvent = StaticRepo(gameData.mai2Events) { it.id }
val gameSellingCard = StaticRepo(gameData.mai2SellingCards) { it.cardId }
}
@@ -30,6 +30,8 @@ class Mai2UserAll {
var userFavoritemusicList: List<Mai2UserFavoriteItem>? = null
var userKaleidxScopeList: List<Mai2UserKaleidx>? = null
var userIntimateList: List<Mai2UserIntimate>? = null
var userTicketLimitDateList: List<Mai2UserTicketLimitDate>? = null
var userCircleData: Mai2UserCircleData? = null
var isNewCharacterList: String? = null
var isNewMapList: String? = null
var isNewLoginBonusList: String? = null
@@ -42,6 +44,11 @@ class Mai2UserAll {
var isNewKaleidxScopeList: String? = null
}
class Mai2UserCircleData {
var lastLoginDate: String = ""
var lastMapBonusDate: String = ""
}
class Mai2UserFavoriteItem {
var orderId = 0
var id = 0
@@ -52,4 +59,3 @@ class Mai2UserActivity {
var musicList: List<Mai2UserAct> = emptyList()
}
@@ -87,6 +87,7 @@ class Mai2UserDetail(
var dailyCourseBonusDate: String = "",
var lastPairLoginDate: String = "",
var lastTrialPlayDate: String = "",
var lastMapBonusDate: String = "",
var playVsCount: Int = 0,
var playSyncCount: Int = 0,
var winCount: Int = 0,
@@ -111,6 +111,25 @@ class Mai2UserCharge : Mai2UserEntity() {
var validDate: String = ""
}
@Table(name = "maimai2_user_pass", uniqueConstraints = [UniqueConstraint(columnNames = ["user_id", "pass_type_id"])])
@Entity
class Mai2UserPass : Mai2UserEntity() {
var passTypeId = 0
var passPackId = 0
var passCharaId = 0
var mapId = 0
var startDate: String = ""
var endDate: String = ""
}
@Table(name = "maimai2_user_ticket_limit_date", uniqueConstraints = [UniqueConstraint(columnNames = ["user_id", "item_id"])])
@Entity
class Mai2UserTicketLimitDate : Mai2UserEntity() {
var itemId = 0
var limitDate: String = ""
var lastUsedDate: String = ""
}
@Table(name = "maimai2_user_course")
@Entity
class Mai2UserCourse : Mai2UserEntity() {
@@ -4,8 +4,6 @@ import ext.empty
import ext.int
import ext.parsing
import ext.plus
import ext.str
import ext.truncateVersion
fun OngekiController.ongekiInit() {
fun <T> List<T>.staticLst(key: String) = mapOf("length" to size, key to this)
@@ -15,21 +13,18 @@ fun OngekiController.ongekiInit() {
initUpsertAll()
// Has type, but type is always 1
"GetGameEvent" {
// val trunkVer = truncateVersion((data["version"] ?: "1.50.00").str.split(".").take(2).joinToString("").int)
val events = gdb.event.findAll()
// .filter{ it.id.str.startsWith(trunkVer.str) }
.map {
mapOf("id" to it.id, "type" to 1, "startDate" to "2005-01-01 00:00:00.0", "endDate" to "2099-01-01 05:00:00.0")
}
mapOf("gameEventList" to events, "length" to events.size, "type" to 1)
"GetGameEvent".static {
gdb.event.findAll().map {
mapOf("id" to it.id, "type" to 1, "startDate" to "2005-01-01 00:00:00.0", "endDate" to "2099-01-01 05:00:00.0")
}.staticLst("gameEventList") + mapOf("type" to 1)
}
"GetGamePoint".static { gdb.point.findAll().staticLst("gamePointList") }
"GetGamePresent".static { gdb.present.findAll().staticLst("gamePresentList") }
"GetGameReward".static { gdb.reward.findAll().staticLst("gameRewardList") }
"GetGameTechMusic".static { gdb.gameData.ogkGameTechMusics.staticLst("gameTechMusicList") }
// Dummy endpoints
"GetGameTechMusic".static { empty.staticLst("gameTechMusicList") }
"GetGameMessage" { mapOf("type" to data["type"], "length" to 0, "gameMessageList" to empty) }
"GetGameMusicReleaseState".static { mapOf("techScore" to 0, "cardNum" to 0) }
@@ -87,4 +82,4 @@ fun OngekiController.ongekiInit() {
"UpsertClientTestmode" static { """{"returnCode":1, "apiName":"UpsertClientTestmodeApi"}""" }
"UpsertUserGplog" static { """{"returnCode":1, "apiName":"UpsertUserGplogApi"}""" }
"Ping" static { """{"returnCode":1, "apiName":"Ping"}""" }
}
}
@@ -54,12 +54,11 @@ interface OgkUserDeckRepo : OngekiUserLinked<UserDeck> {
}
interface OgkUserEventMusicRepo : OngekiUserLinked<UserEventMusic> {
fun findByUserAndEventIdAndTypeAndMusicIdAndLevel(
fun findByUserAndEventIdAndTypeAndMusicId(
userData: UserData,
eventId: Int,
type: Int,
musicId: Int,
level: Int
musicId: Int
): UserEventMusic?
}
@@ -163,10 +162,7 @@ interface OgkUserGachaRepo : OngekiUserLinked<UserGacha> {
}
// Re:Fresh
interface OgkUserEventMapRepo : OngekiUserLinked<UserEventMap> {
fun findByUserAndEventIdAndMapId(user: UserData, eventId: Int, mapId: Int): UserEventMap?
fun findByUser_Card_ExtIdAndEventIdAndMapId(extId: Long, eventId: Int, mapId: Int): UserEventMap?
}
interface OgkUserEventMapRepo : OngekiUserLinked<UserEventMap>
interface OgkUserSkinRepo : OngekiUserLinked<UserSkin>
@@ -74,9 +74,7 @@ fun OngekiController.initUpsertAll() {
// UserEventMap
userEventMap?.let {
db.eventMap.save(it.apply {
id = db.eventMap.findByUserAndEventIdAndMapId(u, it.eventId, it.mapId)?.id ?: 0
})
}
id = db.eventMap.findSingleByUser(u)?.id ?: 0 }) }
// UserPlaylogList
userPlaylogList?.let { db.playlog.saveAll(it) }
@@ -197,9 +195,8 @@ fun OngekiController.initUpsertAll() {
// UserEventMusicList
userEventMusicList?.let { list ->
db.eventMusic.saveAll(list.distinctBy { it.eventId to it.type to it.musicId to it.level }.mapApply {
id = db.eventMusic.findByUserAndEventIdAndTypeAndMusicIdAndLevel(u, eventId, type, musicId, level)?.id ?: 0
}) }
db.eventMusic.saveAll(list.distinctBy { it.eventId to it.type to it.musicId }.mapApply {
id = db.eventMusic.findByUserAndEventIdAndTypeAndMusicId(u, eventId, type, musicId)?.id ?: 0 }) }
// UserTechEventList
userTechEventList?.let { list ->
@@ -7,7 +7,6 @@ import icu.samnyan.aqua.sega.general.model.CardStatus
import icu.samnyan.aqua.sega.general.model.UserRecentRating
import icu.samnyan.aqua.sega.ongeki.model.OgkItemType
import icu.samnyan.aqua.sega.ongeki.model.UserItem
import icu.samnyan.aqua.sega.ongeki.model.UserEventMap
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import java.time.LocalDateTime
@@ -15,25 +14,10 @@ import java.time.format.DateTimeFormatter
fun OngekiController.initUser() {
val emptyEventMapData = """{"unlockPoints":0,"_unlockedTileIDs":[1],"_rewardScenarioIDs":[],"_rewardRewardKeyIDs":[],"endless":false,"loopCount":0}"""
"GetUserData" { mapOf("userId" to uid, "userData" to db.data.findByCard_ExtId(uid)) }
"GetUserOption" { mapOf("userId" to uid, "userOption" to db.option.findSingleByUser_Card_ExtId(uid)) }
"GetUserEventMap" {
val eventId = data["eventId"]?.int ?: 0
val mapId = data["mapId"]?.int ?: 0
val userEventMap = db.eventMap.findByUser_Card_ExtIdAndEventIdAndMapId(uid, eventId, mapId)
?: UserEventMap().apply {
this.eventId = eventId
this.mapId = mapId
this.mapData = emptyEventMapData
}
mapOf(
"userId" to uid,
"userEventMap" to userEventMap
)
}
"GetUserEventMap" { mapOf("userId" to uid, "userEventMap" to db.eventMap.findSingleByUser_Card_ExtId(uid)) }
"GetUserTechEvent".unpaged { db.techEvent.findByUser_Card_ExtId(uid) }
"GetUserBoss".unpaged { db.boss.findByUser_Card_ExtId(uid) }
@@ -29,12 +29,6 @@ class GameEvent {
var id: Long = 0
}
class GameTechMusic {
var eventId = 0
var musicId = 0
var level = 0
}
class GameMusic {
var id: Long = 0
var name: String = ""
@@ -111,4 +105,4 @@ class GameGacha {
@JsonProperty("isCeiling")
var isCeiling: Boolean = false
}
}
@@ -166,7 +166,6 @@ class UserChapter : OngekiUserEntity() {
var jewelCount = 0
var lastPlayMusicCategory = 0
var lastPlayMusicId = 0
var lastPlayMusicMinorCategory: String = ""
var lastPlayMusicLevel = 0
var isStoryWatched = false
var isClear = false
@@ -198,10 +197,7 @@ class UserDeck : OngekiUserEntity() {
}
@Entity(name = "OngekiUserEventMusic")
@Table(
name = "ongeki_user_event_music",
uniqueConstraints = [UniqueConstraint(columnNames = ["user_id", "event_id", "type", "music_id", "level"])]
)
@Table(name = "ongeki_user_event_music")
class UserEventMusic : OngekiUserEntity() {
var eventId = 0
var type = 0
@@ -322,7 +318,6 @@ class UserMusicItem : OngekiUserEntity() {
class UserOption : OngekiUserEntity() {
var optionSet = 0
var speed = 0
var fieldWall = 0
var mirror = 0
var judgeTiming = 0
var judgeAdjustment = 0
@@ -347,19 +342,20 @@ class UserOption : OngekiUserEntity() {
var colorSide = 0
var effectDamage = 0
var effectPos = 0
var effectAttack = 0
var judgeDisp = 0
var judgePos = 0
var judgeBreak = 0
var judgeHit = 0
var judgeCriticalBreak = 0
var platinumBreakDisp = 0
var judgeCriticalBreak = 0
var matching = 0
var dispPlayerLv = 0
var dispRating = 0
var dispBP = 0
var headphone = 0
// Re:Fresh
var effectAttack = 0
}
@@ -461,7 +457,6 @@ class UserStory : OngekiUserEntity() {
var jewelCount = 0
var lastPlayMusicId = 0
var lastPlayMusicCategory = 0
var lastPlayMusicMinorCategory: String = ""
var lastPlayMusicLevel = 0
}
@@ -503,10 +498,7 @@ class UserTrainingRoom : OngekiUserEntity() {
// Re:Fresh
@Entity(name = "OngekiUserEventMap")
@Table(
name = "ongeki_user_event_map",
uniqueConstraints = [UniqueConstraint(columnNames = ["user_id", "event_id", "map_id"])]
)
@Table(name = "ongeki_user_event_map")
class UserEventMap : OngekiUserEntity() {
var eventId = 0
var mapId = 0
@@ -13,13 +13,14 @@ import icu.samnyan.aqua.sega.chusan.model.GameLoginBonus as Chu3GameLoginBonus
import icu.samnyan.aqua.sega.chusan.model.GameLoginBonusPreset as Chu3GameLoginBonusPreset
import icu.samnyan.aqua.sega.maimai2.model.Mai2GameCharge
import icu.samnyan.aqua.sega.maimai2.model.Mai2GameEvent
import icu.samnyan.aqua.sega.maimai2.model.Mai2GamePassCharge
import icu.samnyan.aqua.sega.maimai2.model.Mai2GameSellingPassPack
import icu.samnyan.aqua.sega.maimai2.model.Mai2GameSellingCard
import icu.samnyan.aqua.sega.ongeki.model.GameCard as OgkGameCard
import icu.samnyan.aqua.sega.ongeki.model.GameChara as OgkGameChara
import icu.samnyan.aqua.sega.ongeki.model.GameEvent as OgkGameEvent
import icu.samnyan.aqua.sega.ongeki.model.GameGacha as OgkGameGacha
import icu.samnyan.aqua.sega.ongeki.model.GameGachaCard as OgkGameGachaCard
import icu.samnyan.aqua.sega.ongeki.model.GameTechMusic as OgkGameTechMusic
import icu.samnyan.aqua.sega.ongeki.model.GameMusic as OgkGameMusic
import icu.samnyan.aqua.sega.ongeki.model.GamePoint as OgkGamePoint
import icu.samnyan.aqua.sega.ongeki.model.GamePresent as OgkGamePresent
@@ -51,12 +52,13 @@ class GameDataService() {
log.warn("Game data file $f or resource $resPath not found, using empty list")
return emptyList()
}
}
// maimai2
lateinit var mai2Events: List<Mai2GameEvent>
lateinit var mai2Charges: List<Mai2GameCharge>
lateinit var mai2PassCharges: List<Mai2GamePassCharge>
lateinit var mai2SellingPassPacks: List<Mai2GameSellingPassPack>
lateinit var mai2SellingCards: List<Mai2GameSellingCard>
lateinit var mai2GameEncryption: List<GameEncryptionKey>
@@ -74,7 +76,6 @@ class GameDataService() {
lateinit var ogkGameCards: List<OgkGameCard>
lateinit var ogkGameCharas: List<OgkGameChara>
lateinit var ogkGameEvents: List<OgkGameEvent>
lateinit var ogkGameTechMusics: List<OgkGameTechMusic>
lateinit var ogkGameMusics: List<OgkGameMusic>
lateinit var ogkGamePoints: List<OgkGamePoint>
lateinit var ogkGamePresents: List<OgkGamePresent>
@@ -91,6 +92,8 @@ class GameDataService() {
fun load() {
mai2Events = load("maimai2", "game_event.json")
mai2Charges = load("maimai2", "game_charge.json")
mai2PassCharges = load("maimai2", "game_pass_charge.json")
mai2SellingPassPacks = load("maimai2", "game_selling_pass_pack.json")
mai2SellingCards = load("maimai2", "game_selling_card.json")
mai2GameEncryption = load(game = "maimai2", file = "game_encryption.json")
@@ -106,7 +109,6 @@ class GameDataService() {
ogkGameCards = load("ongeki", "game_card.json")
ogkGameCharas = load("ongeki", "game_chara.json")
ogkGameEvents = load("ongeki", "game_event.json")
ogkGameTechMusics = load("ongeki", "game_tech_music.json")
ogkGameMusics = load("ongeki", "game_music.json")
ogkGamePoints = load("ongeki", "game_point.json")
ogkGamePresents = load("ongeki", "game_present.json")
@@ -0,0 +1,4 @@
[
{"orderId": 0, "passTypeId": 2, "price": 2, "startDate": "2026-09-17 00:00:00.000000", "endDate": "2099-01-01 00:00:00.000000"},
{"orderId": 0, "passTypeId": 3, "price": 3, "startDate": "2026-09-17 00:00:00.000000", "endDate": "2099-01-01 00:00:00.000000"}
]
@@ -0,0 +1,5 @@
[
{"passPackId": 7001, "startDate": "2026-09-17 00:00:00.000000", "endDate": "2026-11-06 00:00:00.000000", "noticeStartDate": "2026-09-03 00:00:00.000000", "noticeEndDate": "2026-11-06 00:00:00.000000"},
{"passPackId": 7002, "startDate": "2026-11-06 00:00:00.000000", "endDate": "2027-01-08 00:00:00.000000", "noticeStartDate": "2026-10-23 00:00:00.000000", "noticeEndDate": "2027-01-08 00:00:00.000000"},
{"passPackId": 7003, "startDate": "2027-01-08 00:00:00.000000", "endDate": "2099-01-01 00:00:00.000000", "noticeStartDate": "2026-12-25 00:00:00.000000", "noticeEndDate": "2099-01-01 00:00:00.000000"}
]
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,31 @@
ALTER TABLE maimai2_user_detail
ADD COLUMN last_map_bonus_date VARCHAR(64) NOT NULL DEFAULT '';
CREATE TABLE maimai2_user_pass
(
id BIGINT AUTO_INCREMENT NOT NULL,
user_id BIGINT NOT NULL,
pass_type_id INT NOT NULL,
pass_pack_id INT NOT NULL,
pass_chara_id INT NOT NULL,
map_id INT NOT NULL DEFAULT 0,
start_date VARCHAR(64) NOT NULL DEFAULT '',
end_date VARCHAR(64) NOT NULL DEFAULT '',
CONSTRAINT pk_maimai2_user_pass PRIMARY KEY (id),
CONSTRAINT uc_maimai2_user_pass_type UNIQUE (user_id, pass_type_id),
CONSTRAINT fk_maimai2_user_pass_user FOREIGN KEY (user_id)
REFERENCES maimai2_user_detail (id) ON DELETE CASCADE
);
CREATE TABLE maimai2_user_ticket_limit_date
(
id BIGINT AUTO_INCREMENT NOT NULL,
user_id BIGINT NOT NULL,
item_id INT NOT NULL,
limit_date VARCHAR(64) NOT NULL DEFAULT '',
last_used_date VARCHAR(64) NOT NULL DEFAULT '',
CONSTRAINT pk_maimai2_user_ticket_limit_date PRIMARY KEY (id),
CONSTRAINT uc_maimai2_user_ticket_limit_date_item UNIQUE (user_id, item_id),
CONSTRAINT fk_maimai2_user_ticket_limit_date_user FOREIGN KEY (user_id)
REFERENCES maimai2_user_detail (id) ON DELETE CASCADE
);
@@ -1,20 +0,0 @@
-- should have been not nullable in the first place smh
DELETE FROM ongeki_user_event_map
WHERE user_id IS NULL;
ALTER TABLE ongeki_user_event_map
MODIFY user_id BIGINT NOT NULL;
-- dedupe before adding new constraint just in case
DELETE FROM ongeki_user_event_map
WHERE id NOT IN (
SELECT id
FROM (
SELECT MAX(id) AS id
FROM ongeki_user_event_map
GROUP BY user_id, event_id, map_id
) AS rows_to_keep
);
ALTER TABLE ongeki_user_event_map
ADD UNIQUE KEY uniq_ongeki_user_event_map (user_id, event_id, map_id);
@@ -1,23 +0,0 @@
-- allow separate Technical Challenge records for each chart difficulty
-- drop the old constraint and dedupe with the new constraint just in case again
ALTER TABLE ongeki_user_event_music
DROP INDEX IF EXISTS UKo5PU3BgZeTKB8SNykq9U7xjfshp;
DELETE FROM ongeki_user_event_music
WHERE user_id IS NULL;
ALTER TABLE ongeki_user_event_music
MODIFY user_id BIGINT NOT NULL;
DELETE FROM ongeki_user_event_music
WHERE id NOT IN (
SELECT id
FROM (
SELECT MAX(id) AS id
FROM ongeki_user_event_music
GROUP BY user_id, event_id, type, music_id, level
) AS rows_to_keep
);
ALTER TABLE ongeki_user_event_music
ADD UNIQUE KEY uniq_ongeki_user_event_music (user_id, event_id, type, music_id, level);
@@ -1,5 +0,0 @@
ALTER TABLE ongeki_user_chapter
ADD COLUMN last_play_music_minor_category VARCHAR(255) NOT NULL DEFAULT '';
ALTER TABLE ongeki_user_story
ADD COLUMN last_play_music_minor_category VARCHAR(255) NOT NULL DEFAULT '';
@@ -1,7 +0,0 @@
-- new field wall option field
ALTER TABLE ongeki_user_option
ADD COLUMN field_wall INT NOT NULL DEFAULT 0;
-- varchar(255) not enough for event map storage
ALTER TABLE ongeki_user_event_map
MODIFY COLUMN map_data TEXT NULL;
+1 -1
View File
@@ -59,7 +59,7 @@ class Mai2Test : StringSpec({
liveTest("GetGameChargeApi") {
post("GetGameChargeApi", """{"isAll":false}""").let { (_, result) ->
result shouldBe """{"length":5,"gameChargeList":[{"chargeId":0,"orderId":0,"price":1,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":2,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":3,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":4,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":5,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"}],"gamePassChargeList":[]}""".jsonMap()
result shouldBe """{"length":5,"gameChargeList":[{"chargeId":0,"orderId":0,"price":1,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":2,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":3,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":4,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"},{"chargeId":0,"orderId":0,"price":5,"startDate":"2019-01-01 00:00:00.000000","endDate":"2099-01-01 00:00:00.000000"}]}""".jsonMap()
}
}