magarena/src/magic/MagicMain.java

278 lines
9.8 KiB
Java

package magic;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.SplashScreen;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.pushingpixels.trident.TridentConfig;
import magic.ai.MagicAI;
import magic.data.DeckGenerators;
import magic.data.DuelConfig;
import magic.exception.InvalidDeckException;
import magic.game.state.GameLoader;
import magic.model.DuelPlayerConfig;
import magic.model.MagicDeck;
import magic.model.MagicDuel;
import magic.model.player.AiProfile;
import magic.test.TestGameBuilder;
import magic.ui.ScreenController;
import magic.ui.SplashProgressReporter;
import magic.ui.UiExceptionHandler;
import magic.ui.WikiPage;
import magic.ui.helpers.LaFHelper;
import magic.ui.widget.duel.animation.MagicAnimations;
import magic.utility.DeckUtils;
import magic.utility.MagicFileSystem;
import magic.utility.MagicFileSystem.DataPath;
import magic.utility.MagicSystem;
import magic.utility.ProgressReporter;
public class MagicMain {
private static final Logger LOGGER = Logger.getLogger(MagicMain.class.getName());
private static SplashScreen splash;
private static ProgressReporter reporter = new ProgressReporter();
public static void main(final String[] args) {
final CommandLineArgs cmdline = new CommandLineArgs(args);
// transfer control to HeadlessAIGame
if (cmdline.isHeadless()) {
HeadlessAIGame.main(cmdline);
return;
}
// transfer control to automatic card test
if (cmdline.isCardTest()) {
CardTest.main(cmdline);
return;
}
parseCommandLine(cmdline);
Thread.setDefaultUncaughtExceptionHandler(new UiExceptionHandler());
setSplashScreen();
System.out.println(MagicSystem.getRuntimeParameters());
// show the data folder being used
System.out.println("Data folder : "+ MagicFileSystem.getDataPath());
// init subsystems
final long start_time = System.currentTimeMillis();
reporter.setMessage("Initializing game engine...");
MagicSystem.initialize(reporter);
if (MagicSystem.showStartupStats()) {
final double duration = (double)(System.currentTimeMillis() - start_time) / 1000;
System.err.println("Initialization of engine took " + duration + "s");
}
LaFHelper.setDefaultLookAndFeel();
reporter.setMessage("Starting UI...");
SwingUtilities.invokeLater(() -> startUI(cmdline));
}
private static void printRefreshRate() {
System.out.println("=== Screen devices refresh rates ===");
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
for (int i = 0; i < gs.length; i++) {
DisplayMode dm = gs[i].getDisplayMode();
int refreshRate = dm.getRefreshRate();
if (refreshRate == DisplayMode.REFRESH_RATE_UNKNOWN) {
System.err.printf("[%d] Unknown rate\n", i);
} else {
System.out.printf("[%d] %d Hz", i, refreshRate);
System.out.println();
}
}
}
/**
* Sets custom pulse behavior - higher frame rate, lower frame rate or dynamic frame rate.
* <p>
* By default, Trident timelines are driven by a dedicated thread that wakes up every 40ms and
* updates all the timelines. When the CPU is not heavily used this results in 25 frames-per-second
* refresh rate for Trident-driven UI animations - consistent with the frame rate of theatrical films
* and non-interlaced PAL television standard.
* <p>
* (see https://kenai.com/projects/trident/pages/CustomPulseSource)
*
* Must be run before any instance of Timeline is created in the application otherwise it will
* generate the "cannot replace the pulse source thread once it's running..." error.
*/
private static void setTridentFPS(int fps) {
long sleepMs = 1000L / fps;
try {
TridentConfig.getInstance().setPulseSource(() -> {
try {
Thread.sleep(sleepMs);
} catch (InterruptedException ex) {
LOGGER.log(Level.WARNING, null, ex);
}
});
} catch (RuntimeException ex) {
LOGGER.log(Level.WARNING, ex.getMessage());
}
}
private static void parseCommandLine(CommandLineArgs cmdline) {
if (cmdline.showHelp()) {
System.err.println("--help specified - opening wiki page and exit...\n" + WikiPage.COMMAND_LINE.getUrl());
WikiPage.show(WikiPage.COMMAND_LINE);
System.exit(0);
}
if (cmdline.showFPS()) {
printRefreshRate();
System.exit(0);
}
if (cmdline.getFPS() > 0) {
setTridentFPS(cmdline.getFPS());
System.out.println("Trident FPS = " + cmdline.getFPS());
}
MagicAnimations.setEnabled(cmdline.isAnimationsEnabled());
MagicAI.setMaxThreads(cmdline.getMaxThreads());
MagicSystem.setIsDevMode(cmdline.isDevMode());
}
/**
* Sets splash screen as defined in JAR manifest or via "-splash" command line.
* <p>
* Can override with custom splash by placing "splash.png" in mods folder.
*/
private static void setSplashScreen() {
splash = SplashScreen.getSplashScreen();
if (splash == null) {
System.err.println("Error: no splash image specified on the command line");
} else {
reporter = new SplashProgressReporter(splash);
try {
final File splashFile = MagicFileSystem.getDataPath(DataPath.MODS).resolve("splash.png").toFile();
if (splashFile.exists()) {
splash.setImageURL(splashFile.toURI().toURL());
}
} catch (IOException ex) {
// A problem occurred trying to set custom splash.
// Log error and use default splash screen.
System.err.println(ex);
}
}
}
public static void setPlayerDeck(String deckArg, DuelPlayerConfig player) {
if (deckArg.isEmpty()) {
player.setDeckProfile("Random;***");
DeckGenerators.setRandomDeck(player);
return;
} else if (deckArg.equals("@")) { // random deck file.
player.setDeckProfile("Random;@");
DeckGenerators.setRandomDeck(player);
return;
} else if (deckArg.equals("#")) { // random single color deck.
player.setDeckProfile("Random;*");
DeckGenerators.setRandomDeck(player);
return;
} else if (deckArg.equals("##")) { //random two-color deck.
player.setDeckProfile("Random;**");
DeckGenerators.setRandomDeck(player);
return;
} else if (deckArg.equals("###")) { // random three-color deck.
player.setDeckProfile("Random;***");
DeckGenerators.setRandomDeck(player);
return;
} else { // search for deck file.
File deckFile = DeckUtils.findDeckFile(deckArg);
if (deckFile != null) {
MagicDeck deck = DeckUtils.loadDeckFromFile(deckFile.toPath());
if (deck.isValid()) {
player.setDeck(deck);
return;
}
}
}
throw new InvalidDeckException("Invalid deck specified in command line : " + deckArg);
}
private static void runAivsAiGame(CommandLineArgs cmdline) {
System.out.println("");
System.err.println("=== AI vs AI ===");
final DuelConfig config = DuelConfig.getInstance();
config.load();
config.setPlayerProfile(0, AiProfile.create(cmdline.getAi1(), cmdline.getAi1Level()));
config.setPlayerProfile(1, AiProfile.create(cmdline.getAi2(), cmdline.getAi2Level()));
setPlayerDeck(cmdline.getDeck1(), config.getPlayerConfig(0));
setPlayerDeck(cmdline.getDeck2(), config.getPlayerConfig(1));
config.setNrOfGames(cmdline.getGames());
config.setStartLife(cmdline.getLife());
System.out.println("P1 : " + config.getPlayerProfile(0).getPlayerLabel());
System.out.println(" " + config.getPlayerConfig(0).getDeck().getQualifiedName());
System.out.println("P2 : " + config.getPlayerProfile(1).getPlayerLabel());
System.out.println(" " + config.getPlayerConfig(1).getDeck().getQualifiedName());
System.out.println("Threads : " + MagicAI.getMaxThreads());
MagicDuel.newDuel();
ScreenController.showDuelScreen();
}
private static void startUI(CommandLineArgs cmdline) {
// -DtestGame=X, where X is one of the classes (without the .java) in "magic.test".
final String testGame = System.getProperty("testGame");
if (testGame != null) {
ScreenController.showDuelGameScreen(TestGameBuilder.buildGame(testGame));
return;
}
// -DsaveGame=X, where X is the path to a snapshot.game
final String saveGame = System.getProperty("saveGame");
if (saveGame != null) {
final File f = new File(saveGame);
ScreenController.showDuelGameScreen(GameLoader.loadSavedGame(f));
return;
}
// AI vs AI game.
if (MagicSystem.isAiVersusAi()) {
try {
runAivsAiGame(cmdline);
} catch (InvalidDeckException ex) {
System.err.println(ex);
} catch (RuntimeException ex) {
LOGGER.log(Level.SEVERE, null, ex);
}
return;
}
// normal UI startup.
ScreenController.showStartScreen();
}
}