From aa2e8cdea4b425514c72a67f094c227dd92885d8 Mon Sep 17 00:00:00 2001 From: samnyan <4137880+samnyan@users.noreply.github.com> Date: Mon, 14 Dec 2020 17:18:21 +0800 Subject: [PATCH] [chuni] Add auto version matching --- .../aqua/sega/allnet/AllNetController.java | 18 +++++--- .../controller/ChuniServletController.java | 11 ++--- .../ChuniServletControllerAdvice.java | 10 ++++- .../handler/impl/GetUserDataHandler.java | 18 ++++++-- .../handler/impl/GetUserPreviewHandler.java | 20 +++++++-- .../impl/UpsertClientSettingHandler.java | 34 ++++++++++++++- .../chunithm/model/requet/ClientSetting.java | 21 ++++++++++ .../model/requet/ClientSettingRequest.java | 12 ++++++ .../general/dao/GameVersionRepository.java | 21 ++++++++++ .../aqua/sega/general/model/GameVersion.java | 21 ++++++++++ .../general/service/ClientSettingService.java | 42 +++++++++++++++++++ .../icu/samnyan/aqua/sega/util/AquaConst.java | 8 ++++ .../samnyan/aqua/sega/util/VersionUtil.java | 25 +++++++++++ .../mysql/V25__add_client_setting.sql | 8 ++++ .../sqlite/V25__add_client_setting.sql | 8 ++++ 15 files changed, 256 insertions(+), 21 deletions(-) create mode 100644 src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSetting.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSettingRequest.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/general/dao/GameVersionRepository.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/general/model/GameVersion.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/general/service/ClientSettingService.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/util/AquaConst.java create mode 100644 src/main/java/icu/samnyan/aqua/sega/util/VersionUtil.java create mode 100644 src/main/resources/db/migration/mysql/V25__add_client_setting.sql create mode 100644 src/main/resources/db/migration/sqlite/V25__add_client_setting.sql 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 5d9b0634..5d9fb099 100644 --- a/src/main/java/icu/samnyan/aqua/sega/allnet/AllNetController.java +++ b/src/main/java/icu/samnyan/aqua/sega/allnet/AllNetController.java @@ -17,8 +17,11 @@ import java.io.InputStream; import java.time.Instant; import java.time.LocalDateTime; import java.util.Map; +import java.util.UUID; import java.util.zip.DataFormatException; +import static icu.samnyan.aqua.sega.util.AquaConst.DEFAULT_KEYCHIP_ID; + /** * @author samnyan (privateamusement@protonmail.com) */ @@ -53,17 +56,22 @@ public class AllNetController { byte[] bytes = dataStream.readAllBytes(); Map reqMap = Decoder.decode(bytes); - logger.info("Request: PowerOn, " + new ObjectMapper().writeValueAsString(reqMap)); + logger.info("Request: PowerOn, " + mapper.writeValueAsString(reqMap)); // TODO: Verify KeyChip id String gameId = reqMap.getOrDefault("game_id", ""); + String ver = reqMap.getOrDefault("ver", "1.0"); + String serial = reqMap.getOrDefault("serial", DEFAULT_KEYCHIP_ID); + if (serial.equals(DEFAULT_KEYCHIP_ID)) { + serial = UUID.randomUUID().toString(); + } String format_ver = reqMap.getOrDefault("format_ver", ""); PowerOnResponse resp; if (format_ver.startsWith("2")) { var now = LocalDateTime.now(); resp = new PowerOnResponseV2( 1, - switchUri(gameId), + switchUri(gameId, ver, serial), switchHost(gameId), "123", "", @@ -87,7 +95,7 @@ public class AllNetController { } else { resp = new PowerOnResponseV3( 1, - switchUri(gameId), + switchUri(gameId, ver, serial), switchHost(gameId), "123", "", @@ -110,10 +118,10 @@ public class AllNetController { return resp.toString().concat("\n"); } - private String switchUri(String gameId) { + private String switchUri(String gameId, String ver, String serial) { switch (gameId) { case "SDBT": - return "http://" + HOST + ":" + PORT + "/"; + return "http://" + HOST + ":" + PORT + "/ChuniServlet/" + ver + "/" + serial + "/"; case "SBZV": return "http://" + HOST + ":" + PORT + "/diva/"; case "SDDT": diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletController.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletController.java index e2ecfd27..48883e98 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletController.java +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletController.java @@ -4,10 +4,7 @@ package icu.samnyan.aqua.sega.chunithm.controller; import com.fasterxml.jackson.core.JsonProcessingException; import icu.samnyan.aqua.sega.chunithm.handler.impl.*; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.Map; @@ -15,7 +12,7 @@ import java.util.Map; * @author samnyan (privateamusement@protonmail.com) */ @RestController -@RequestMapping("ChuniServlet") +@RequestMapping({"/ChuniServlet/{ROM_VERSION}/{CLIENT_ID}/ChuniServlet", "/ChuniServlet"}) public class ChuniServletController { private final GameLoginHandler gameLoginHandler; @@ -242,8 +239,8 @@ public class ChuniServletController { } @PostMapping("UpsertClientSettingApi") - String upsertClientSetting(@ModelAttribute Map request) { - return "{\"returnCode\":\"1\"}"; + String upsertClientSetting(@ModelAttribute Map request) throws JsonProcessingException { + return upsertClientSettingHandler.handle(request); } @PostMapping("UpsertClientTestmodeApi") diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletControllerAdvice.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletControllerAdvice.java index efcfd574..edce6dc7 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletControllerAdvice.java +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/controller/ChuniServletControllerAdvice.java @@ -5,13 +5,17 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.HandlerMapping; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Map; +import static icu.samnyan.aqua.sega.util.AquaConst.*; + /** * @author samnyan (privateamusement@protonmail.com) */ @@ -28,12 +32,16 @@ public class ChuniServletControllerAdvice { */ @ModelAttribute public Map preHandle(HttpServletRequest request) throws IOException { + var pathVar = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); byte[] src = request.getInputStream().readAllBytes(); String outputString = new String(src, StandardCharsets.UTF_8).trim(); logger.info("Request " + request.getRequestURI() + ": " + outputString); ObjectMapper mapper = new ObjectMapper(); - return mapper.readValue(outputString, new TypeReference<>() { + Map result = mapper.readValue(outputString, new TypeReference<>() { }); + result.put(SERIAL_KEY, pathVar.getOrDefault(SERIAL_KEY, DEFAULT_KEYCHIP_ID)); + result.put(VERSION_KEY, pathVar.getOrDefault(VERSION_KEY, CHUNI_DEFAULT_VERSION)); + return result; } } diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserDataHandler.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserDataHandler.java index b8c17d45..c3007b42 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserDataHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserDataHandler.java @@ -3,7 +3,9 @@ package icu.samnyan.aqua.sega.chunithm.handler.impl; import com.fasterxml.jackson.core.JsonProcessingException; import icu.samnyan.aqua.sega.chunithm.handler.BaseHandler; import icu.samnyan.aqua.sega.chunithm.model.userdata.UserData; +import icu.samnyan.aqua.sega.general.service.ClientSettingService; import icu.samnyan.aqua.sega.chunithm.service.UserDataService; +import icu.samnyan.aqua.sega.util.VersionUtil; import icu.samnyan.aqua.sega.util.jackson.StringMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,6 +17,8 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; +import static icu.samnyan.aqua.sega.util.AquaConst.SERIAL_KEY; + /** * @author samnyan (privateamusement@protonmail.com) */ @@ -25,6 +29,8 @@ public class GetUserDataHandler implements BaseHandler { private final StringMapper mapper; + private final ClientSettingService clientSettingService; + private final UserDataService userDataService; private final boolean overwriteVersion; @@ -33,12 +39,13 @@ public class GetUserDataHandler implements BaseHandler { @Autowired public GetUserDataHandler(StringMapper mapper, - UserDataService userDataService, + ClientSettingService clientSettingService, UserDataService userDataService, @Value("${game.chunithm.overwrite-version}") boolean overwriteVersion, @Value("${game.chunithm.rom-version}") String romVersion, @Value("${game.chunithm.data-version}") String dataVersion ) { this.mapper = mapper; + this.clientSettingService = clientSettingService; this.userDataService = userDataService; this.overwriteVersion = overwriteVersion; this.romVersion = romVersion; @@ -55,10 +62,13 @@ public class GetUserDataHandler implements BaseHandler { resultMap.put("userId", userId); UserData user = userDataOptional.get(); - if (overwriteVersion) { - user.setLastRomVersion(romVersion); - user.setLastDataVersion(dataVersion); + var vo = clientSettingService.getSetting((String) request.get(SERIAL_KEY)); + if (vo.isPresent()) { + var version = vo.get(); + user.setLastRomVersion(VersionUtil.getTargetVersion(user.getLastRomVersion(), version.getRomVersion())); + user.setLastDataVersion(VersionUtil.getTargetVersion(user.getLastDataVersion(), version.getDataVersion())); } + resultMap.put("userData", user); String json = mapper.write(resultMap); logger.info("Response: " + json); diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserPreviewHandler.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserPreviewHandler.java index 1def9cfe..6eb4ce10 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserPreviewHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/GetUserPreviewHandler.java @@ -6,9 +6,11 @@ import icu.samnyan.aqua.sega.chunithm.model.response.GetUserPreviewResp; import icu.samnyan.aqua.sega.chunithm.model.userdata.UserCharacter; import icu.samnyan.aqua.sega.chunithm.model.userdata.UserData; import icu.samnyan.aqua.sega.chunithm.model.userdata.UserGameOption; +import icu.samnyan.aqua.sega.general.service.ClientSettingService; import icu.samnyan.aqua.sega.chunithm.service.UserCharacterService; import icu.samnyan.aqua.sega.chunithm.service.UserDataService; import icu.samnyan.aqua.sega.chunithm.service.UserGameOptionService; +import icu.samnyan.aqua.sega.util.VersionUtil; import icu.samnyan.aqua.sega.util.jackson.StringMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -19,6 +21,8 @@ import org.springframework.stereotype.Component; import java.util.Map; import java.util.Optional; +import static icu.samnyan.aqua.sega.util.AquaConst.SERIAL_KEY; + /** * The handler for loading basic profile information. *

@@ -33,6 +37,8 @@ public class GetUserPreviewHandler implements BaseHandler { private final StringMapper mapper; + private final ClientSettingService clientSettingService; + private final UserDataService userDataService; private final UserCharacterService userCharacterService; private final UserGameOptionService userGameOptionService; @@ -43,7 +49,7 @@ public class GetUserPreviewHandler implements BaseHandler { @Autowired public GetUserPreviewHandler(StringMapper mapper, - UserDataService userDataService, + ClientSettingService clientSettingService, UserDataService userDataService, UserCharacterService userCharacterService, UserGameOptionService userGameOptionService, @Value("${game.chunithm.overwrite-version}") boolean overwriteVersion, @@ -51,6 +57,7 @@ public class GetUserPreviewHandler implements BaseHandler { @Value("${game.chunithm.data-version}") String dataVersion ) { this.mapper = mapper; + this.clientSettingService = clientSettingService; this.userDataService = userDataService; this.userCharacterService = userCharacterService; this.userGameOptionService = userGameOptionService; @@ -84,8 +91,15 @@ public class GetUserPreviewHandler implements BaseHandler { resp.setPlayerRating(user.getPlayerRating()); resp.setLastGameId(user.getLastGameId()); - resp.setLastRomVersion(user.getLastRomVersion()); - resp.setLastDataVersion(user.getLastDataVersion()); + var vo = clientSettingService.getSetting((String) request.get(SERIAL_KEY)); + if (vo.isPresent()) { + var version = vo.get(); + resp.setLastRomVersion(VersionUtil.getTargetVersion(user.getLastRomVersion(), version.getRomVersion())); + resp.setLastDataVersion(VersionUtil.getTargetVersion(user.getLastDataVersion(), version.getDataVersion())); + } else { + resp.setLastRomVersion(user.getLastRomVersion()); + resp.setLastDataVersion(user.getLastDataVersion()); + } if (overwriteVersion) { resp.setLastRomVersion(romVersion); diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/UpsertClientSettingHandler.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/UpsertClientSettingHandler.java index bf33456e..85a036b8 100644 --- a/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/UpsertClientSettingHandler.java +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/handler/impl/UpsertClientSettingHandler.java @@ -1,10 +1,42 @@ package icu.samnyan.aqua.sega.chunithm.handler.impl; +import com.fasterxml.jackson.core.JsonProcessingException; +import icu.samnyan.aqua.sega.chunithm.handler.BaseHandler; +import icu.samnyan.aqua.sega.chunithm.model.requet.ClientSettingRequest; +import icu.samnyan.aqua.sega.general.model.GameVersion; +import icu.samnyan.aqua.sega.general.service.ClientSettingService; +import icu.samnyan.aqua.sega.util.jackson.StringMapper; import org.springframework.stereotype.Component; +import java.time.LocalDateTime; +import java.util.Map; + +import static icu.samnyan.aqua.sega.util.AquaConst.DEFAULT_KEYCHIP_ID; +import static icu.samnyan.aqua.sega.util.AquaConst.SERIAL_KEY; + /** * @author samnyan (privateamusement@protonmail.com) */ @Component -public class UpsertClientSettingHandler { +public class UpsertClientSettingHandler implements BaseHandler { + + private final StringMapper mapper; + + private final ClientSettingService clientSettingService; + + public UpsertClientSettingHandler(StringMapper mapper, ClientSettingService clientSettingService) { + this.mapper = mapper; + this.clientSettingService = clientSettingService; + } + + @Override + public String handle(Map request) throws JsonProcessingException { + var req = mapper.convert(request, ClientSettingRequest.class); + var set = req.getClientSetting(); + var serial = (String) request.get(SERIAL_KEY); + if (!serial.equals(DEFAULT_KEYCHIP_ID)) { + clientSettingService.writeSetting(new GameVersion(serial, set.getRomVersion(), set.getDataVersion(), LocalDateTime.now())); + } + return "{\"returnCode\":\"1\"}"; + } } diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSetting.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSetting.java new file mode 100644 index 00000000..9c127f1e --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSetting.java @@ -0,0 +1,21 @@ +package icu.samnyan.aqua.sega.chunithm.model.requet; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ClientSetting { + private String placeId; + private String clientId; + private String placeName; + private String regionId; + private String regionName; + private String allNetId; + private String bordId; + private String romVersion; + private String dataVersion; + private String dumpFileNum; +} diff --git a/src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSettingRequest.java b/src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSettingRequest.java new file mode 100644 index 00000000..fab337b8 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/chunithm/model/requet/ClientSettingRequest.java @@ -0,0 +1,12 @@ +package icu.samnyan.aqua.sega.chunithm.model.requet; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ClientSettingRequest { + private ClientSetting clientSetting; +} diff --git a/src/main/java/icu/samnyan/aqua/sega/general/dao/GameVersionRepository.java b/src/main/java/icu/samnyan/aqua/sega/general/dao/GameVersionRepository.java new file mode 100644 index 00000000..dd7dfcdb --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/general/dao/GameVersionRepository.java @@ -0,0 +1,21 @@ +package icu.samnyan.aqua.sega.general.dao; + +import icu.samnyan.aqua.security.model.WebUser; +import icu.samnyan.aqua.sega.general.model.Card; +import icu.samnyan.aqua.sega.general.model.GameVersion; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +/** + * @author samnyan (privateamusement@protonmail.com) + */ +@Repository("GameVersionRepository") +public interface GameVersionRepository extends JpaRepository { + Optional findByUuid(String uuid); + List findByLastTimeBefore(LocalDateTime time); +} diff --git a/src/main/java/icu/samnyan/aqua/sega/general/model/GameVersion.java b/src/main/java/icu/samnyan/aqua/sega/general/model/GameVersion.java new file mode 100644 index 00000000..2919ef00 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/general/model/GameVersion.java @@ -0,0 +1,21 @@ +package icu.samnyan.aqua.sega.general.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity(name = "SegaGameVersion") +@Table(name = "sega_game_version") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class GameVersion { + @Id + private String uuid; + private String romVersion; + private String dataVersion; + private LocalDateTime lastTime; +} diff --git a/src/main/java/icu/samnyan/aqua/sega/general/service/ClientSettingService.java b/src/main/java/icu/samnyan/aqua/sega/general/service/ClientSettingService.java new file mode 100644 index 00000000..6b789310 --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/general/service/ClientSettingService.java @@ -0,0 +1,42 @@ +package icu.samnyan.aqua.sega.general.service; + +import icu.samnyan.aqua.sega.general.dao.GameVersionRepository; +import icu.samnyan.aqua.sega.general.model.GameVersion; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Optional; + +import static icu.samnyan.aqua.sega.util.AquaConst.DEFAULT_KEYCHIP_ID; + +@Service +public class ClientSettingService { + + private final GameVersionRepository gameVersionRepository; + + public ClientSettingService(GameVersionRepository gameVersionRepository) { + this.gameVersionRepository = gameVersionRepository; + } + + public void writeSetting(GameVersion setting) { + gameVersionRepository.save(setting); + } + + public Optional getSetting(String serial) { + if (serial.equals(DEFAULT_KEYCHIP_ID)) { + return Optional.empty(); + } + try { + var vo = gameVersionRepository.findByUuid(serial); + if (vo.isPresent()) { + var v = vo.get(); + v.setLastTime(LocalDateTime.now()); + gameVersionRepository.save(v); + return Optional.of(v); + } + return Optional.empty(); + } catch (Exception e) { + return Optional.empty(); + } + } +} diff --git a/src/main/java/icu/samnyan/aqua/sega/util/AquaConst.java b/src/main/java/icu/samnyan/aqua/sega/util/AquaConst.java new file mode 100644 index 00000000..bc5de58f --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/util/AquaConst.java @@ -0,0 +1,8 @@ +package icu.samnyan.aqua.sega.util; + +public class AquaConst { + public static String SERIAL_KEY = "CLIENT_ID"; + public static String VERSION_KEY = "ROM_VERSION"; + public static String CHUNI_DEFAULT_VERSION = "1.30"; + public static String DEFAULT_KEYCHIP_ID = "A69E01A8888"; +} diff --git a/src/main/java/icu/samnyan/aqua/sega/util/VersionUtil.java b/src/main/java/icu/samnyan/aqua/sega/util/VersionUtil.java new file mode 100644 index 00000000..b26d341d --- /dev/null +++ b/src/main/java/icu/samnyan/aqua/sega/util/VersionUtil.java @@ -0,0 +1,25 @@ +package icu.samnyan.aqua.sega.util; + +public class VersionUtil { + + public static String getTargetVersion(String savedVersion, String currentVersion) { + var v1s = savedVersion.split("\\."); + var v2s = currentVersion.split("\\."); + try { + for (int i = 0; i < v1s.length; i++) { + int v1n = Integer.parseInt(v1s[i]); + int v2n = Integer.parseInt(v2s[i]); + if (v1n > v2n) { + return currentVersion; + } + if (v1n < v2n) { + return savedVersion; + } + } + // if all same + return savedVersion; + } catch (Exception e) { + return savedVersion; + } + } +} diff --git a/src/main/resources/db/migration/mysql/V25__add_client_setting.sql b/src/main/resources/db/migration/mysql/V25__add_client_setting.sql new file mode 100644 index 00000000..371db158 --- /dev/null +++ b/src/main/resources/db/migration/mysql/V25__add_client_setting.sql @@ -0,0 +1,8 @@ +create table sega_game_version +( + uuid varchar(255) + primary key, + rom_version varchar(255) not null, + data_version varchar(255) not null, + last_time DATETIME not null +); \ No newline at end of file diff --git a/src/main/resources/db/migration/sqlite/V25__add_client_setting.sql b/src/main/resources/db/migration/sqlite/V25__add_client_setting.sql new file mode 100644 index 00000000..4c15bef7 --- /dev/null +++ b/src/main/resources/db/migration/sqlite/V25__add_client_setting.sql @@ -0,0 +1,8 @@ +create table sega_game_version +( + uuid varchar + primary key, + rom_version varchar not null, + data_version varchar not null, + last_time DATETIME not null +); \ No newline at end of file