magarena/src/magic/data/stats/MagicStats.java

239 lines
7.3 KiB
Java

package magic.data.stats;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.h2.api.ErrorCode;
import magic.data.DeckType;
import magic.data.DuelConfig;
import magic.data.GeneralConfig;
import magic.data.stats.h2.H2Database;
import magic.model.MagicDeck;
import magic.model.MagicDuel;
import magic.model.MagicGame;
import magic.ui.ScreenController;
import magic.utility.DeckUtils;
import magic.utility.MagicSystem;
public final class MagicStats {
private MagicStats() {}
private static final GeneralConfig CONFIG = GeneralConfig.getInstance();
// do not access directly, use getDB().
private static H2Database db;
private static H2Database getDB() throws SQLException {
// primarily to prevent multiple threads trying
// to update the schema at the same time.
synchronized(MagicStats.class) {
if (db == null) {
db = new H2Database();
}
return db;
}
}
public static void init() {
try {
getDB();
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
private static boolean isDatabaseAlreadyOpenError(Exception ex) {
return ex instanceof SQLException
&& ((SQLException) ex).getErrorCode() == ErrorCode.DATABASE_ALREADY_OPEN_1;
}
private static void HandleErrorDisableStats(Exception ex) {
CONFIG.setGameStatsEnabled(false);
if (isDatabaseAlreadyOpenError(ex)) {
ScreenController.showWarningMessage(H2Database.getDatabaseFile() + "\n\nCannot connect to stats database as it is already open.\nStats have been switched off (see setting in preferences).");
} else {
CONFIG.save();
throw new RuntimeException(ex);
}
}
/**
* Only log game stats for a normal human vs AI game.
*/
private static boolean isNormalGame(MagicGame game) {
return !game.isArtificial()
&& !MagicSystem.isTestGame()
&& !MagicSystem.isDevMode()
&& !MagicSystem.isAiVersusAi();
}
/**
* Currently stats are only logged when, at the end of game, you click on
* the resume button to take you back to the duel decks screen. If after the
* game ends you open the menu and click back to the main menu no stats are logged.
*/
public static void logStats(MagicDuel duel, MagicGame game) {
if (isNormalGame(game)) {
saveGameData(game);
logFileBasedStats(duel, game);
}
}
/**
* Logs player stats using the old, file-based way and which are currently
* displayed on the new duel and player selection screens.
*
* TODO: phase out / replace with database system.
*/
private static void logFileBasedStats(MagicDuel duel, MagicGame game) {
if (!MagicSystem.isAiVersusAi()) {
// log player stats using the old way.
final DuelConfig duelConfig = duel.getConfiguration();
final boolean won = game.getLosingPlayer() != game.getPlayers()[0];
duelConfig.getPlayerProfile(0).getStats().update(won, game.getPlayer(0), game);
duelConfig.getPlayerProfile(1).getStats().update(!won, game.getPlayer(1), game);
}
}
/**
* Saves detailed game data to database.
*/
public static void saveGameData(MagicGame game) {
if (CONFIG.isGameStatsEnabled()) {
try {
getDB().logGameStats(game);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
}
public static String getPlayedWonLost(MagicDeck deck) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getPlayedWonLost(deck);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return "";
}
public static int getTotalGamesPlayed() {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getTotalGamesPlayed();
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return 0;
}
public static int getTotalGamesPlayed(MagicDeck deck) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getTotalGamesPlayed(deck);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return 0;
}
public static List<GameStatsInfo> getGameStats(int limit, int rowsToSkip) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getGameStats(limit, rowsToSkip);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return new ArrayList<>();
}
public static List<GameStatsInfo> getGameStats(MagicDeck deck, int limit, int page) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getGameStats(deck, limit, page);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return new ArrayList<>();
}
public static String getSchemaVersion() {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getSchemaVersion();
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return "";
}
private static List<MagicDeck> getTopDecks(List<DeckStatsInfo> decksInfo) {
List<MagicDeck> decks = new ArrayList<>();
for (DeckStatsInfo info : decksInfo) {
MagicDeck deck = DeckUtils.loadDeckFromFile(
info.deckName, DeckType.valueOf(info.deckType)
);
if (DeckUtils.getDeckFileChecksum(deck) == info.deckCheckSum) {
decks.add(deck);
}
}
return decks;
}
private static List<DeckStatsInfo> getMostPlayedDecks(int limit) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getMostPlayedDecks(limit);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return new ArrayList<>();
}
public static List<MagicDeck> getMostPlayedDecks() {
return getTopDecks(getMostPlayedDecks(20));
}
private static List<DeckStatsInfo> getTopWinningDecks(int limit) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getTopWinningDecks(limit);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return new ArrayList<>();
}
public static List<MagicDeck> getTopWinningDecks() {
return getTopDecks(getTopWinningDecks(20));
}
private static List<DeckStatsInfo> getRecentlyPlayedDecks(int limit) {
if (CONFIG.isGameStatsEnabled()) {
try {
return getDB().getRecentlyPlayedDecks(limit);
} catch (Exception ex) {
HandleErrorDisableStats(ex);
}
}
return new ArrayList<>();
}
public static List<MagicDeck> getRecentlyPlayedDecks() {
return getTopDecks(getRecentlyPlayedDecks(20));
}
}