/******************************************************************************* * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html * * Contributors: * Mathew A. Nelson * - Initial API and implementation * Flemming N. Larsen * - Code cleanup * - Removed check for the system property "SINGLEBUFFER", as it is not used * anymore * - Replaced the noDisplay with manager.setEnableGUI() and isGUIEnabled() * - Replaced the -fps option with the -tps option * - Added -nosound option and disables sound i the -nogui is specified * - Updated to use methods from WindowUtil, FileUtil, Logger, which replaces * methods that has been (re)moved from the robocode.util.Utils class * - Moved the printRunningThreads() from robocode.util.Utils into this class * and added javadoc for it * - Added playing theme music at the startup, if music is provided * - Changed to use FileUtil.getRobotsDir() * - Setting the results file is now independent of setting the battle file * - Robocode now returns with an error message if a specified battle file * could not be found * - Extended the usage / syntax for using Robocode from a console *******************************************************************************/ package net.sf.robocode.core; import net.sf.robocode.battle.BattleResultsTableModel; import net.sf.robocode.battle.IBattleManager; import net.sf.robocode.host.ICpuManager; import net.sf.robocode.host.IHostManager; import net.sf.robocode.io.FileUtil; import net.sf.robocode.io.Logger; import net.sf.robocode.recording.BattleRecordFormat; import net.sf.robocode.recording.IRecordManager; import net.sf.robocode.repository.IRepositoryManager; import net.sf.robocode.serialization.SerializableOptions; import net.sf.robocode.settings.ISettingsManager; import net.sf.robocode.sound.ISoundManager; import net.sf.robocode.ui.IWindowManager; import net.sf.robocode.version.IVersionManager; import robocode.control.events.*; import java.awt.Toolkit; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; /** * Robocode - A programming game involving battling AI tanks.<br> * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * * @author Mathew A. Nelson (original) * @author Flemming N. Larsen (contributor) * @author Pavel Savara (contributor) * @see <a target="_top" href="http://robocode.sourceforge.net">robocode.sourceforge.net</a> */ public final class RobocodeMain extends RobocodeMainBase { private final Setup setup; private final BattleObserver battleObserver = new BattleObserver(); final private ISettingsManager properties; final private IHostManager hostManager; final private IWindowManager windowManager; final private ISoundManager soundManager; final private IBattleManager battleManager; final private IRecordManager recordManager; final private IVersionManager versionManager; private static class Setup { boolean minimize; boolean exitOnComplete; String battleFilename; String recordFilename; String recordXmlFilename; String replayFilename; String resultsFilename; int tps; } public RobocodeMain(ISettingsManager properties, IHostManager hostManager, IWindowManager windowManager, ISoundManager soundManager, IBattleManager battleManager, IRecordManager recordManager, IVersionManager versionManager ) { setup = new Setup(); this.properties = properties; this.hostManager = hostManager; this.windowManager = windowManager; this.soundManager = soundManager; this.battleManager = battleManager; this.recordManager = recordManager; this.versionManager = versionManager; } public RobocodeMain(ISettingsManager properties, IHostManager hostManager, IBattleManager battleManager, IRecordManager recordManager, IVersionManager versionManager ) { setup = new Setup(); this.properties = properties; this.hostManager = hostManager; this.windowManager = null; this.soundManager = null; this.battleManager = battleManager; this.recordManager = recordManager; this.versionManager = versionManager; } public void run() { try { hostManager.initSecurity(); // Set the Look and Feel (LAF) if (windowManager.isGUIEnabled()) { windowManager.setLookAndFeel(); } properties.setOptionsBattleDesiredTPS(setup.tps); battleManager.addListener(battleObserver); if (windowManager.isGUIEnabled()) { if (!setup.minimize && setup.battleFilename == null && soundManager != null) { soundManager.playThemeMusic(); windowManager.showSplashScreen(); } windowManager.showRobocodeFrame(true, setup.minimize); // Play the intro battle if a battle file is not specified and this is the first time Robocode is being run if (setup.battleFilename == null && versionManager.isLastRunVersionChanged()) { properties.saveProperties(); windowManager.runIntroBattle(); } } final boolean enableCLIRecording = (setup.recordFilename != null || setup.recordXmlFilename != null); // Note: At this point the GUI should be opened (if enabled) before starting the battle from a battle file if (setup.battleFilename != null) { if (setup.replayFilename != null) { System.err.println("You cannot run both a battle and replay a battle record in the same time."); System.exit(8); } setup.exitOnComplete = true; battleManager.setBattleFilename(setup.battleFilename); if (new File(battleManager.getBattleFilename()).exists()) { battleManager.startNewBattle(battleManager.loadBattleProperties(), false, enableCLIRecording); } else { System.err.println("The specified battle file '" + setup.battleFilename + "' was not found"); System.exit(8); } } else if (setup.replayFilename != null) { setup.exitOnComplete = true; recordManager.loadRecord(setup.replayFilename, BattleRecordFormat.BINARY_ZIP); if (new File(setup.replayFilename).exists()) { battleManager.replay(); } else { System.err.println("The specified battle record file '" + setup.replayFilename + "' was not found"); System.exit(8); } } } catch (Throwable e) { Logger.logError(e); } } public void loadSetup(String[] args) { final String nosecMessage = "Robocode is running without a security manager.\n" + "Robots have full access to your system.\n" + "You should only run robots which you trust!"; final String exMessage = "Robocode is running in experimental mode.\n" + "Robots have access to their IRobotPeer interfaces.\n" + "You should only run robots which you trust!"; if (System.getProperty("NOSECURITY", "false").equals("true")) { Logger.logMessage(nosecMessage); } if (System.getProperty("EXPERIMENTAL", "false").equals("true")) { Logger.logMessage(exMessage); } if (windowManager != null) { if (System.getProperty("NOSECURITY", "false").equals("true")) { windowManager.messageWarning(nosecMessage); } if (System.getProperty("EXPERIMENTAL", "false").equals("true")) { windowManager.messageWarning(exMessage); } } setup.tps = properties.getOptionsBattleDesiredTPS(); // Disable canonical file path cache under Windows as it causes trouble when returning // paths with differently-capitalized file names. if (System.getProperty("os.name").toLowerCase().startsWith("windows ")) { System.setProperty("sun.io.useCanonCaches", "false"); } // Initialize the system property so the AWT does not use headless mode meaning that the // GUI (Awt and Swing) is enabled per default when running starting Robocode. // It might be set to true later, if the -nodisplay option is set (in the setEnableGUI method). // Read more about headless mode here: // http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/ System.setProperty("java.awt.headless", "false"); for (int i = 0; i < args.length; i++) { if (args[i].equals("-cwd") && (i < args.length + 1)) { changeDirectory(args[i + 1]); i++; } else if (args[i].equals("-battle") && (i < args.length + 1)) { setup.battleFilename = args[i + 1]; i++; } else if (args[i].equals("-record") && (i < args.length + 1)) { setup.recordFilename = args[i + 1]; i++; } else if (args[i].equals("-recordXML") && (i < args.length + 1)) { setup.recordXmlFilename = args[i + 1]; i++; } else if (args[i].equals("-replay") && (i < args.length + 1)) { setup.replayFilename = args[i + 1]; i++; } else if (args[i].equals("-results") && (i < args.length + 1)) { setup.resultsFilename = args[i + 1]; i++; } else if (args[i].equals("-tps") && (i < args.length + 1)) { setup.tps = Integer.parseInt(args[i + 1]); if (setup.tps < 1) { Logger.logError("tps must be > 0"); System.exit(8); } i++; } else if (args[i].equals("-minimize")) { setup.minimize = true; } else if (args[i].equals("-nodisplay")) { if (windowManager != null) { windowManager.setEnableGUI(false); } if (soundManager != null) { soundManager.setEnableSound(false); } setup.tps = 10000; // set TPS to maximum } else if (args[i].equals("-nosound")) { if (soundManager != null) { soundManager.setEnableSound(false); } } else if (args[i].equals("-?") || args[i].equals("-help")) { printUsage(); System.exit(0); } else { Logger.logError("Not understood: " + args[i]); printUsage(); System.exit(8); } } File robotsDir = FileUtil.getRobotsDir(); if (robotsDir == null) { System.err.println("No valid robot directory is specified"); System.exit(8); } else if (!(robotsDir.exists() && robotsDir.isDirectory())) { System.err.println('\'' + robotsDir.getAbsolutePath() + "' is not a valid robot directory"); System.exit(8); } // The Default Toolkit must be set as soon as we know if we are going to use headless mode or not. // That is if the toolkit must be headless or not (GUI on/off). If we are running in headless mode // from this point, a HeadlessException will be thrown if we access a AWT/Swing component. // Read more about headless mode here: // http://java.sun.com/developer/technicalArticles/J2SE/Desktop/headless/ Toolkit.getDefaultToolkit(); } private void changeDirectory(String robocodeDir) { try { FileUtil.setCwd(new File(robocodeDir)); } catch (IOException e) { System.err.println(robocodeDir + " is not a valid directory to start Robocode in."); System.exit(8); } } private void printUsage() { System.out.print( "Usage: robocode [-?] [-help] [-cwd path] [-battle filename [-results filename]\n" + " [-record filename] [-recordXML filename] [-replay filename]\n" + " [-tps tps] [-minimize] [-nodisplay] [-nosound]\n\n" + "where options include:\n" + " -? or -help Prints out the command line usage of Robocode\n" + " -cwd <path> Change the current working directory\n" + " -battle <battle file> Run the battle specified in a battle file\n" + " -results <results file> Save results to the specified text file\n" + " -record <bin record file> Record the battle into the specified file as binary\n" + " -recordXML <xml rec file> Record the battle into the specified file as XML\n" + " -replay <record file> Replay the specified battle record\n" + " -tps <tps> Set the TPS > 0 (Turns Per Second)\n" + " -minimize Run minimized when Robocode starts\n" + " -nodisplay Run with the display / GUI disabled\n" + " -nosound Run with sound disabled\n\n" + "Java Properties include:\n" + " -DWORKINGDIRECTORY=<path> Set the working directory\n" + " -DROBOTPATH=<path> Set the robots directory (default is 'robots')\n" + " -DBATTLEPATH=<path> Set the battles directory (default is 'battles')\n" + " -DNOSECURITY=true|false Enable/disable Robocode's security manager\n" + " -Ddebug=true|false Enable/disable debugging used for preventing\n" + " robot timeouts and skipped turns, and allows an\n" + " an unlimited painting buffer when debugging robots\n" + " -DEXPERIMENTAL=true|false Enable/disable access to peer in robot interfaces\n" + " -DPARALLEL=true|false Enable/disable parallel processing of robots turns\n" + " -DRANDOMSEED=<long number> Set seed for deterministic behavior of random\n" + " numbers\n"); } private void printResultsData(BattleCompletedEvent event) { // Do not print out if no result file has been specified and the GUI is enabled if ((setup.resultsFilename == null && (!setup.exitOnComplete || windowManager.isGUIEnabled()))) { return; } PrintStream out = null; FileOutputStream fos = null; if (setup.resultsFilename == null) { out = Logger.realOut; } else { File f = new File(setup.resultsFilename); try { fos = new FileOutputStream(f); out = new PrintStream(fos); } catch (IOException e) { Logger.logError(e); } } BattleResultsTableModel resultsTable = new BattleResultsTableModel(event.getSortedResults(), event.getBattleRules().getNumRounds()); if (out != null) { resultsTable.print(out); out.close(); } if (fos != null) { try { fos.close(); } catch (IOException e) {// swallow } } } private class BattleObserver extends BattleAdaptor { boolean isReplay; @Override public void onBattleStarted(BattleStartedEvent event) { isReplay = event.isReplay(); } @Override public void onBattleCompleted(BattleCompletedEvent event) { if (!isReplay) { printResultsData(event); } if (setup.recordFilename != null) { recordManager.saveRecord(setup.recordFilename, BattleRecordFormat.BINARY_ZIP, new SerializableOptions(false)); } if (setup.recordXmlFilename != null) { recordManager.saveRecord(setup.recordXmlFilename, BattleRecordFormat.XML, new SerializableOptions(false)); } } @Override public void onBattleMessage(BattleMessageEvent event) { Logger.realOut.println(event.getMessage()); } @Override public void onBattleError(BattleErrorEvent event) { Logger.realErr.println(event.getError()); } } public void cleanup() { final IWindowManager windowManager = Container.getComponent(IWindowManager.class); if (windowManager != null) { windowManager.cleanup(); } Container.getComponent(IBattleManager.class).cleanup(); Container.getComponent(IHostManager.class).cleanup(); } public void initForRobocodeEngine(IBattleListener listener) { final IWindowManager windowManager = Container.getComponent(IWindowManager.class); if (windowManager != null) { windowManager.setSlave(true); windowManager.setEnableGUI(false); } Container.getComponent(IHostManager.class).initSecurity(); if (listener != null) { Container.getComponent(IBattleManager.class).addListener(listener); } Container.getComponent(ICpuManager.class).getCpuConstant(); Container.getComponent(IRepositoryManager.class).reload(versionManager.isLastRunVersionChanged()); } }