From a9e14a93ddee8cb7105266e5b684ce63d458cae4 Mon Sep 17 00:00:00 2001 From: Azalea <22280294+hykilpikonna@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:24:56 -0500 Subject: [PATCH] [F] Fix mai2 playlog by introducing a backlog --- .../sega/maimai2/handler/BaseHandler.java | 2 +- .../impl/UploadUserPlaylogHandler.java | 59 ------------------- .../handler/impl/UploadUserPlaylogHandler.kt | 55 +++++++++++++++++ .../handler/impl/UpsertUserAllHandler.java | 29 +++------ 4 files changed, 65 insertions(+), 80 deletions(-) delete mode 100644 src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.kt diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/BaseHandler.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/BaseHandler.java index 97ff46f2..f4f2a6c5 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/BaseHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/BaseHandler.java @@ -9,5 +9,5 @@ import java.util.Map; */ public interface BaseHandler { - String handle(Map request) throws JsonProcessingException; + Object handle(Map request) throws JsonProcessingException; } diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.java deleted file mode 100644 index 0520e947..00000000 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.java +++ /dev/null @@ -1,59 +0,0 @@ -package icu.samnyan.aqua.sega.maimai2.handler.impl; - -import com.fasterxml.jackson.core.JsonProcessingException; - -import icu.samnyan.aqua.sega.maimai2.dao.userdata.UserDataRepository; -import icu.samnyan.aqua.sega.maimai2.dao.userdata.UserPlaylogRepository; -import icu.samnyan.aqua.sega.maimai2.handler.BaseHandler; -import icu.samnyan.aqua.sega.maimai2.model.request.UploadUserPlaylog; -import icu.samnyan.aqua.sega.maimai2.model.userdata.UserDetail; -import icu.samnyan.aqua.sega.maimai2.model.userdata.UserPlaylog; -import icu.samnyan.aqua.sega.util.jackson.BasicMapper; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.Optional; - -/** - * @author samnyan (privateamusement@protonmail.com) - */ -@Component("Maimai2UploadUserPlaylogHandler") -public class UploadUserPlaylogHandler implements BaseHandler { - - private static final Logger logger = LoggerFactory.getLogger(UploadUserPlaylogHandler.class); - - private final BasicMapper mapper; - private final UserPlaylogRepository userPlaylogRepository; - private final UserDataRepository userDataRepository; - - public UploadUserPlaylogHandler(UserDataRepository userDataRepository, UserPlaylogRepository userPlaylogRepository, BasicMapper mapper) { - this.userDataRepository = userDataRepository; - this.userPlaylogRepository = userPlaylogRepository; - this.mapper = mapper; - } - - @Override - public String handle(Map request) throws JsonProcessingException { - UploadUserPlaylog uploadUserPlaylog = mapper.convert(request, UploadUserPlaylog.class); - - Optional userOptional = userDataRepository.findByCardExtId(uploadUserPlaylog.getUserId()); - - /* - Due to how we handle userId, first user playlog can't be saved. - (sequence order swapped, it sends playlog then user detail) - It might be possible to fix this with some workaround, but leave it like this at this time. - */ - if (userOptional.isPresent()) { - UserDetail userDetail = userOptional.get(); - - UserPlaylog userPlaylog = uploadUserPlaylog.getUserPlaylog(); - - userPlaylog.setUser(userDetail); - userPlaylogRepository.save(userPlaylog); - } - - return "{\"returnCode\":1,\"apiName\":\"com.sega.maimai2servlet.api.UploadUserPlaylogApi\"}"; - } -} diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.kt b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.kt new file mode 100644 index 00000000..f01af649 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UploadUserPlaylogHandler.kt @@ -0,0 +1,55 @@ +package icu.samnyan.aqua.sega.maimai2.handler.impl + +import ext.millis +import icu.samnyan.aqua.sega.maimai2.dao.userdata.UserDataRepository +import icu.samnyan.aqua.sega.maimai2.dao.userdata.UserPlaylogRepository +import icu.samnyan.aqua.sega.maimai2.handler.BaseHandler +import icu.samnyan.aqua.sega.maimai2.model.request.UploadUserPlaylog +import icu.samnyan.aqua.sega.maimai2.model.userdata.UserPlaylog +import icu.samnyan.aqua.sega.util.jackson.BasicMapper +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Component +import kotlin.jvm.optionals.getOrNull + +/** + * @author samnyan (privateamusement@protonmail.com) + */ +@Component("Maimai2UploadUserPlaylogHandler") +class UploadUserPlaylogHandler( + private val userDataRepository: UserDataRepository, + private val playlogRepo: UserPlaylogRepository, + private val mapper: BasicMapper +) : BaseHandler { + data class BacklogEntry(val time: Long, val playlog: UserPlaylog) + companion object { + @JvmStatic + val playBacklog = mutableMapOf>() + } + + override fun handle(request: Map): String { + val req = mapper.convert(request, UploadUserPlaylog::class.java) + + // Save if the user is registered + val u = userDataRepository.findByCardExtId(req.userId).getOrNull() + if (u != null) playlogRepo.save(req.userPlaylog.apply { user = u }) + + // If the user hasn't registered (first play), save the playlog to a backlog + else { + playBacklog.putIfAbsent(req.userId, mutableListOf()) + playBacklog[req.userId]?.apply { + add(BacklogEntry(millis(), req.userPlaylog)) + if (size > 6) clear() // Prevent abuse + } + } + + return """{"returnCode":1,"apiName":"com.sega.maimai2servlet.api.UploadUserPlaylogApi"}""" + } + + @Scheduled(fixedDelay = 60_000) + fun cleanBacklog() { + // Clean all backlog entries that are older than 5 minutes + val now = millis() + playBacklog.filter { (k, v) -> v.isEmpty() || v[0].time - now > 300_000 }.toList() + .forEach { (k, _) -> playBacklog.remove(k) } + } +} diff --git a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java index d3f7081f..5f067216 100644 --- a/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/maimai2/handler/impl/UpsertUserAllHandler.java @@ -11,6 +11,7 @@ import icu.samnyan.aqua.sega.maimai2.model.response.data.UserActivity; import icu.samnyan.aqua.sega.maimai2.model.response.data.UserRating; import icu.samnyan.aqua.sega.maimai2.model.userdata.*; import icu.samnyan.aqua.sega.util.jackson.BasicMapper; +import lombok.AllArgsConstructor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -23,6 +24,7 @@ import java.util.Optional; /** * @author samnyan (privateamusement@protonmail.com) */ +@AllArgsConstructor @Component("Maimai2UpsertUserAllHandler") public class UpsertUserAllHandler implements BaseHandler { @@ -46,25 +48,7 @@ public class UpsertUserAllHandler implements BaseHandler { private final UserGeneralDataRepository userGeneralDataRepository; private final UserCourseRepository userCourseRepository; private final UserFriendSeasonRankingRepository userFriendSeasonRankingRepository; - - public UpsertUserAllHandler(BasicMapper mapper, CardService cardService, UserDataRepository userDataRepository, UserExtendRepository userExtendRepository, UserOptionRepository userOptionRepository, UserItemRepository userItemRepository, UserMusicDetailRepository userMusicDetailRepository, UserActRepository userActRepository, UserCharacterRepository userCharacterRepository, UserMapRepository userMapRepository, UserLoginBonusRepository userLoginBonusRepository, UserFavoriteRepository userFavoriteRepository, UserUdemaeRepository userUdemaeRepository, UserGeneralDataRepository userGeneralDataRepository, UserCourseRepository userCourseRepository, UserFriendSeasonRankingRepository userFriendSeasonRankingRepository) { - this.mapper = mapper; - this.cardService = cardService; - this.userDataRepository = userDataRepository; - this.userExtendRepository = userExtendRepository; - this.userOptionRepository = userOptionRepository; - this.userItemRepository = userItemRepository; - this.userMusicDetailRepository = userMusicDetailRepository; - this.userActRepository = userActRepository; - this.userCharacterRepository = userCharacterRepository; - this.userMapRepository = userMapRepository; - this.userLoginBonusRepository = userLoginBonusRepository; - this.userFavoriteRepository = userFavoriteRepository; - this.userUdemaeRepository = userUdemaeRepository; - this.userGeneralDataRepository = userGeneralDataRepository; - this.userCourseRepository = userCourseRepository; - this.userFriendSeasonRankingRepository = userFriendSeasonRankingRepository; - } + private final UserPlaylogRepository userPlaylogRepository; @Override public String handle(Map request) throws JsonProcessingException { @@ -105,6 +89,11 @@ public class UpsertUserAllHandler implements BaseHandler { // Set isNetMember value to 1, which enables some in-game features. newUserData.setNetMember(1); userDataRepository.saveAndFlush(newUserData); + + // Check playlog backlog + var backlog = UploadUserPlaylogHandler.getPlayBacklog(); + if (backlog.containsKey(userId)) + backlog.remove(userId).forEach(it -> userPlaylogRepository.save(it.getPlaylog())); } // UserExtend @@ -343,7 +332,7 @@ public class UpsertUserAllHandler implements BaseHandler { sb.append(item.getMusicId()).append(":").append(item.getLevel()).append(":").append(item.getRomVersion()).append(":").append(item.getAchievement()); sb.append(","); } - if (sb.length() > 0) { + if (!sb.isEmpty()) { sb.deleteCharAt(sb.length() - 1); } Optional uOptional = userGeneralDataRepository.findByUserAndPropertyKey(newUserData, key);