package net.sf.colossus.appmain; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; import net.sf.colossus.client.Client; import net.sf.colossus.cmdline.CmdLine; import net.sf.colossus.cmdline.Opt; import net.sf.colossus.cmdline.Opts; import net.sf.colossus.common.Constants; import net.sf.colossus.common.Options; import net.sf.colossus.common.WhatNextManager; import net.sf.colossus.common.WhatNextManager.WhatToDoNext; import net.sf.colossus.guiutil.DebugMethods; import net.sf.colossus.server.GameLoading; import net.sf.colossus.server.GameServerSide; import net.sf.colossus.server.VariantSupport; import net.sf.colossus.util.BuildInfo; import net.sf.colossus.util.ViableEntityManager; import net.sf.colossus.variant.Variant; import net.sf.colossus.webclient.WebClient; /** * Class Start contains code to start the different types of games. * * @author David Ripton * @author Clemens Katzer (rewritten big parts of the main() method) */ public final class Start { private static final Logger LOGGER = Logger.getLogger(Start.class .getName()); private CmdLine cmdLine; private final WhatNextManager whatNextManager; // Options remembered only inside this running application, // related to server/port/name startup settings; initialized // from command line options, perhaps modified by dialogs. // We never save those startOptions, but some of them are copied // to the server options in GetPlayers when the user initiates // a related action. private final Options startOptions; /** * To create the one "Start" object which handles initiates the * "whatToDoNext" action according to what the user wants. * * Brings up one of the dialogs, or starts a Game, a Network client * or a Web Client. */ public Start(String[] args) { this.startOptions = new Options(Constants.OPTIONS_START); // initialize it from the -D..forceViewBoard cmdline settings, // defaulting to false if no such argument given startOptions .setOption(Options.FORCE_BOARD, Constants.FORCE_VIEW_BOARD); this.whatNextManager = new WhatNextManager(startOptions); commandLineProcessing(args); } public Options getStartOptions() { return startOptions; } public WhatNextManager getWhatNextManager() { return whatNextManager; } public WhatToDoNext getWhatToDoNext() { return whatNextManager.getWhatToDoNext(); } /** * Print a usage string to stdout. (*Not* to the logfile, where casual * users will miss it.) */ private static void usage(Opts opts) { System.out.println("Usage: java -jar Colossus.jar [options]"); Iterator<Opt> it = opts.getOptions().iterator(); while (it.hasNext()) { Opt opt = it.next(); // This needs to go to the console, not the log, to be useful. System.out.println(opt.toString()); } } /** * Prepare the "Opts" object to parse all options from command line. * As result, creates/sets the instance variable CmdLine object "cmdLine" * from which one can then query which options were set, and their value * if they require one. * * @param args The String-Array given to main() * @return */ private void commandLineProcessing(String[] args) { Opts opts = new Opts(); // Catch-all block so we can log fatal exceptions. try { opts.addOption('h', "help", false, "Show options help"); opts.addOption('l', "load", true, "Load savegame"); opts.addOption('z', "latest", false, "Load latest savegame"); opts.addOption('g', "go", false, "Skip startup dialogs"); opts.addOption('v', "variant", true, "Set variant"); opts.addOption('u', "nhuman", true, "Number of humans"); opts.addOption('i', "nai", true, "Number of AIs (default: random)"); opts.addOption('Z', "simpleai", true, "Number of SimpleAIs"); opts.addOption('r', "rationalai", true, "Number of RationalAIs"); opts.addOption('M', "milvangai", true, "Number of MilvangAIs"); opts.addOption('n', "nnetwork", true, "Number of network slots"); opts.addOption('q', "quit", false, "Quit JVM when game ends"); opts.addOption('p', "port", true, "Server port number"); opts.addOption('d', "delay", true, "AI delay in ms"); opts.addOption('t', "timelimit", true, "AI time limit in s"); opts.addOption('c', "client", false, "Run network client instead"); opts.addOption('C', "spectatorclient", false, "Run network spectator client"); opts.addOption('w', "webclient", false, "Run web client instead"); opts.addOption('F', "flagfile", true, "Create flagfile when socket up"); opts.addOption('s', "server", true, "Server name or IP"); opts.addOption('S', "autosave", false, "Autosave"); opts.addOption('A', "autoplay", false, "Autoplay"); opts.addOption('N', "non-random-battle-dice", false, "Use non-random battle dice"); opts.addOption('R', "resetOptions", false, "Reset options"); opts.addOption('m', "myname", true, "My player name"); opts.addOption('O', "noobserver", false, "Go on without observer"); cmdLine = opts.parse(args); } catch (Exception ex) { LOGGER.log(Level.SEVERE, "Exception during commandline processing: " + ex.toString(), ex); System.exit(1); return; // just to avoid the warning "cl might be null" ... } if (cmdLine.optIsSet('h')) { usage(opts); System.exit(0); } } /** * Based on command line options -c, -w, possibly -g, set * startObject to the right "whatToDoNext" action and * set in startOptions the related values. * Expects that server (cf) options are already loaded. * * */ public void setInitialAction(Options serverOptions, Options netclientOptions) { // just as shortcut... CmdLine cl = cmdLine; // Host, port and player name are stored back only to the startObject. // They would be copied to the server cf file in GetPlayers, when one // starts a client (host, player, and client-connects-to-port), // or when runs a game (load or new game) for serve-at-port. // The result is that the command line options given to -g do not // immediately modify the cf file settings -- unless player // "confirms" them by one of the GetPlayers actions. String hostname = null; if (cl.optIsSet('s')) { hostname = cl.getOptValue('s'); } else if (cl.optIsSet('g') && (cl.optIsSet('c') || cl.optIsSet('C'))) { // Host empty means for NetworkClientDialog: no command line wish given // => it will initialize host name box based on mostly LRU list. // If no -s given, but -g, we must here do the same preferredHost // evaluation what NetworkClientDialog would do, so that the automatic // Client starting has a host != null. // And I want that "with or without -g" behaves (apart from the // whether one has to click somewhere or not) otherwise same, // ( = would end up same host). Set<String> dummy = new TreeSet<String>(); String preferred = NetworkClientDialog.initServerNames(hostname, dummy, netclientOptions); hostname = preferred; dummy.clear(); } if (hostname == null) { // Options class does not like null values - use empty instead. hostname = ""; } startOptions.setOption(Options.runClientHost, hostname); startOptions.setOption(Options.webServerHost, hostname); if (cl.optIsSet('C')) { startOptions.setOption(Options.runSpectatorClient, true); } // Ports (both serves-at-port and client-connects-to-port): // take from cf file (not set: defaultPort), overridable from cmdline int cp = netclientOptions.getIntOption(Options.runClientPort); int sp = serverOptions.getIntOption(Options.serveAtPort); // WebPort: only hand over the cmdline wish. If none, wEWebClient // itself handles the "which one to use (cmdline, cf, default)". int wp = -1; cp = (cp != -1 ? cp : Constants.defaultPort); sp = (sp != -1 ? sp : Constants.defaultPort); int pOpt; if (cl.optIsSet('p')) { pOpt = Integer.parseInt(cl.getOptValue('p')); if (pOpt != -1) { cp = pOpt; sp = pOpt; wp = pOpt; } } startOptions.setOption(Options.runClientPort, cp); startOptions.setOption(Options.serveAtPort, sp); startOptions.setOption(Options.webServerPort, wp); String playerName = netclientOptions .getStringOption(Options.runClientPlayer); String webLogin = ""; if (playerName == null || playerName.equals("")) { playerName = Constants.username; } if (cl.optIsSet('m')) { playerName = cl.getOptValue('m'); webLogin = cl.getOptValue('m'); } startOptions.setOption(Options.runClientPlayer, playerName); startOptions.setOption(Options.webClientLogin, webLogin); String webGameFlagFileName = ""; if (cl.optIsSet('F')) { webGameFlagFileName = cl.getOptValue('F'); } startOptions.setOption(Options.webFlagFileName, webGameFlagFileName); if (cl.optIsSet('l') || cl.optIsSet('z')) { whatNextManager.setWhatToDoNext(WhatToDoNext.LOAD_GAME, false, false); String filename = null; if (cl.optIsSet('l')) { filename = cl.getOptValue('l'); } else if (cl.optIsSet('z')) { filename = "--latest"; } else { LOGGER.severe("Unreacheable else block reached??"); filename = "--latest"; } startOptions.setOption(Options.loadGameFileName, filename); } else if (cl.optIsSet('g')) { if (cl.optIsSet('c') || cl.optIsSet('C')) { whatNextManager.setWhatToDoNext(WhatToDoNext.START_NET_CLIENT, false); } else if (cl.optIsSet('w')) { whatNextManager.setWhatToDoNext(WhatToDoNext.START_WEB_CLIENT, false); } else { whatNextManager .setWhatToDoNext(WhatToDoNext.START_GAME, false); } } else { if (cl.optIsSet('c') || cl.optIsSet('C')) { whatNextManager.setWhatToDoNext( WhatToDoNext.NET_CLIENT_DIALOG, false); } else if (cl.optIsSet('w')) { whatNextManager.setWhatToDoNext(WhatToDoNext.START_WEB_CLIENT, false); } else { whatNextManager.setWhatToDoNext( WhatToDoNext.GET_PLAYERS_DIALOG, false); } } } /** * Bring up the GetPlayers dialog, and then wait until is has set * startObject to the next action to do and notified us to continue. * * @param options The "server side" main options Object which holds the * information what kind of game to play next (variant, which players) * and the "Game options" for the to-be-started game, like * unlimitedMulligans, viewmode, balancedTowers, ...) */ private void runGetPlayersDialogAndWait(Options options) { Object mutex = new Object(); new GetPlayers(options, mutex, getWhatNextManager(), false); synchronized (mutex) { try { mutex.wait(); } catch (InterruptedException e) { LOGGER.log(Level.WARNING, "Start waiting for GetPlayers " + "to complete, wait interrupted?"); // just to be sure to do something useful there... whatNextManager.setWhatToDoNext( WhatToDoNext.GET_PLAYERS_DIALOG, false); } } mutex = null; } /* * Modify options from command-line args if possible. * Return false if something is wrong. */ private boolean setupOptionsFromCommandLine(CmdLine cl, Options startOptions, Options options) { if (cl == null) { return true; } int numHumans = 0; int numAIs = 0; int numNetworks = 0; int numSimpleAIs = 0; int numRationalAIs = 0; int numMilvangAIs = 0; options.removeOption(Options.autoPlay); options.removeOption(Options.goOnWithoutObserver); if (cl.optIsSet('R')) { options.setOption(Options.autosave, false); options.setOption(Options.autoQuit, false); options.setOption(Options.variant, Constants.variantArray[0]); } if (cl.optIsSet('v')) { String variantName = cl.getOptValue('v'); // XXX Check that this variant is in the list. options.setOption(Options.variant, variantName); } else if (options.getStringOption(Options.variant) == null) { options.setOption(Options.variant, Constants.variantArray[0]); } if (cl.optIsSet('q')) { options.setOption(Options.autoQuit, true); } if (cl.optIsSet('O')) { // needed basically only for the old stresstest which runs // without any window options.setOption(Options.goOnWithoutObserver, true); } if (cl.optIsSet('S')) { options.setOption(Options.autosave, true); } if (cl.optIsSet('A')) { options.setOption(Options.autoPlay, true); } if (cl.optIsSet('N')) { options.setOption(Options.fixedSequenceBattleDice, true); } if (cl.optIsSet('d')) { String buf = cl.getOptValue('d'); int delay = Integer.parseInt(buf); options.setOption(Options.aiDelay, delay); } if (cl.optIsSet('t')) { String buf = cl.getOptValue('t'); int limit = Integer.parseInt(buf); options.setOption(Options.aiTimeLimit, limit); } if (cl.optIsSet('u')) { options.clearPlayerInfo(); String buf = cl.getOptValue('u'); numHumans = Integer.parseInt(buf); } if (cl.optIsSet('i')) { options.clearPlayerInfo(); String buf = cl.getOptValue('i'); numAIs = Integer.parseInt(buf); } if (cl.optIsSet('Z')) { options.clearPlayerInfo(); String buf = cl.getOptValue('Z'); numSimpleAIs = Integer.parseInt(buf); } if (cl.optIsSet('r')) { options.clearPlayerInfo(); String buf = cl.getOptValue('r'); numRationalAIs = Integer.parseInt(buf); } if (cl.optIsSet('M')) { options.clearPlayerInfo(); String buf = cl.getOptValue('M'); numMilvangAIs = Integer.parseInt(buf); } if (cl.optIsSet('n')) { options.clearPlayerInfo(); String buf = cl.getOptValue('n'); numNetworks = Integer.parseInt(buf); } // Quit if values are bogus. if (numHumans < 0 || numAIs < 0 || numNetworks < 0 || numHumans + numAIs + numNetworks > Constants.MAX_MAX_PLAYERS) { LOGGER.log(Level.SEVERE, "Illegal number of players"); return false; } if (numAIs < (numSimpleAIs + numRationalAIs + numMilvangAIs)) { LOGGER.log(Level.SEVERE, "Illegal number of specific AIs"); return false; } for (int i = 0; i < numHumans; i++) { String name = null; String preferredHumanName = startOptions .getStringOption(Options.runClientPlayer); if (i == 0 && preferredHumanName != null && !preferredHumanName.equals("")) { name = preferredHumanName; } else { // name = Constants.byColor + i; name = Constants.byType + i; } options.setOption(Options.playerName + i, name); options.setOption(Options.playerType + i, Constants.human); } for (int j = numHumans; j < numNetworks + numHumans; j++) { String name = Constants.byClient + j; options.setOption(Options.playerName + j, name); options.setOption(Options.playerType + j, Constants.network); } for (int k = numHumans + numNetworks; k < numAIs + numHumans + numNetworks; k++) { // String name = Constants.byColor + k; String name = Constants.byType + k; options.setOption(Options.playerName + k, name); if (numSimpleAIs > 0) { options.setOption(Options.playerType + k, "SimpleAI"); numSimpleAIs--; } else if (numRationalAIs > 0) { options.setOption(Options.playerType + k, "RationalAI"); numRationalAIs--; } else if (numMilvangAIs > 0) { options.setOption(Options.playerType + k, "MilvangAI"); numMilvangAIs--; } else { options.setOption(Options.playerType + k, Constants.defaultAI); } } return true; } /** * Do the setup of the various Options objects (server, netclient), * some more preparations, and then it stays in the loop which * - waits for user input what to do next * - initiates that action and waits until it completes (or if canceled, * like closing the network client dialog, bring up back the main * (=GetPlayers) dialog, or if user requests Quit, exit the loop; * and when it exited the loop control will return back to main() * and the JVM should terminate sooner or later ;-) */ private void setupAndLoop() { // Read option settings (from Server cf file) Options serverOptions = new Options(Constants.OPTIONS_SERVER_NAME); serverOptions.loadOptions(); // Read netclient option settings (from own cf file) Options netclientOptions = new Options( Constants.OPTIONS_NET_CLIENT_NAME); netclientOptions.loadOptions(); // set in startObject and startOptions the values related to // what-to-do, host, port, playerName: setInitialAction(serverOptions, netclientOptions); // Set in options the remaining options // Needs startOptions only to get the player name to use in some cases. if (!setupOptionsFromCommandLine(cmdLine, startOptions, serverOptions)) { LOGGER.log(Level.SEVERE, "setupOptionsFromCommandLine signalled error, " + "continuing anyway.", (Throwable)null); } boolean oneClientRunOnly = false; if (cmdLine.optIsSet('c') && cmdLine.optIsSet('g') && cmdLine.optIsSet('q')) { oneClientRunOnly = true; } // Command line arguments have effect only to first game // - or are stored within the options or startOptions. cmdLine = null; // Make sure "AIs stop when no humans left" is off when stresstesting if (Options.isStresstest()) { startOptions.setOption(Options.autoStop, false); serverOptions.setOption(Options.autoStop, false); } boolean dontWait = false; // Now loop until user requested Quitting the whole application: while (getWhatToDoNext() != WhatToDoNext.QUIT_ALL) { // re-initialize options, except in first loop round, // there they have been loaded already and modified // according to command line options if (serverOptions == null) { serverOptions = new Options(Constants.OPTIONS_SERVER_NAME); serverOptions.loadOptions(); if (Options.isStresstest()) { serverOptions.setOption(Options.autoPlay, true); } } if (netclientOptions == null) { netclientOptions = new Options( Constants.OPTIONS_NET_CLIENT_NAME); netclientOptions.loadOptions(); } // Unless there is already something selected what to do // (e.g. in in first round on command line, or user ended // a game/closed board with selecting Load Game etc.), // as first thing come up with the dialog to ask what to do: if (getWhatToDoNext() == WhatToDoNext.GET_PLAYERS_DIALOG) { runGetPlayersDialogAndWait(serverOptions); } // intentionally not else if - short way if user selected // in GetPlayers dialog the "Run network client" button. if (getWhatToDoNext() == WhatToDoNext.NET_CLIENT_DIALOG) { runNetClientDialogAndWait(); } // ---------------------------------------------------------------- // Longish if-elseif-else - now we do the thing user wants: // TODO change to switch statement if (getWhatToDoNext() == WhatToDoNext.GET_PLAYERS_DIALOG || getWhatToDoNext() == WhatToDoNext.NET_CLIENT_DIALOG) { // ok, just done. Need if also in this else-if chain // otherwise the "else" would complain... dontWait = true; } else if (getWhatToDoNext() == WhatToDoNext.START_GAME) { whatNextManager.decrementHowManyGamesLeft(); // TODO is this re-setting it needed? whatNextManager.setWhatToDoNext( WhatToDoNext.GET_PLAYERS_DIALOG, false); int port = startOptions.getIntOption(Options.serveAtPort); serverOptions.setOption(Options.serveAtPort, port); String webGameFlagFileName = startOptions .getStringOption(Options.webFlagFileName); startOptions.removeOption(Options.webFlagFileName); String variantName = serverOptions .getStringOption(Options.variant); Variant variant = VariantSupport.loadVariantByName( variantName, true); GameServerSide game = GameServerSide.newGameServerSide( getWhatNextManager(), serverOptions, variant); if (webGameFlagFileName != null && !webGameFlagFileName.equals("")) { whatNextManager.setWhatToDoNext(WhatToDoNext.QUIT_ALL, false); game.setFlagFilename(webGameFlagFileName); } game.startNewGameAndWaitUntilOver(null); } else if (getWhatToDoNext() == WhatToDoNext.LOAD_GAME) { boolean wasInteractive = whatNextManager.isInteractive(); whatNextManager.decrementHowManyGamesLeft(); // TODO is this re-setting it needed? whatNextManager.setWhatToDoNext( WhatToDoNext.GET_PLAYERS_DIALOG, false); int port = startOptions.getIntOption(Options.serveAtPort); serverOptions.setOption(Options.serveAtPort, port); String loadFileName = startOptions .getStringOption(Options.loadGameFileName); if (loadFileName != null && loadFileName.length() > 0) { GameLoading loader = new GameLoading(); String reasonForFailure = loader.loadGame(loadFileName); if (reasonForFailure == null) { GameServerSide game = GameServerSide .newGameServerSide(getWhatNextManager(), serverOptions, loader.getVariant()); game.setWasLoaded(true); serverOptions.clearPlayerInfo(); String webGameFlagFileName = startOptions .getStringOption(Options.webFlagFileName); startOptions.removeOption(Options.webFlagFileName); if (webGameFlagFileName != null && !webGameFlagFileName.equals("")) { whatNextManager.setWhatToDoNext( WhatToDoNext.QUIT_ALL, false); game.setFlagFilename(webGameFlagFileName); } game.loadGameAndWaitUntilOver(loader.getRoot()); } else { if (wasInteractive) { JOptionPane.showMessageDialog(null, "Error loading game: " + reasonForFailure, "Loading game failed!", JOptionPane.ERROR_MESSAGE); } LOGGER.severe("GameLoading returned failure: " + reasonForFailure); } } else { LOGGER.log(Level.SEVERE, "Selected action LoadGame, but filename is '" + loadFileName + "' (= null or empty)!", (Throwable)null); } } // User clicked "Go" button in the Network Client tab of // GetPlayers - GUI stores values in options // @TODO: get via startObject instead? else if (getWhatToDoNext() == WhatToDoNext.START_NET_CLIENT) { whatNextManager.decrementHowManyGamesLeft(); // by default (if user does not say anything other when ending), // after that come back to NetClient dialog. if (oneClientRunOnly) { whatNextManager.setWhatToDoNext(WhatToDoNext.QUIT_ALL, false); } else { whatNextManager.setWhatToDoNext( WhatToDoNext.NET_CLIENT_DIALOG, false); } dontWait = startNetClient(startOptions); } else if (getWhatToDoNext() == WhatToDoNext.START_WEB_CLIENT) { // By default get back to Main dialog. whatNextManager.setWhatToDoNext( WhatToDoNext.GET_PLAYERS_DIALOG, false); String hostname = startOptions .getStringOption(Options.webServerHost); int port = startOptions.getIntOption(Options.webServerPort); String login = startOptions .getStringOption(Options.webClientLogin); String password = null; new WebClient(getWhatNextManager(), hostname, port, login, password); } // User clicked Quit in GetPlayers (this loop round), // --or-- // User selected File=>Quit in the game started from previous // loop round. else if (getWhatToDoNext() == WhatToDoNext.QUIT_ALL) { // Nothing to do, loop will end. dontWait = true; } // What else?? else { LOGGER.log(Level.SEVERE, "Unknown value '" + getWhatToDoNext() + "' in main() loop???", (Throwable)null); } // ---------------------------------------------------------- // Activity initiated ... or at least attempted to do so. // Wait for it to end, except if it was a failed netclient start // or the activity "Quit" from main menu anyway. if (dontWait) { LOGGER.log(Level.FINEST, "QuitAll selected, not waiting for anything to finish."); dontWait = false; } else { ViableEntityManager.waitUntilAllGone(); } // ResourceLoader has static String telling the server; if not reset, // for a remote client closing while game is not over, he set next // to do to GetPlayers dialog, dialog wants to load Variant Readme, // resourceloader would fail. net.sf.colossus.util.StaticResourceLoader.resetDataServer(); // DebugStuff.doCleanupStuff(false); // For Stresstesting (controlled by a system property): if (Options.isStresstest() && whatNextManager.getHowManyGamesLeft() > 0) { String loadFileName = startOptions .getStringOption(Options.loadGameFileName); if (loadFileName != null) { whatNextManager.setWhatToDoNext(WhatToDoNext.LOAD_GAME, false); } else { whatNextManager.setWhatToDoNext(WhatToDoNext.START_GAME, false); } } else { serverOptions = null; netclientOptions = null; } } // end WHILE not QuitAll } private void runNetClientDialogAndWait() { Object mutex = new Object(); new NetworkClientDialog(mutex, getWhatNextManager()); synchronized (mutex) { try { mutex.wait(); } catch (InterruptedException e) { LOGGER.log(Level.WARNING, "Start waiting for GetPlayers " + "to complete, wait interrupted?"); // just to be sure to do something useful there... whatNextManager.setWhatToDoNext( WhatToDoNext.GET_PLAYERS_DIALOG, false); } } mutex = null; } private boolean startNetClient(Options startOptions) { boolean dontWait = false; String playerName = startOptions .getStringOption(Options.runClientPlayer); String hostname = startOptions.getStringOption(Options.runClientHost); int port = startOptions.getIntOption(Options.runClientPort); boolean spectator = startOptions.getOption(Options.runSpectatorClient); try { String type = Constants.aiPackage + Constants.network; Client.createClient(hostname, port, playerName, type, whatNextManager, null, false, false, true, spectator); } catch (Exception e) { LOGGER.warning("Creating the network CLIENT failed, reason: " + e.getMessage()); dontWait = true; } // If starting net client succeeded, main() shall wait until // it ends. But if it fails, main() shall not wait, so that user // gets a new dialog immediately. return dontWait; } /* ********************************************************************** * * The m a i n () of the Start Class * * ********************************************************************** */ public static void main(String[] args) { DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); LOGGER.log( Level.INFO, "Start for Colossus version '" + BuildInfo.getFullBuildInfoString() + "' at " + dateFormat.format(new Date())); Start startObject = new Start(args); // Setup the various options objects, and loop until user // requests quitting the application startObject.setupAndLoop(); // After there is no reference to the startObject, the GC should // be able to clean up the while object tree. We will see :) startObject = null; WelcomeDialog.disposeDialogIfNecessary(); // ================================================================== // Application-ending related processing, mostly for debug purposes // Probably this could be totally deleted, but... // DebugMethods.doCleanupStuff(true); final boolean doWaitReturnLoop = false; if (doWaitReturnLoop) { // if this is used, make sure you have debug level for // Root logger and InstanceTracker at least to INFO, // otherwise you won't see any statistics... final boolean forceLoopAnyway = false; DebugMethods.waitReturnLoop(forceLoopAnyway); } final boolean printObjectStatistics = false; if (printObjectStatistics) { net.sf.colossus.util.InstanceTracker.printStatistics(); } // If want to have a way to prevent it from straight exit, // e.g. look at it with Profiler when everything is supposed to // be gone already (Clemens) final boolean waitReturnBeforeExiting = false; if (waitReturnBeforeExiting) { LOGGER.log(Level.ALL, "OK, after next RETURN it will really end."); DebugMethods.waitReturn(); } LOGGER.log(Level.FINE, "Start.main() at the end " + "- JVM should exit now by itself."); // JVM should do a clean exit now, no System.exit() needed. // To be sure, at all places where user selects "Quit", a demon // thread is started that does the System.exit() after a certain // delay (currently 10 secs - see class TimedJvmQuit). } }