diff --git a/src/main/java/icu/samnyan/aqua/sega/allnet/AllNetController.java b/src/main/java/icu/samnyan/aqua/sega/allnet/AllNetController.java index e8ba0b72..3ec26c1c 100644 --- a/src/main/java/icu/samnyan/aqua/sega/allnet/AllNetController.java +++ b/src/main/java/icu/samnyan/aqua/sega/allnet/AllNetController.java @@ -188,7 +188,7 @@ public class AllNetController { return "http://" + addr + ":" + port + "/Maimai2Servlet/"; } case "SDHD": - return "http://" + addr + ":" + port + "/ChusanServlet/"; + return "http://" + addr + ":" + port + "/ChusanServlet/" + ver + "/"; case "SDED": return "http://" + addr + ":" + port + "/CardMakerServlet/"; default: diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/controller/ChusanServletController.java b/src/main/java/icu/samnyan/aqua/sega/chusan/controller/ChusanServletController.java index 79c3915c..dbce8059 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/controller/ChusanServletController.java +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/controller/ChusanServletController.java @@ -12,7 +12,7 @@ import java.util.Map; * @author samnyan (privateamusement@protonmail.com) */ @RestController -@RequestMapping({"/ChusanServlet/ChuniServlet", "/ChusanServlet"}) +@RequestMapping({"/ChusanServlet/{version}/ChuniServlet", "/ChusanServlet/{version}"}) public class ChusanServletController { private final GameLoginHandler gameLoginHandler; @@ -235,7 +235,8 @@ public class ChusanServletController { } @PostMapping("GetUserMusicApi") - String getUserMusic(@ModelAttribute Map request) throws JsonProcessingException { + String getUserMusic(@ModelAttribute Map request, @PathVariable String version) throws JsonProcessingException { + request.put("version", version); return getUserMusicHandler.handle(request); } diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/dto/UserMusicDetailForAncientChusan.java b/src/main/java/icu/samnyan/aqua/sega/chusan/dto/UserMusicDetailForAncientChusan.java new file mode 100644 index 00000000..43868a9e --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/dto/UserMusicDetailForAncientChusan.java @@ -0,0 +1,84 @@ +package icu.samnyan.aqua.sega.chusan.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonPropertyOrder({ + "musicId", + "level", + "playCount", + "scoreMax", + "missCount", + "maxComboCount", + "isFullCombo", + "isAllJustice", + "isSuccess", + "fullChain", + "maxChain", + "isLock", + "theoryCount", + "ext1" +}) +public class UserMusicDetailForAncientChusan implements Serializable { + private static final long serialVersionUID = 1L; + + private int musicId; + + private int level; + + private int playCount; + + private int scoreMax; + + private int missCount; + + private int maxComboCount; + + @JsonProperty("isFullCombo") + private boolean isFullCombo; + + @JsonProperty("isAllJustice") + private boolean isAllJustice; + + @JsonProperty("isSuccess") + private boolean isSuccess; + + private int fullChain; + + private int maxChain; + + private int scoreRank; + + @JsonProperty("isLock") + private boolean isLock; + + private int theoryCount; + + private int ext1; + + public UserMusicDetailForAncientChusan(int musicId, int level, int playCount, int scoreMax, int missCount, int maxComboCount, boolean isFullCombo, boolean isAllJustice, int isSuccess, int fullChain, int maxChain, int scoreRank, boolean isLock, int theoryCount, int ext1) { + this.musicId = musicId; + this.level = level; + this.playCount = playCount; + this.scoreMax = scoreMax; + this.missCount = missCount; + this.maxComboCount = maxComboCount; + this.isFullCombo = isFullCombo; + this.isAllJustice = isAllJustice; + this.isSuccess = isSuccess > 0; + this.fullChain = fullChain; + this.maxChain = maxChain; + this.scoreRank = scoreRank; + this.isLock = isLock; + this.theoryCount = theoryCount; + this.ext1 = ext1; + } +} diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/dto/UserMusicListItemForAncientChusan.java b/src/main/java/icu/samnyan/aqua/sega/chusan/dto/UserMusicListItemForAncientChusan.java new file mode 100644 index 00000000..23974297 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/dto/UserMusicListItemForAncientChusan.java @@ -0,0 +1,19 @@ +package icu.samnyan.aqua.sega.chusan.dto; + +import icu.samnyan.aqua.sega.chusan.model.userdata.UserMusicDetail; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author samnyan (privateamusement@protonmail.com) + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserMusicListItemForAncientChusan { + private int length; + private List userMusicDetailList; +} diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/handler/impl/GetUserMusicHandler.java b/src/main/java/icu/samnyan/aqua/sega/chusan/handler/impl/GetUserMusicHandler.java index f83b7cde..cdd0c425 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/handler/impl/GetUserMusicHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/handler/impl/GetUserMusicHandler.java @@ -1,6 +1,8 @@ package icu.samnyan.aqua.sega.chusan.handler.impl; import com.fasterxml.jackson.core.JsonProcessingException; +import icu.samnyan.aqua.sega.chusan.dto.UserMusicDetailForAncientChusan; +import icu.samnyan.aqua.sega.chusan.dto.UserMusicListItemForAncientChusan; import icu.samnyan.aqua.sega.chusan.handler.BaseHandler; import icu.samnyan.aqua.sega.chusan.model.response.data.UserMusicListItem; import icu.samnyan.aqua.sega.chusan.model.userdata.UserMusicDetail; @@ -78,13 +80,51 @@ public class GetUserMusicHandler implements BaseHandler { userMusicMap.remove(lastMusicId); } + String version = (String) request.get("version"); + Map userMusicMapForAncientChusan = new LinkedHashMap<>(); + boolean isAncient = false; + try { + if (Double.parseDouble(version) < 2.15){ + userMusicMap.forEach((k, v) -> { + UserMusicListItemForAncientChusan list = new UserMusicListItemForAncientChusan(); + list.setLength(v.getLength()); + List userMusicDetailForAncientChusanList = new ArrayList<>(); + v.getUserMusicDetailList().forEach(userMusicDetail -> { + UserMusicDetailForAncientChusan userMusicDetailForAncientChusan = new UserMusicDetailForAncientChusan( + userMusicDetail.getMusicId(), + userMusicDetail.getLevel(), + userMusicDetail.getPlayCount(), + userMusicDetail.getScoreMax(), + userMusicDetail.getMissCount(), + userMusicDetail.getMaxComboCount(), + userMusicDetail.isFullCombo(), + userMusicDetail.isAllJustice(), + userMusicDetail.getIsSuccess(), + userMusicDetail.getFullChain(), + userMusicDetail.getMaxChain(), + userMusicDetail.getScoreRank(), + userMusicDetail.isLock(), + userMusicDetail.getTheoryCount(), + userMusicDetail.getExt1() + ); + userMusicDetailForAncientChusanList.add(userMusicDetailForAncientChusan); + }); + list.setUserMusicDetailList(userMusicDetailForAncientChusanList); + userMusicMapForAncientChusan.put(k, list); + }); + isAncient = true; + } + } catch (Exception e) { + logger.error("Error when handling ancient version of chusan", e); + } + long nextIndex = currentIndex + dbPage.getNumberOfElements() - lastListSize; Map resultMap = new LinkedHashMap<>(); resultMap.put("userId", userId); resultMap.put("length", userMusicMap.size()); resultMap.put("nextIndex", dbPage.getNumberOfElements() < maxCount ? -1 : nextIndex); - resultMap.put("userMusicList", userMusicMap.values()); + resultMap.put("userMusicList", isAncient ? userMusicMapForAncientChusan.values() : userMusicMap.values()); String json = mapper.write(resultMap); logger.info("Response: " + json); diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/model/userdata/UserMusicDetail.java b/src/main/java/icu/samnyan/aqua/sega/chusan/model/userdata/UserMusicDetail.java index 71a92dec..69f991b3 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/model/userdata/UserMusicDetail.java +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/model/userdata/UserMusicDetail.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import icu.samnyan.aqua.sega.chusan.util.BooleanStringIntDeserializer; +import icu.samnyan.aqua.sega.chusan.util.BooleanToIntegerDeserializer; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -69,9 +69,9 @@ public class UserMusicDetail implements Serializable { @JsonProperty("isAllJustice") private boolean isAllJustice; - @JsonDeserialize(using = BooleanStringIntDeserializer.class) + @JsonDeserialize(using = BooleanToIntegerDeserializer.class) @JsonProperty("isSuccess") - private boolean isSuccess; + private int isSuccess; private int fullChain; @@ -90,7 +90,7 @@ public class UserMusicDetail implements Serializable { user = userData; } - public UserMusicDetail(int musicId, int level, int playCount, int scoreMax, int missCount, int maxComboCount, boolean isFullCombo, boolean isAllJustice, boolean isSuccess, int fullChain, int maxChain, int scoreRank, boolean isLock, int theoryCount, int ext1) { + public UserMusicDetail(int musicId, int level, int playCount, int scoreMax, int missCount, int maxComboCount, boolean isFullCombo, boolean isAllJustice, int isSuccess, int fullChain, int maxChain, int scoreRank, boolean isLock, int theoryCount, int ext1) { this.musicId = musicId; this.level = level; this.playCount = playCount; diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/util/BooleanStringIntDeserializer.java b/src/main/java/icu/samnyan/aqua/sega/chusan/util/BooleanStringIntDeserializer.java deleted file mode 100644 index 6f506e3f..00000000 --- a/src/main/java/icu/samnyan/aqua/sega/chusan/util/BooleanStringIntDeserializer.java +++ /dev/null @@ -1,21 +0,0 @@ -package icu.samnyan.aqua.sega.chusan.util; - -import com.fasterxml.jackson.core.JacksonException; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonDeserializer; - -import java.io.IOException; - -public class BooleanStringIntDeserializer extends JsonDeserializer { - @Override - public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { - return switch (p.getCurrentToken()) { - case VALUE_STRING -> p.getText().trim().equals("1") || p.getText().trim().equalsIgnoreCase("true"); - case VALUE_NUMBER_INT -> p.getIntValue() == 1; - case VALUE_TRUE -> true; - case VALUE_FALSE -> false; - default -> throw new UnsupportedOperationException("Cannot deserialize to boolean field"); - }; - } -} diff --git a/src/main/java/icu/samnyan/aqua/sega/chusan/util/BooleanToIntegerDeserializer.java b/src/main/java/icu/samnyan/aqua/sega/chusan/util/BooleanToIntegerDeserializer.java new file mode 100644 index 00000000..d5b8288a --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/chusan/util/BooleanToIntegerDeserializer.java @@ -0,0 +1,35 @@ +package icu.samnyan.aqua.sega.chusan.util; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; +import java.util.Locale; + +public class BooleanToIntegerDeserializer extends JsonDeserializer { + @Override + public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + return switch (p.getCurrentToken()) { + case VALUE_STRING -> { + String value = p.getValueAsString(); + if (value.toLowerCase(Locale.ROOT).equals("true")) { + yield 1; + } else if (value.toLowerCase(Locale.ROOT).equals("false")) { + yield 0; + } else { + try { + yield Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new UnsupportedOperationException("Cannot deserialize to integer field"); + } + } + } + case VALUE_NUMBER_INT -> p.getIntValue(); + case VALUE_TRUE -> 1; + case VALUE_FALSE -> 0; + default -> throw new UnsupportedOperationException("Cannot deserialize to integer field"); + }; + } +} diff --git a/src/main/resources/db/migration/mariadb/V245__add_chusan_sunplus_compatibility.sql b/src/main/resources/db/migration/mariadb/V245__add_chusan_sunplus_compatibility.sql new file mode 100644 index 00000000..7b4a4549 --- /dev/null +++ b/src/main/resources/db/migration/mariadb/V245__add_chusan_sunplus_compatibility.sql @@ -0,0 +1,2 @@ +ALTER TABLE chusan_user_music_detail +MODIFY is_success INTEGER NOT NULL; diff --git a/src/main/resources/db/migration/mysql/V245__add_chusan_sunplus_compatibility.sql b/src/main/resources/db/migration/mysql/V245__add_chusan_sunplus_compatibility.sql new file mode 100644 index 00000000..7b4a4549 --- /dev/null +++ b/src/main/resources/db/migration/mysql/V245__add_chusan_sunplus_compatibility.sql @@ -0,0 +1,2 @@ +ALTER TABLE chusan_user_music_detail +MODIFY is_success INTEGER NOT NULL; diff --git a/src/main/resources/db/migration/sqlite/V245__add_chusan_sunplus_compatibility.sql b/src/main/resources/db/migration/sqlite/V245__add_chusan_sunplus_compatibility.sql new file mode 100644 index 00000000..90290431 --- /dev/null +++ b/src/main/resources/db/migration/sqlite/V245__add_chusan_sunplus_compatibility.sql @@ -0,0 +1,32 @@ +-- Step 1: Create a new table with the desired changes +CREATE TABLE temp_chusan_user_music_detail ( + id INTEGER, + full_chain INTEGER NOT NULL, + is_all_justice BOOLEAN NOT NULL, + is_full_combo BOOLEAN NOT NULL, + is_lock BOOLEAN NOT NULL, + is_success INTEGER NOT NULL, -- Changed to INTEGER + level INTEGER NOT NULL, + max_chain INTEGER NOT NULL, + max_combo_count INTEGER NOT NULL, + miss_count INTEGER NOT NULL, + music_id INTEGER NOT NULL, + play_count INTEGER NOT NULL, + theory_count INTEGER, + ext1 INTEGER, + score_max INTEGER NOT NULL, + score_rank INTEGER NOT NULL, + user_id BIGINT REFERENCES chusan_user_data (id) ON DELETE CASCADE, + PRIMARY KEY (id), + CONSTRAINT chusan_user_music_detail_uq UNIQUE (level, music_id, user_id) +); + +-- Step 2: Copy the data from the original table to the new table +INSERT INTO temp_chusan_user_music_detail +SELECT * FROM chusan_user_music_detail; + +-- Step 3: Delete the original table +DROP TABLE chusan_user_music_detail; + +-- Step 4: Rename the new table to the original table's name +ALTER TABLE temp_chusan_user_music_detail RENAME TO chusan_user_music_detail;