[chuni] Add auto version matching

pull/1/head
samnyan 2020-12-14 17:18:21 +08:00
parent b3a8e80399
commit aa2e8cdea4
15 changed files with 256 additions and 21 deletions

View File

@ -17,8 +17,11 @@ import java.io.InputStream;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.zip.DataFormatException; import java.util.zip.DataFormatException;
import static icu.samnyan.aqua.sega.util.AquaConst.DEFAULT_KEYCHIP_ID;
/** /**
* @author samnyan (privateamusement@protonmail.com) * @author samnyan (privateamusement@protonmail.com)
*/ */
@ -53,17 +56,22 @@ public class AllNetController {
byte[] bytes = dataStream.readAllBytes(); byte[] bytes = dataStream.readAllBytes();
Map<String, String> reqMap = Decoder.decode(bytes); Map<String, String> reqMap = Decoder.decode(bytes);
logger.info("Request: PowerOn, " + new ObjectMapper().writeValueAsString(reqMap)); logger.info("Request: PowerOn, " + mapper.writeValueAsString(reqMap));
// TODO: Verify KeyChip id // TODO: Verify KeyChip id
String gameId = reqMap.getOrDefault("game_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", ""); String format_ver = reqMap.getOrDefault("format_ver", "");
PowerOnResponse resp; PowerOnResponse resp;
if (format_ver.startsWith("2")) { if (format_ver.startsWith("2")) {
var now = LocalDateTime.now(); var now = LocalDateTime.now();
resp = new PowerOnResponseV2( resp = new PowerOnResponseV2(
1, 1,
switchUri(gameId), switchUri(gameId, ver, serial),
switchHost(gameId), switchHost(gameId),
"123", "123",
"", "",
@ -87,7 +95,7 @@ public class AllNetController {
} else { } else {
resp = new PowerOnResponseV3( resp = new PowerOnResponseV3(
1, 1,
switchUri(gameId), switchUri(gameId, ver, serial),
switchHost(gameId), switchHost(gameId),
"123", "123",
"", "",
@ -110,10 +118,10 @@ public class AllNetController {
return resp.toString().concat("\n"); return resp.toString().concat("\n");
} }
private String switchUri(String gameId) { private String switchUri(String gameId, String ver, String serial) {
switch (gameId) { switch (gameId) {
case "SDBT": case "SDBT":
return "http://" + HOST + ":" + PORT + "/"; return "http://" + HOST + ":" + PORT + "/ChuniServlet/" + ver + "/" + serial + "/";
case "SBZV": case "SBZV":
return "http://" + HOST + ":" + PORT + "/diva/"; return "http://" + HOST + ":" + PORT + "/diva/";
case "SDDT": case "SDDT":

View File

@ -4,10 +4,7 @@ package icu.samnyan.aqua.sega.chunithm.controller;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import icu.samnyan.aqua.sega.chunithm.handler.impl.*; import icu.samnyan.aqua.sega.chunithm.handler.impl.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map; import java.util.Map;
@ -15,7 +12,7 @@ import java.util.Map;
* @author samnyan (privateamusement@protonmail.com) * @author samnyan (privateamusement@protonmail.com)
*/ */
@RestController @RestController
@RequestMapping("ChuniServlet") @RequestMapping({"/ChuniServlet/{ROM_VERSION}/{CLIENT_ID}/ChuniServlet", "/ChuniServlet"})
public class ChuniServletController { public class ChuniServletController {
private final GameLoginHandler gameLoginHandler; private final GameLoginHandler gameLoginHandler;
@ -242,8 +239,8 @@ public class ChuniServletController {
} }
@PostMapping("UpsertClientSettingApi") @PostMapping("UpsertClientSettingApi")
String upsertClientSetting(@ModelAttribute Map<String, Object> request) { String upsertClientSetting(@ModelAttribute Map<String, Object> request) throws JsonProcessingException {
return "{\"returnCode\":\"1\"}"; return upsertClientSettingHandler.handle(request);
} }
@PostMapping("UpsertClientTestmodeApi") @PostMapping("UpsertClientTestmodeApi")

View File

@ -5,13 +5,17 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ModelAttribute; 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.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Map; import java.util.Map;
import static icu.samnyan.aqua.sega.util.AquaConst.*;
/** /**
* @author samnyan (privateamusement@protonmail.com) * @author samnyan (privateamusement@protonmail.com)
*/ */
@ -28,12 +32,16 @@ public class ChuniServletControllerAdvice {
*/ */
@ModelAttribute @ModelAttribute
public Map<String, Object> preHandle(HttpServletRequest request) throws IOException { public Map<String, Object> preHandle(HttpServletRequest request) throws IOException {
var pathVar = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
byte[] src = request.getInputStream().readAllBytes(); byte[] src = request.getInputStream().readAllBytes();
String outputString = new String(src, StandardCharsets.UTF_8).trim(); String outputString = new String(src, StandardCharsets.UTF_8).trim();
logger.info("Request " + request.getRequestURI() + ": " + outputString); logger.info("Request " + request.getRequestURI() + ": " + outputString);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(outputString, new TypeReference<>() { Map<String, Object> 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;
} }
} }

View File

@ -3,7 +3,9 @@ package icu.samnyan.aqua.sega.chunithm.handler.impl;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import icu.samnyan.aqua.sega.chunithm.handler.BaseHandler; import icu.samnyan.aqua.sega.chunithm.handler.BaseHandler;
import icu.samnyan.aqua.sega.chunithm.model.userdata.UserData; 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.chunithm.service.UserDataService;
import icu.samnyan.aqua.sega.util.VersionUtil;
import icu.samnyan.aqua.sega.util.jackson.StringMapper; import icu.samnyan.aqua.sega.util.jackson.StringMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -15,6 +17,8 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static icu.samnyan.aqua.sega.util.AquaConst.SERIAL_KEY;
/** /**
* @author samnyan (privateamusement@protonmail.com) * @author samnyan (privateamusement@protonmail.com)
*/ */
@ -25,6 +29,8 @@ public class GetUserDataHandler implements BaseHandler {
private final StringMapper mapper; private final StringMapper mapper;
private final ClientSettingService clientSettingService;
private final UserDataService userDataService; private final UserDataService userDataService;
private final boolean overwriteVersion; private final boolean overwriteVersion;
@ -33,12 +39,13 @@ public class GetUserDataHandler implements BaseHandler {
@Autowired @Autowired
public GetUserDataHandler(StringMapper mapper, public GetUserDataHandler(StringMapper mapper,
UserDataService userDataService, ClientSettingService clientSettingService, UserDataService userDataService,
@Value("${game.chunithm.overwrite-version}") boolean overwriteVersion, @Value("${game.chunithm.overwrite-version}") boolean overwriteVersion,
@Value("${game.chunithm.rom-version}") String romVersion, @Value("${game.chunithm.rom-version}") String romVersion,
@Value("${game.chunithm.data-version}") String dataVersion @Value("${game.chunithm.data-version}") String dataVersion
) { ) {
this.mapper = mapper; this.mapper = mapper;
this.clientSettingService = clientSettingService;
this.userDataService = userDataService; this.userDataService = userDataService;
this.overwriteVersion = overwriteVersion; this.overwriteVersion = overwriteVersion;
this.romVersion = romVersion; this.romVersion = romVersion;
@ -55,10 +62,13 @@ public class GetUserDataHandler implements BaseHandler {
resultMap.put("userId", userId); resultMap.put("userId", userId);
UserData user = userDataOptional.get(); UserData user = userDataOptional.get();
if (overwriteVersion) { var vo = clientSettingService.getSetting((String) request.get(SERIAL_KEY));
user.setLastRomVersion(romVersion); if (vo.isPresent()) {
user.setLastDataVersion(dataVersion); var version = vo.get();
user.setLastRomVersion(VersionUtil.getTargetVersion(user.getLastRomVersion(), version.getRomVersion()));
user.setLastDataVersion(VersionUtil.getTargetVersion(user.getLastDataVersion(), version.getDataVersion()));
} }
resultMap.put("userData", user); resultMap.put("userData", user);
String json = mapper.write(resultMap); String json = mapper.write(resultMap);
logger.info("Response: " + json); logger.info("Response: " + json);

View File

@ -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.UserCharacter;
import icu.samnyan.aqua.sega.chunithm.model.userdata.UserData; import icu.samnyan.aqua.sega.chunithm.model.userdata.UserData;
import icu.samnyan.aqua.sega.chunithm.model.userdata.UserGameOption; 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.UserCharacterService;
import icu.samnyan.aqua.sega.chunithm.service.UserDataService; import icu.samnyan.aqua.sega.chunithm.service.UserDataService;
import icu.samnyan.aqua.sega.chunithm.service.UserGameOptionService; import icu.samnyan.aqua.sega.chunithm.service.UserGameOptionService;
import icu.samnyan.aqua.sega.util.VersionUtil;
import icu.samnyan.aqua.sega.util.jackson.StringMapper; import icu.samnyan.aqua.sega.util.jackson.StringMapper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -19,6 +21,8 @@ import org.springframework.stereotype.Component;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import static icu.samnyan.aqua.sega.util.AquaConst.SERIAL_KEY;
/** /**
* The handler for loading basic profile information. * The handler for loading basic profile information.
* <p> * <p>
@ -33,6 +37,8 @@ public class GetUserPreviewHandler implements BaseHandler {
private final StringMapper mapper; private final StringMapper mapper;
private final ClientSettingService clientSettingService;
private final UserDataService userDataService; private final UserDataService userDataService;
private final UserCharacterService userCharacterService; private final UserCharacterService userCharacterService;
private final UserGameOptionService userGameOptionService; private final UserGameOptionService userGameOptionService;
@ -43,7 +49,7 @@ public class GetUserPreviewHandler implements BaseHandler {
@Autowired @Autowired
public GetUserPreviewHandler(StringMapper mapper, public GetUserPreviewHandler(StringMapper mapper,
UserDataService userDataService, ClientSettingService clientSettingService, UserDataService userDataService,
UserCharacterService userCharacterService, UserCharacterService userCharacterService,
UserGameOptionService userGameOptionService, UserGameOptionService userGameOptionService,
@Value("${game.chunithm.overwrite-version}") boolean overwriteVersion, @Value("${game.chunithm.overwrite-version}") boolean overwriteVersion,
@ -51,6 +57,7 @@ public class GetUserPreviewHandler implements BaseHandler {
@Value("${game.chunithm.data-version}") String dataVersion @Value("${game.chunithm.data-version}") String dataVersion
) { ) {
this.mapper = mapper; this.mapper = mapper;
this.clientSettingService = clientSettingService;
this.userDataService = userDataService; this.userDataService = userDataService;
this.userCharacterService = userCharacterService; this.userCharacterService = userCharacterService;
this.userGameOptionService = userGameOptionService; this.userGameOptionService = userGameOptionService;
@ -84,8 +91,15 @@ public class GetUserPreviewHandler implements BaseHandler {
resp.setPlayerRating(user.getPlayerRating()); resp.setPlayerRating(user.getPlayerRating());
resp.setLastGameId(user.getLastGameId()); resp.setLastGameId(user.getLastGameId());
resp.setLastRomVersion(user.getLastRomVersion()); var vo = clientSettingService.getSetting((String) request.get(SERIAL_KEY));
resp.setLastDataVersion(user.getLastDataVersion()); 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) { if (overwriteVersion) {
resp.setLastRomVersion(romVersion); resp.setLastRomVersion(romVersion);

View File

@ -1,10 +1,42 @@
package icu.samnyan.aqua.sega.chunithm.handler.impl; 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 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) * @author samnyan (privateamusement@protonmail.com)
*/ */
@Component @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<String, Object> 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\"}";
}
} }

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<GameVersion, String> {
Optional<GameVersion> findByUuid(String uuid);
List<GameVersion> findByLastTimeBefore(LocalDateTime time);
}

View File

@ -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;
}

View File

@ -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<GameVersion> 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();
}
}
}

View File

@ -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";
}

View File

@ -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;
}
}
}

View File

@ -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
);

View File

@ -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
);