Tokens rework:

- added tokens database (now all tokens store in tokens-database.txt, related to #10139);
 - added sets/cards/tokens stats on app's start;
This commit is contained in:
Oleg Agafonov 2023-04-22 02:22:11 +04:00
parent 1dc7dcc94c
commit 24bd4315c6
8 changed files with 348 additions and 47 deletions

View file

@ -22,10 +22,22 @@ public final class RepositoryUtil {
public static final boolean CARD_DB_RECREATE_BY_CLIENT_SIDE = true; // re-creates db from client (best performance) or downloads from server on connects (can be slow)
public static void bootstrapLocalDb() {
// call local db to init all sets and cards repository (need for correct updates cycle, not on random request)
// call local db to init all sets and cards/tokens repository (need for correct updates cycle, not on random request)
logger.info("Loading database...");
ExpansionRepository.instance.getContentVersionConstant();
CardRepository.instance.getContentVersionConstant();
TokenRepository.instance.getAllTokens().size();
// stats
int totalCards = CardRepository.instance.findCards(new CardCriteria().nightCard(false)).size()
+ CardRepository.instance.findCards(new CardCriteria().nightCard(true)).size();
logger.info("Database stats:");
logger.info(" - sets: " + ExpansionRepository.instance.getAll().size());
logger.info(" - cards: " + totalCards);
logger.info(" - tokens: " + TokenRepository.instance.getByType(TokenType.TOKEN).size());
logger.info(" - emblems: " + TokenRepository.instance.getByType(TokenType.EMBLEM).size());
logger.info(" - planes: " + TokenRepository.instance.getByType(TokenType.PLANE).size());
logger.info(" - dungeons: " + TokenRepository.instance.getByType(TokenType.DUNGEON).size());
}
public static boolean isDatabaseObsolete(ConnectionSource connectionSource, String entityName, long version) throws SQLException {

View file

@ -0,0 +1,71 @@
package mage.cards.repository;
/**
* Token item for tokens database
*
* @author JayDi85
*/
public class TokenInfo {
private TokenType tokenType;
private String name;
private String setCode;
private Integer imageNumber = 1; // if one set contains diff images with same name
private String classFileName;
private String imageFileName;
public TokenInfo(TokenType tokenType, String name, String setCode, Integer imageNumber) {
this(tokenType, name, setCode, imageNumber, "", "");
}
public TokenInfo(TokenType tokenType, String name, String setCode, Integer imageNumber, String classFileName, String imageFileName) {
this.tokenType = tokenType;
this.name = name;
this.setCode = setCode;
this.imageNumber = imageNumber;
this.classFileName = classFileName;
this.imageFileName = imageFileName;
}
public TokenType getTokenType() {
return tokenType;
}
public String getName() {
return name;
}
public String getImageFileName() {
return imageFileName;
}
public String getSetCode() {
return setCode;
}
public String getClassFileName() {
return classFileName;
}
public Integer getImageNumber() {
return imageNumber;
}
public String getFullClassFileName() {
String simpleName = classFileName.isEmpty() ? name.replaceAll("[^a-zA-Z0-9]", "") : classFileName;
switch (this.tokenType) {
case TOKEN:
return "mage.game.permanent.token." + simpleName;
case EMBLEM:
return "mage.game.command.emblems." + simpleName;
case PLANE:
return "mage.game.command.planes." + simpleName;
case DUNGEON:
return "mage.game.command.dungeons." + simpleName;
default:
throw new IllegalStateException("Unknown token type: " + this.tokenType);
}
}
}

View file

@ -0,0 +1,197 @@
package mage.cards.repository;
import org.apache.log4j.Logger;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author JayDi85
*/
public enum TokenRepository {
instance;
private static final Logger logger = Logger.getLogger(TokenRepository.class);
private ArrayList<TokenInfo> allTokens = new ArrayList<>();
private Map<String, List<TokenInfo>> indexByClassName = new HashMap<>();
private Map<TokenType, List<TokenInfo>> indexByType = new HashMap<>();
TokenRepository() {
}
public void init() {
allTokens.clear();
indexByClassName.clear();
indexByType.clear();
allTokens = loadAllTokens();
// index
allTokens.forEach(token -> {
// by class
List<TokenInfo> list = indexByClassName.getOrDefault(token.getClassFileName(), null);
if (list == null) {
list = new ArrayList<>();
indexByClassName.put(token.getClassFileName(), list);
}
list.add(token);
// by type
list = indexByType.getOrDefault(token.getTokenType(), null);
if (list == null) {
list = new ArrayList<>();
indexByType.put(token.getTokenType(), list);
}
list.add(token);
});
}
public List<TokenInfo> getAllTokens() {
if (allTokens.isEmpty()) {
init();
}
return allTokens;
}
public List<TokenInfo> getByType(TokenType tokenType) {
return indexByType.getOrDefault(tokenType, new ArrayList<>());
}
private static ArrayList<TokenInfo> loadAllTokens() throws RuntimeException {
// Must load tokens data in strict mode (throw exception on any error)
// Try to put verify checks here instead verify tests
String dbSource = "tokens-database.txt";
ArrayList<TokenInfo> list = new ArrayList<>();
InputStream in = TokenRepository.class.getClassLoader().getResourceAsStream(dbSource);
if (in == null) {
throw new RuntimeException("Tokens database: can't load resource file " + dbSource);
}
List<String> errorsList = new ArrayList<>();
try (InputStreamReader input = new InputStreamReader(in);
BufferedReader reader = new BufferedReader(input)) {
String line = reader.readLine();
while (line != null) {
try {
line = line.trim();
if (!line.startsWith("|")) {
continue;
}
List<String> params = Arrays.stream(line.split("\\|", -1))
.map(String::trim)
.collect(Collectors.toList());
if (params.size() < 5) {
errorsList.add("Tokens database: wrong params count: " + line);
continue;
}
if (!params.get(1).toLowerCase(Locale.ENGLISH).equals("generate")) {
// TODO: remove "generate" from db
errorsList.add("Tokens database: miss generate param: " + line);
continue;
}
// image number (uses if one set contains multiple tokens with same name)
int imageNumber = 0;
if (!params.get(4).isEmpty()) {
imageNumber = Integer.parseInt(params.get(4));
}
// image file name
String imageFileName = "";
if (params.size() > 5 && !params.get(5).isEmpty()) {
imageFileName = params.get(5);
}
// token class name (uses for images search for render)
String tokenClassName = "";
if (params.size() > 7 && !params.get(6).isEmpty()) {
tokenClassName = params.get(6);
}
if (tokenClassName.isEmpty()) {
errorsList.add("Tokens database: miss class name: " + line);
continue;
}
// object type
String objectType = params.get(2);
String tokenName = params.get(3);
String setCode = "";
TokenType tokenType = null;
// type - token
if (objectType.startsWith("TOK:")) {
setCode = objectType.substring("TOK:".length());
tokenType = TokenType.TOKEN;
}
// type - emblem
if (objectType.startsWith("EMBLEM:")) {
setCode = objectType.substring("EMBLEM:".length());
tokenType = TokenType.EMBLEM;
if (!tokenName.startsWith("Emblem ")) {
errorsList.add("Tokens database: emblem's name must start with [Emblem ...] word: " + line);
continue;
}
if (!tokenClassName.endsWith("Emblem")) {
errorsList.add("Tokens database: emblem's class name must ends with [...Emblem] word: " + line);
continue;
}
}
// type - plane
if (objectType.startsWith("PLANE:")) {
setCode = objectType.substring("PLANE:".length());
tokenType = TokenType.PLANE;
if (!tokenName.startsWith("Plane - ")) {
errorsList.add("Tokens database: plane's name must start with [Plane - ...] word: " + line);
continue;
}
if (!tokenClassName.endsWith("Plane")) {
errorsList.add("Tokens database: plane's class name must ends with [...Plane] word: " + line);
continue;
}
}
// type - dungeon
if (objectType.startsWith("DUNGEON:")) {
setCode = objectType.substring("DUNGEON:".length());
tokenType = TokenType.DUNGEON;
if (!tokenClassName.endsWith("Dungeon")) {
errorsList.add("Tokens database: dungeon's class name must ends with [...Dungeon] word: " + line);
continue;
}
}
// type - unknown
if (tokenType == null) {
errorsList.add("Tokens database: unknown line format: " + line);
continue;
}
// OK
TokenInfo token = new TokenInfo(tokenType, tokenName, setCode, imageNumber, tokenClassName, imageFileName);
list.add(token);
} finally {
line = reader.readLine();
}
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Tokens database: can't read data, unknown error - " + e.getMessage());
}
if (!errorsList.isEmpty()) {
errorsList.forEach(logger::error);
throw new RuntimeException(String.format("Tokens database: found %d errors, see logs above for details", errorsList.size()));
}
return list;
}
}

View file

@ -0,0 +1,15 @@
package mage.cards.repository;
/**
* XMage's token types for images
*
* @author JayDi85
*/
public enum TokenType {
TOKEN,
EMBLEM,
PLANE,
DUNGEON
}

File diff suppressed because it is too large Load diff