1 Commits
Author SHA1 Message Date
asterisk727 c1d3f61b00 feat: no longer stub maimai magical 2026-09-25 12:49:14 -05:00
11 changed files with 200 additions and 5 deletions
@@ -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.UserRivalMusic
import icu.samnyan.aqua.sega.maimai2.model.UserRivalMusicDetail 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.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 icu.samnyan.aqua.sega.maimai2.model.userdata.UserRegions
import java.time.LocalDate import java.time.LocalDate
import kotlin.random.Random import kotlin.random.Random
@@ -55,7 +60,81 @@ fun Maimai2ServletController.initApis() {
// Maimai only request for event type 1 // Maimai only request for event type 1
"GetGameEvent" static { mapOf("type" to 1, "gameEventList" to db.gameEvent.findAll()) } "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) } } "GetGameCharge" static {
db.gameCharge.findAll().let {
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( "GetUserOption" { mapOf(
"userId" to uid, "userId" to uid,
@@ -383,14 +462,19 @@ fun Maimai2ServletController.initApis() {
"aggrDate" to "" "aggrDate" to ""
) } ) }
"GetUserCircleData" static { mapOf( "GetUserCircleData" {
mapOf(
"userId" to uid,
"circleId" to 0, "circleId" to 0,
"circleName" to "一緒に歌おう!", "circleName" to "一緒に歌おう!",
"isPlace" to false, "isPlace" to false,
"circleClass" to 0, "circleClass" to 0,
"lastLoginDate" to "", "lastLoginDate" to "",
"mapBonusId" to 0,
"lastMapBonusDate" to (db.userData.findByCardExtId(uid)?.lastMapBonusDate ?: ""),
"circlePointRankingList" to empty "circlePointRankingList" to empty
) } )
}
"GetUserCircleChallenge" { mapOf( "GetUserCircleChallenge" { mapOf(
"userId" to uid, "userId" to uid,
@@ -75,7 +75,7 @@ class UpsertUserAllHandler(
listOfNotNull( listOfNotNull(
userExtend, userOption, userCharacterList, userMapList, userLoginBonusList, userItemList, userExtend, userOption, userCharacterList, userMapList, userLoginBonusList, userItemList,
userMusicDetailList, userCourseList, userFriendSeasonRankingList, userFavoriteList, userMusicDetailList, userCourseList, userFriendSeasonRankingList, userFavoriteList,
userKaleidxScopeList, userIntimateList, upsertUserAll.userPlaylogList userKaleidxScopeList, userIntimateList, userTicketLimitDateList, upsertUserAll.userPlaylogList
) )
}.flatten().forEach { it.user = u } }.flatten().forEach { it.user = u }
@@ -142,6 +142,17 @@ class UpsertUserAllHandler(
repos.userIntimate.saveAll(lst.mapApply { repos.userIntimate.saveAll(lst.mapApply {
id = repos.userIntimate.findByUserAndPartnerId(u, partnerId)?.id ?: 0 }) } 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 // Added on SDGA 1.65 / SDGB 1.53
upsertUserAll.userPlaylogList?.let(repos.userPlaylog::saveAll) upsertUserAll.userPlaylogList?.let(repos.userPlaylog::saveAll)
@@ -20,6 +20,22 @@ class Mai2GameCharge {
var endDate: String? = "2029-01-01 00:00:00.000000" 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 { class Mai2GameSellingCard {
var cardId = 0L var cardId = 0L
var startDate: String? = "2019-01-01 00:00:00.000000" var startDate: String? = "2019-01-01 00:00:00.000000"
@@ -42,6 +42,14 @@ interface Mai2UserCharacterRepo : Mai2UserLinked<Mai2UserCharacter> {
interface Mai2UserChargeRepo : Mai2UserLinked<Mai2UserCharge> 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> { interface Mai2UserCourseRepo : Mai2UserLinked<Mai2UserCourse> {
fun findByUserAndCourseId(user: Mai2UserDetail, courseId: Int): Mai2UserCourse? fun findByUserAndCourseId(user: Mai2UserDetail, courseId: Int): Mai2UserCourse?
} }
@@ -130,6 +138,8 @@ class Mai2Repos(
val userCard: Mai2UserCardRepo, val userCard: Mai2UserCardRepo,
val userCharacter: Mai2UserCharacterRepo, val userCharacter: Mai2UserCharacterRepo,
val userCharge: Mai2UserChargeRepo, val userCharge: Mai2UserChargeRepo,
val userPass: Mai2UserPassRepo,
val userTicketLimitDate: Mai2UserTicketLimitDateRepo,
val userCourse: Mai2UserCourseRepo, val userCourse: Mai2UserCourseRepo,
val userData: Mai2UserDataRepo, val userData: Mai2UserDataRepo,
val userExtend: Mai2UserExtendRepo, val userExtend: Mai2UserExtendRepo,
@@ -150,6 +160,8 @@ class Mai2Repos(
val gameData: GameDataService val gameData: GameDataService
) { ) {
val gameCharge = StaticRepo(gameData.mai2Charges) { it.orderId } 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 gameEvent = StaticRepo(gameData.mai2Events) { it.id }
val gameSellingCard = StaticRepo(gameData.mai2SellingCards) { it.cardId } val gameSellingCard = StaticRepo(gameData.mai2SellingCards) { it.cardId }
} }
@@ -30,6 +30,8 @@ class Mai2UserAll {
var userFavoritemusicList: List<Mai2UserFavoriteItem>? = null var userFavoritemusicList: List<Mai2UserFavoriteItem>? = null
var userKaleidxScopeList: List<Mai2UserKaleidx>? = null var userKaleidxScopeList: List<Mai2UserKaleidx>? = null
var userIntimateList: List<Mai2UserIntimate>? = null var userIntimateList: List<Mai2UserIntimate>? = null
var userTicketLimitDateList: List<Mai2UserTicketLimitDate>? = null
var userCircleData: Mai2UserCircleData? = null
var isNewCharacterList: String? = null var isNewCharacterList: String? = null
var isNewMapList: String? = null var isNewMapList: String? = null
var isNewLoginBonusList: String? = null var isNewLoginBonusList: String? = null
@@ -42,6 +44,11 @@ class Mai2UserAll {
var isNewKaleidxScopeList: String? = null var isNewKaleidxScopeList: String? = null
} }
class Mai2UserCircleData {
var lastLoginDate: String = ""
var lastMapBonusDate: String = ""
}
class Mai2UserFavoriteItem { class Mai2UserFavoriteItem {
var orderId = 0 var orderId = 0
var id = 0 var id = 0
@@ -52,4 +59,3 @@ class Mai2UserActivity {
var musicList: List<Mai2UserAct> = emptyList() var musicList: List<Mai2UserAct> = emptyList()
} }
@@ -87,6 +87,7 @@ class Mai2UserDetail(
var dailyCourseBonusDate: String = "", var dailyCourseBonusDate: String = "",
var lastPairLoginDate: String = "", var lastPairLoginDate: String = "",
var lastTrialPlayDate: String = "", var lastTrialPlayDate: String = "",
var lastMapBonusDate: String = "",
var playVsCount: Int = 0, var playVsCount: Int = 0,
var playSyncCount: Int = 0, var playSyncCount: Int = 0,
var winCount: Int = 0, var winCount: Int = 0,
@@ -111,6 +111,25 @@ class Mai2UserCharge : Mai2UserEntity() {
var validDate: String = "" 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") @Table(name = "maimai2_user_course")
@Entity @Entity
class Mai2UserCourse : Mai2UserEntity() { class Mai2UserCourse : Mai2UserEntity() {
@@ -13,6 +13,8 @@ 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.chusan.model.GameLoginBonusPreset as Chu3GameLoginBonusPreset
import icu.samnyan.aqua.sega.maimai2.model.Mai2GameCharge import icu.samnyan.aqua.sega.maimai2.model.Mai2GameCharge
import icu.samnyan.aqua.sega.maimai2.model.Mai2GameEvent 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.maimai2.model.Mai2GameSellingCard
import icu.samnyan.aqua.sega.ongeki.model.GameCard as OgkGameCard 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.GameChara as OgkGameChara
@@ -55,6 +57,8 @@ class GameDataService() {
// maimai2 // maimai2
lateinit var mai2Events: List<Mai2GameEvent> lateinit var mai2Events: List<Mai2GameEvent>
lateinit var mai2Charges: List<Mai2GameCharge> lateinit var mai2Charges: List<Mai2GameCharge>
lateinit var mai2PassCharges: List<Mai2GamePassCharge>
lateinit var mai2SellingPassPacks: List<Mai2GameSellingPassPack>
lateinit var mai2SellingCards: List<Mai2GameSellingCard> lateinit var mai2SellingCards: List<Mai2GameSellingCard>
lateinit var mai2GameEncryption: List<GameEncryptionKey> lateinit var mai2GameEncryption: List<GameEncryptionKey>
@@ -88,6 +92,8 @@ class GameDataService() {
fun load() { fun load() {
mai2Events = load("maimai2", "game_event.json") mai2Events = load("maimai2", "game_event.json")
mai2Charges = load("maimai2", "game_charge.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") mai2SellingCards = load("maimai2", "game_selling_card.json")
mai2GameEncryption = load(game = "maimai2", file = "game_encryption.json") mai2GameEncryption = load(game = "maimai2", file = "game_encryption.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"}
]
@@ -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
);