[+] Maimai item unlock

This commit is contained in:
Azalea
2024-03-18 05:31:55 -04:00
parent af83cf552e
commit b3955731c2
6 changed files with 84 additions and 32 deletions

View File

@@ -12,6 +12,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNamingStrategy import kotlinx.serialization.json.JsonNamingStrategy
import org.apache.tika.Tika import org.apache.tika.Tika
import org.apache.tika.mime.MimeTypes import org.apache.tika.mime.MimeTypes
import org.slf4j.LoggerFactory
import org.springframework.http.HttpStatus import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.RequestBody import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestHeader import org.springframework.web.bind.annotation.RequestHeader
@@ -67,6 +68,16 @@ val HTTP = HttpClient(CIO) {
val TIKA = Tika() val TIKA = Tika()
val MIMES = MimeTypes.getDefaultMimeTypes() val MIMES = MimeTypes.getDefaultMimeTypes()
// Class resource
object Ext {
val log = LoggerFactory.getLogger(Ext::class.java)
}
fun res(name: Str) = Ext::class.java.getResourceAsStream(name)
fun resStr(name: Str) = res(name)?.reader()?.readText()
inline fun <reified T> resJson(name: Str, warn: Boolean = true) = resStr(name)?.let {
JSON.decodeFromString<T>(it)
} ?: run { if (warn) Ext.log.warn("Resource $name is not found"); null }
// Date and time // Date and time
fun millis() = System.currentTimeMillis() fun millis() = System.currentTimeMillis()
val DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd") val DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd")

View File

@@ -68,7 +68,13 @@ data class GenericMusicMeta(
@Serializable @Serializable
data class GenericNoteMeta( data class GenericNoteMeta(
val lv: Double, val lv: Double,
val lvId: Int )
@Serializable
data class GenericItemMeta(
val name: String? = null,
val disable: Boolean? = null,
val ver: String? = null
) )
// Here are some interfaces to generalize across multiple games // Here are some interfaces to generalize across multiple games
@@ -111,12 +117,10 @@ interface GenericPlaylogRepo<T: IGenericGamePlaylog> : JpaRepository<T, Long> {
} }
abstract class GameApiController(name: String) { abstract class GameApiController(name: String) {
val musicMapping: Map<Int, GenericMusicMeta> = GameApiController::class.java val musicMapping = resJson<Map<String, GenericMusicMeta>>("/meta/$name/music.json")
.getResourceAsStream("/meta/$name/music.json") ?.mapKeys { it.key.toInt() } ?: emptyMap()
.use { it?.reader()?.readText() }
?.let { JSON.decodeFromString<Map<String, GenericMusicMeta>>(it) } val itemMapping = resJson<Map<String, Map<String, GenericItemMeta>>>("/meta/$name/items.json") ?: emptyMap()
?.mapKeys { it.key.toInt() }
?: emptyMap()
abstract val us: AquaUserServices abstract val us: AquaUserServices
abstract val userDataRepo: GenericUserDataRepo<*> abstract val userDataRepo: GenericUserDataRepo<*>

View File

@@ -4,6 +4,7 @@ import icu.samnyan.aqua.net.games.Maimai2
import icu.samnyan.aqua.sega.general.dao.CardRepository import icu.samnyan.aqua.sega.general.dao.CardRepository
import icu.samnyan.aqua.sega.maimai2.handler.BaseHandler import icu.samnyan.aqua.sega.maimai2.handler.BaseHandler
import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos import icu.samnyan.aqua.sega.maimai2.model.Mai2Repos
import icu.samnyan.aqua.sega.maimai2.model.userdata.UserItem.Mai2ItemKind
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import org.springframework.data.domain.PageRequest import org.springframework.data.domain.PageRequest
@@ -25,7 +26,21 @@ class GetUserItemHandler(
"itemId" to it.key, "itemId" to it.key,
"stock" to 1, "stock" to 1,
"isValid" to true, "isValid" to true,
).toMap() } } ) } }
val itemUnlock = Mai2ItemKind.ALL.filter { it.key in 1..3 || it.key in 9..12 }
.mapValues { (kind, kindEnum) -> maimai2.itemMapping[kindEnum.name]?.map { (id, item) ->
mapOf(
"itemKind" to kind,
"itemId" to id,
"stock" to 1,
"isValid" to true,
) } ?: emptyList() }
init {
if (musicUnlock[5].isNullOrEmpty()) logger.warn("Mai2 music info is empty")
if (itemUnlock[1].isNullOrEmpty()) logger.warn("Mai2 item info is empty")
}
override fun handle(request: Map<String, Any>): Any { override fun handle(request: Map<String, Any>): Any {
val userId = (request["userId"] as Number).toLong() val userId = (request["userId"] as Number).toLong()
@@ -35,19 +50,28 @@ class GetUserItemHandler(
val kind = (nextIndexVal / MULT).toInt() val kind = (nextIndexVal / MULT).toInt()
val nextIndex = (nextIndexVal % MULT).toInt() val nextIndex = (nextIndexVal % MULT).toInt()
val pageNum = nextIndex / maxCount val pageNum = nextIndex / maxCount
val kindType = Mai2ItemKind.ALL[kind]?.name
// Aqua Net game unlock feature // Aqua Net game unlock feature
cardRepo.findByExtId(userId).getOrNull()?.aquaUser?.gameOptions?.let { opt -> cardRepo.findByExtId(userId).getOrNull()?.aquaUser?.gameOptions?.let { opt ->
// All Music unlock val items = when {
if (kind in 5..8 && opt.unlockMusic) { (kind in 5..8) && opt.unlockMusic -> musicUnlock.getValue(kind)
logger.info("Response: ${maimai2.musicMapping.size} items - Music unlock") (kind in 1..3 || kind == 11) && opt.unlockCollectables -> itemUnlock[kind]
return mapOf( (kind == 12) && opt.unlockTickets -> itemUnlock[kind]
"userId" to userId, (kind in 9..10) && opt.unlockChara -> itemUnlock[kind]
"nextIndex" to 0, else -> emptyList()
"itemKind" to kind,
"userItemList" to musicUnlock.getValue(kind)
)
} }
// If no items are found, disable the unlock feature
if (items.isNullOrEmpty()) return@let
logger.info("Response: ${items.size} $kindType items - All unlock")
return mapOf(
"userId" to userId,
"nextIndex" to 0,
"itemKind" to kind,
"userItemList" to items
)
} }
val dbPage = repos.userItem.findByUser_Card_ExtIdAndItemKind(userId, kind, PageRequest.of(pageNum, maxCount)) val dbPage = repos.userItem.findByUser_Card_ExtIdAndItemKind(userId, kind, PageRequest.of(pageNum, maxCount))
@@ -60,7 +84,7 @@ class GetUserItemHandler(
"userItemList" to dbPage.content "userItemList" to dbPage.content
) )
logger.info("Response: ${dbPage.numberOfElements} items") logger.info("Response: ${dbPage.numberOfElements} $kindType items")
return result return result
} }

