[F] Fix mai2 playlog by introducing a backlog

pull/17/head
Azalea 2024-02-29 16:24:56 -05:00
parent 8e2c0d8653
commit a9e14a93dd
4 changed files with 65 additions and 80 deletions

View File

@ -9,5 +9,5 @@ import java.util.Map;
*/
public interface BaseHandler {
String handle(Map<String, Object> request) throws JsonProcessingException;
Object handle(Map<String, Object> request) throws JsonProcessingException;
}

View File

@ -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<String, Object> request) throws JsonProcessingException {
UploadUserPlaylog uploadUserPlaylog = mapper.convert(request, UploadUserPlaylog.class);
Optional<UserDetail> 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\"}";
}
}

View File

@ -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<Long, MutableList<BacklogEntry>>()
}
override fun handle(request: Map<String, Any>): 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) }
}
}

View File

@ -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<String, Object> 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<UserGeneralData> uOptional = userGeneralDataRepository.findByUserAndPropertyKey(newUserData, key);