/******************************************************************************* * 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 * - Replaced FileSpecificationVector with plain Vector * - GUI is disabled per default. If the setVisible() is called, the GUI will * be enabled. The close() method is only calling dispose() on the * RobocodeFrame if the GUI is enabled * - Updated to use methods from FileUtil and Logger, which replaces * methods that have been (re)moved from the robocode.util.Utils class * - Changed to use FileUtil.getRobotsDir() * - Modified getLocalRepository() to support teams by using * FileSpecification instead of RobotFileSpecification * - System.out, System.err, and System.in is now only set once, as new * instances of the RobocodeEngine causes memory leaks with * System.setOut() and System.setErr() * - Updated Javadocs * Robert D. Maupin * - Replaced old collection types like Vector and Hashtable with * synchronized List and HashMap * Nathaniel Troutman * - Bugfix: Inconsistent Behavior of RobocodeEngine.setVisible() * - Bugfix: Added cleanup of the Robocode manager to the close() method * Joachim Hofer * - Bugfix in onBattleCompleted() where the RobotResults were ordered * incorrectly when calling the battleComplete() so the results were given * for the wrong robots *******************************************************************************/ package robocode.control; import net.sf.robocode.battle.IBattleManagerBase; import net.sf.robocode.core.ContainerBase; import net.sf.robocode.gui.IWindowManagerBase; import net.sf.robocode.io.FileUtil; import net.sf.robocode.io.Logger; import net.sf.robocode.manager.IVersionManagerBase; import net.sf.robocode.repository.IRepositoryManagerBase; import net.sf.robocode.security.HiddenAccess; import robocode.control.events.*; import java.io.File; /** * The RobocodeEngine is the interface provided for external applications * in order to let these applications run battles within the Robocode application, * and to get the results from these battles. * <p/> * This class in the main entry class of the {@code robocode.control} package. * <p/> * The RobocodeEngine is used by e.g. RoboRumble@Home client, which is integrated in * Robocode. In addition, the RobocodeEngine is also used by the test units for * testing the Robocode application itself. * * @author Mathew A. Nelson (original) * @author Flemming N. Larsen (contributor) * @author Robert D. Maupin (contributor) * @author Nathaniel Troutman (contributor) * @author Joachim Hofer (contributor) * @author Pavel Savara (contributor) */ public class RobocodeEngine implements IRobocodeEngine { private BattleObserver battleObserver; private BattleSpecification battleSpecification; /** * Creates a new RobocodeEngine for controlling Robocode. * In order for this constructor to work, the current working directory must be the * home directory directory of Robocode, e.g. C:\Robocode * * @see #RobocodeEngine(File) * @see #close() * @since 1.6.2 */ public RobocodeEngine() { init(null, (IBattleListener) null); } /** * Creates a new RobocodeEngine for controlling Robocode. * * @param robocodeHome the home directory of Robocode, e.g. C:\Robocode. * @see #RobocodeEngine() * @see #close() * @since 1.6.2 */ public RobocodeEngine(File robocodeHome) { init(robocodeHome, (IBattleListener) null); } /** * @deprecated Since 1.6.2. Use {@link #RobocodeEngine(File)} and * {@link #addBattleListener(IBattleListener) addBattleListener()} instead. * <p/> * Creates a new RobocodeEngine for controlling Robocode. * * @param robocodeHome the root directory of Robocode, e.g. C:\Robocode. * @param listener the listener that must receive the callbacks from this * RobocodeEngine. * @see #RobocodeEngine() * @see #RobocodeEngine(File) * @see #close() */ @Deprecated @SuppressWarnings({ "deprecation"}) public RobocodeEngine(File robocodeHome, RobocodeListener listener) { init(robocodeHome, listener); } /** * @deprecated Since 1.6.2. Use {@link #RobocodeEngine()} and * {@link #addBattleListener(IBattleListener) addBattleListener()} instead. * <p/> * Creates a new RobocodeEngine for controlling Robocode. The JAR file of * Robocode is used to determine the root directory of Robocode. * * @param listener the listener that must receive the callbacks from this * RobocodeEngine. * @see #RobocodeEngine() * @see #RobocodeEngine(File) * @see #close() */ @Deprecated @SuppressWarnings({ "deprecation"}) public RobocodeEngine(RobocodeListener listener) { init(null, listener); } public RobocodeEngine(IBattleListener listener) { init(null, listener); } /** * {@inheritDoc} */ @Override protected void finalize() throws Throwable { try { // Make sure close() is called to prevent memory leaks close(); } finally { super.finalize(); } } @SuppressWarnings("deprecation") // We must still support deprecated RobocodeListener private void init(File robocodeHome, RobocodeListener listener) { if (listener != null) { battleObserver = new BattleObserver(); battleObserver.listener = listener; } HiddenAccess.initContainerForRobotEngine(robocodeHome, battleObserver); } private void init(File robocodeHome, IBattleListener listener) { HiddenAccess.initContainerForRobotEngine(robocodeHome, listener); } /** * {@inheritDoc} */ public void addBattleListener(IBattleListener listener) { ContainerBase.getComponent(IBattleManagerBase.class).addListener(listener); } /** * {@inheritDoc} */ public void removeBattleListener(IBattleListener listener) { ContainerBase.getComponent(IBattleManagerBase.class).removeListener(listener); } /** * {@inheritDoc} */ public void close() { setVisible(false); if (battleObserver != null) { ContainerBase.getComponent(IBattleManagerBase.class).removeListener(battleObserver); } HiddenAccess.cleanup(); } /** * {@inheritDoc} */ public String getVersion() { return ContainerBase.getComponent(IVersionManagerBase.class).getVersion(); } /** * Returns the current working directory. * * @return a File for the current working directory. * * @since 1.7.1 */ public static File getCurrentWorkingDir() { return FileUtil.getCwd(); } /** * Returns the directory containing the robots. * * @return a File for the robot directory containing all robots. * * @since 1.7.1 */ public static File getRobotsDir() { return FileUtil.getRobotsDir(); } /** * {@inheritDoc} */ public void setVisible(boolean visible) { ContainerBase.getComponent(IWindowManagerBase.class).setVisibleForRobotEngine(visible); } /** * {@inheritDoc} */ public RobotSpecification[] getLocalRepository() { final IRepositoryManagerBase repository = ContainerBase.getComponent(IRepositoryManagerBase.class); repository.refresh(); // Bug fix [2972932] return repository.getSpecifications(); } /** * {@inheritDoc} */ public RobotSpecification[] getLocalRepository(String selectedRobots) { final IRepositoryManagerBase repository = ContainerBase.getComponent(IRepositoryManagerBase.class); repository.refresh(); // Bug fix [2972932] return repository.loadSelectedRobots(selectedRobots); } /** * {@inheritDoc} */ public void runBattle(BattleSpecification battleSpecification) { runBattle(battleSpecification, null, false); } /** * {@inheritDoc} */ public void runBattle(BattleSpecification battleSpecification, boolean waitTillOver) { runBattle(battleSpecification, null, waitTillOver); } /** * {@inheritDoc} */ public void runBattle(BattleSpecification battleSpecification, String initialPositions, boolean waitTillOver) { this.battleSpecification = battleSpecification; ContainerBase.getComponent(IBattleManagerBase.class).startNewBattle(battleSpecification, initialPositions, waitTillOver, false); } /** * {@inheritDoc} */ public void waitTillBattleOver() { ContainerBase.getComponent(IBattleManagerBase.class).waitTillOver(); } /** * {@inheritDoc} */ public void abortCurrentBattle() { ContainerBase.getComponent(IBattleManagerBase.class).stop(true); } /** * Prints out all running threads to standard system out. * * @since 1.6.2 */ public static void printRunningThreads() { ThreadGroup currentGroup = Thread.currentThread().getThreadGroup(); if (currentGroup == null) { return; } while (currentGroup.getParent() != null) { currentGroup = currentGroup.getParent(); } ThreadGroup groups[] = new ThreadGroup[256]; Thread threads[] = new Thread[256]; int numGroups = currentGroup.enumerate(groups, true); for (int i = 0; i < numGroups; i++) { currentGroup = groups[i]; if (currentGroup.isDaemon()) { Logger.realOut.print(" "); } else { Logger.realOut.print("* "); } Logger.realOut.println("In group: " + currentGroup.getName()); int numThreads = currentGroup.enumerate(threads); for (int j = 0; j < numThreads; j++) { if (threads[j].isDaemon()) { Logger.realOut.print(" "); } else { Logger.realOut.print("* "); } Logger.realOut.println(threads[j].getName()); } Logger.realOut.println("---------------"); } } /** * Registered only if listener in not null. */ private class BattleObserver extends BattleAdaptor { @SuppressWarnings("deprecation") // We must still support deprecated RobocodeListener private RobocodeListener listener; @SuppressWarnings("deprecation") // We must still support deprecated RobocodeListener @Override public void onBattleFinished(BattleFinishedEvent event) { if (event.isAborted()) { listener.battleAborted(battleSpecification); } } @SuppressWarnings("deprecation") // We must still support deprecated RobocodeListener @Override public void onBattleCompleted(BattleCompletedEvent event) { listener.battleComplete(battleSpecification, RobotResults.convertResults(event.getSortedResults())); } @SuppressWarnings("deprecation") // We must still support deprecated RobocodeListener @Override public void onBattleMessage(BattleMessageEvent event) { listener.battleMessage(event.getMessage()); } } }