View File

@@ -1,13 +1,15 @@
package icu.samnyan.aqua.sega.maimai2.model.userdata; package icu.samnyan.aqua.sega.maimai2.model.userdata;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import jakarta.persistence.*; import jakarta.persistence.*;
@@ -40,15 +42,26 @@ public class UserItem implements Serializable {
this.user = user; this.user = user;
} }
public static final int KIND_NAMEPLATE = 1; public enum Mai2ItemKind {
public static final int KIND_TITLE = 2; plate(1),
public static final int KIND_ICON = 3; title(2),
public static final int KIND_MUSIC_UNLOCK = 5; icon(3),
public static final int KIND_MUSIC_MASTER_UNLOCK = 6; musicUnlock(5),
public static final int KIND_MUSIC_REMASTER_UNLOCK = 7; musicMasterUnlock(6),
public static final int KIND_MUSIC_STRONG_UNLOCK = 8; musicRemasterUnlock(7),
public static final int KIND_CHARACTER = 9; musicStrongUnlock(8),
public static final int KIND_PARTNER = 10; chara(9),
public static final int KIND_FRAME = 11; partner(10),
public static final int KIND_TICKETS = 12; frame(11),
ticket(12);
public final int value;
Mai2ItemKind(int value) {
this.value = value;
}
public static final Map<Integer, Mai2ItemKind> ALL = Arrays.stream(Mai2ItemKind.class.getEnumConstants())
.map(k -> Map.entry(k.value, k)).collect(HashMap::new, (m, v) -> m.put(v.getKey(), v.getValue()), Map::putAll);
}
} }

View File

@@ -2,7 +2,6 @@ package icu.samnyan.aqua.spring
import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.pattern.CompositeConverter import ch.qos.logback.core.pattern.CompositeConverter
import ext.center
import ext.get import ext.get
import org.springframework.boot.ansi.AnsiColor import org.springframework.boot.ansi.AnsiColor
import org.springframework.boot.ansi.AnsiOutput import org.springframework.boot.ansi.AnsiOutput
@@ -10,7 +9,7 @@ import org.springframework.boot.ansi.AnsiOutput
private const val PROJECT_PACKAGE_PREFIX = "icu.samnyan.aqua" private const val PROJECT_PACKAGE_PREFIX = "icu.samnyan.aqua"
private const val SEGA_PACKAGE_PREFIX = "icu.samnyan.aqua.sega" private const val SEGA_PACKAGE_PREFIX = "icu.samnyan.aqua.sega"
private val SYSTEM_COLOR = AnsiColor.WHITE private val SYSTEM_COLOR = AnsiColor.WHITE
private val SEGA_COLOR = AnsiColor.MAGENTA private val SEGA_COLOR = AnsiColor.BRIGHT_CYAN
private val MISC_COLOR = AnsiColor.BRIGHT_BLUE private val MISC_COLOR = AnsiColor.BRIGHT_BLUE
class LoggerComponent : CompositeConverter<ILoggingEvent>() { class LoggerComponent : CompositeConverter<ILoggingEvent>() {

View File

@@ -7,4 +7,5 @@ mkdir -p "$DIR/ongeki" "$DIR/mai2" "$DIR/chu3"
curl "https://aquadx.net/d/ongeki/00/all-music.json" -o "$DIR/ongeki/music.json" curl "https://aquadx.net/d/ongeki/00/all-music.json" -o "$DIR/ongeki/music.json"
curl "https://aquadx.net/d/mai2/00/all-music.json" -o "$DIR/mai2/music.json" curl "https://aquadx.net/d/mai2/00/all-music.json" -o "$DIR/mai2/music.json"
curl "https://aquadx.net/d/mai2/00/all-items.json" -o "$DIR/mai2/items.json"
curl "https://aquadx.net/d/chu3/00/all-music.json" -o "$DIR/chu3/music.json" curl "https://aquadx.net/d/chu3/00/all-music.json" -o "$DIR/chu3/music.json"