1 /* $Id$ */
2 /***************************************************************************
3  *                      (C) Copyright 2003 - Marauroa                      *
4  ***************************************************************************
5  ***************************************************************************
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  ***************************************************************************/
13 package games.stendhal.client;
14 
15 import static games.stendhal.common.constants.Actions.MOVE_CONTINUOUS;
16 import static java.io.File.separator;
17 
18 import java.awt.Dimension;
19 import java.io.File;
20 import java.security.AccessControlException;
21 import java.util.ArrayList;
22 import java.util.List;
23 import java.util.Locale;
24 import java.util.concurrent.CountDownLatch;
25 
26 import javax.swing.SwingUtilities;
27 import javax.swing.UIManager;
28 import javax.swing.UnsupportedLookAndFeelException;
29 
30 import org.apache.log4j.Logger;
31 
32 import games.stendhal.client.actions.MoveContinuousAction;
33 import games.stendhal.client.gui.StendhalFirstScreen;
34 import games.stendhal.client.gui.j2DClient;
35 import games.stendhal.client.gui.login.LoginDialog;
36 import games.stendhal.client.gui.login.Profile;
37 import games.stendhal.client.gui.styled.StyledLookAndFeel;
38 import games.stendhal.client.gui.styled.styles.StyleFactory;
39 import games.stendhal.client.gui.wt.core.WtWindowManager;
40 import games.stendhal.client.update.ClientGameConfiguration;
41 import games.stendhal.common.Debug;
42 import games.stendhal.common.MathHelper;
43 import games.stendhal.common.Version;
44 import marauroa.common.Log4J;
45 import marauroa.common.MarauroaUncaughtExceptionHandler;
46 
47 /**
48  * Main game class.
49  */
50 public final class stendhal {
51 
52 	private static final String LOG_FOLDER = "log/";
53 	private static final Logger logger = Logger.getLogger(stendhal.class);
54 
55 	// Use getGameFolder() where you need the real game data location
56 	private static final String STENDHAL_FOLDER;
57 	public static final String GAME_NAME;
58 	/**
59 	 * Directory for storing the persistent game data.
60 	 */
61 	private static String gameFolder;
62 	/**
63 	 * Just a try to get Webstart working without additional rights.
64 	 */
65 	static boolean WEB_START_SANDBOX = false;
66 
67 	// detect web start sandbox and init STENDHAL_FOLDER otherwise
68 	static {
69 		try {
70 			System.getProperty("user.home");
71 		} catch (final AccessControlException e) {
72 			WEB_START_SANDBOX = true;
73 		}
74 
75 		/** We set the main game folder to the game name */
76 		GAME_NAME = ClientGameConfiguration.get("GAME_NAME");
77 		STENDHAL_FOLDER = separator + GAME_NAME.toLowerCase(Locale.ENGLISH) + separator;
initGameFolder()78 		initGameFolder();
79 	}
80 
81 	public static final String VERSION = Version.getVersion();
82 
83 	/** Display sizes optimized for different screen resolutions */
84 	private static final List<Dimension> displaySizes = new ArrayList<Dimension>(3);
85 	public static final Integer SIZE_INDEX = 0;
86 	public static final Integer SIZE_INDEX_LARGE = 1;
87 	public static final Integer SIZE_INDEX_WIDE = 2;
88 	public static final Integer DISPLAY_SIZE_INDEX = SIZE_INDEX;
89 
90 	public static final boolean SHOW_COLLISION_DETECTION = false;
91 
92 	public static final boolean SHOW_EVERYONE_ATTACK_INFO = false;
93 
94 	public static final boolean FILTER_ATTACK_MESSAGES = true;
95 
96 	static final int FPS_LIMIT = 25;
97 	/** For keeping the login status. Blocks until logged in. */
98 	private static final CountDownLatch latch = new CountDownLatch(1);
99 
100 	/**
101 	 * Make the class non-instantiable.
102 	 */
stendhal()103 	private stendhal() {
104 	}
105 
106 	/**
107 	 * Initialize the client game directory.
108 	 * <p>
109 	 * NOTE: IF YOU CHANGE THIS, CHANGE ALSO CORRESPONDING CODE IN
110 	 * Bootstrap.java
111 	 */
initGameFolder()112 	private static void initGameFolder() {
113 		String defaultFolder = System.getProperty("user.home") + STENDHAL_FOLDER;
114 		/*
115 		 * Add any previously unrecognized unix like systems here. These will
116 		 * try to use ~/.config/stendhal if the user does not have saved data
117 		 * in ~/stendhal.
118 		 *
119 		 * OS X is counted in here too, but should it?
120 		 *
121 		 * List taken from:
122 		 * 	http://mindprod.com/jgloss/properties.html#OSNAME
123 		 */
124 		String unixLikes = "AIX|Digital Unix|FreeBSD|HP UX|Irix|Linux|Mac OS X|Solaris";
125 		String system = System.getProperty("os.name");
126 		if (system.matches(unixLikes)) {
127 			// Check first if the user has important data in the default folder.
128 			File f = new File(defaultFolder + "user.dat");
129 			if (!f.exists()) {
130 				gameFolder = System.getProperty("user.home") + separator
131 				+ ".config" + separator + STENDHAL_FOLDER;
132 				return;
133 			}
134 		}
135 		// Everyone else should use the default top level directory in $HOME
136 		gameFolder = defaultFolder;
137 	}
138 
139 	/**
140 	 * Notify that the login has been performed and the client should proceed
141 	 * to show the game window.
142 	 */
setDoLogin()143 	public static void setDoLogin()	{
144 		latch.countDown();
145 	}
146 
147 	/**
148 	 * Get the maximum size of the visible game area.
149 	 *
150 	 * @return screen dimensions
151 	 */
getDisplaySize()152 	public static Dimension getDisplaySize() {
153 		String spec = System.getProperty("display.index");
154 		int sizeIndex = MathHelper.parseIntDefault(spec, DISPLAY_SIZE_INDEX);
155 
156 		try {
157 			return displaySizes.get(sizeIndex);
158 		} catch (IndexOutOfBoundsException e) {
159 			logger.error("Invalid client size index: " + spec + " (" + sizeIndex + ")", e);
160 		}
161 
162 		return displaySizes.get(DISPLAY_SIZE_INDEX);
163 	}
164 
165 	/**
166 	 * Initialize list of dimensions that can be used for the clients
167 	 * display area.
168 	 */
initUsableDisplaySizes()169 	private static void initUsableDisplaySizes() {
170 		// Optimized display dimensions for display resolutions
171 		displaySizes.add(new Dimension(640, 480)); // Smaller 4:3 (1024x768 and smaller)
172 		displaySizes.add(new Dimension(800, 600)); // Larger 4:3 (1280x1024)
173 		displaySizes.add(new Dimension(864, 486)); // Larger 16:9 (1366x768 and larger)
174 	}
175 
176 	/**
177 	 * Starts the LogSystem.
178 	 */
startLogSystem()179 	private static void startLogSystem() {
180 		prepareLoggingSystemEnviroment();
181 
182 		logger.debug("XXXXXXX");
183 
184 		logger.info("-Setting base at :" + STENDHAL_FOLDER);
185 
186 		Log4J.init("data/conf/log4j.properties");
187 		logger.debug("XXXXXXX");
188 
189 		logger.info("Setting base at :" + STENDHAL_FOLDER);
190 		logger.info("Stendhal " + VERSION);
191 		logger.info(Debug.PRE_RELEASE_VERSION);
192 		logger.info("Logging to directory: " + getLogFolder());
193 
194 		String patchLevel = System.getProperty("sun.os.patch.level");
195 		if ((patchLevel == null) || (patchLevel.equals("unknown"))) {
196 			patchLevel = "";
197 		}
198 
199 		logger.info("OS: " + System.getProperty("os.name") + " " + patchLevel
200 				+ " " + System.getProperty("os.version") + " "
201 				+ System.getProperty("os.arch"));
202 		logger.info("Java-Runtime: " + System.getProperty("java.runtime.name")
203 				+ " " + System.getProperty("java.runtime.version") + " from "
204 				+ System.getProperty("java.home"));
205 		logger.info("Java-VM: " + System.getProperty("java.vm.vendor") + " "
206 				+ System.getProperty("java.vm.name") + " "
207 				+ System.getProperty("java.vm.version"));
208 		LogUncaughtExceptionHandler.setup();
209 	}
210 
211 	/**
212 	 * Initialize the logging system.
213 	 */
prepareLoggingSystemEnviroment()214 	private static void prepareLoggingSystemEnviroment() {
215 		// property configuration relies on this parameter
216 		System.setProperty("log.directory", getLogFolder());
217 		//create the log directory if not yet existing:
218 		//removed code as log4j is now capable of doing that automatically
219 	}
220 
221 	/**
222 	 * @return the name of the log folder
223 	 */
getLogFolder()224 	private static String getLogFolder() {
225 		return getGameFolder() + LOG_FOLDER;
226 	}
227 
228 	/**
229 	 * A loop which simply waits for the login to be completed.
230 	 */
waitForLogin()231 	private static void waitForLogin() {
232 		try {
233 			latch.await();
234 		} catch (final InterruptedException e) {
235 			logger.error("Unexpected interrupt", e);
236 			Thread.currentThread().interrupt();
237 		}
238 	}
239 
240 	/**
241 	 * Get the location of persistent game client data.
242 	 *
243 	 * @return game's home directory
244 	 */
getGameFolder()245 	public static String getGameFolder() {
246 		return gameFolder;
247 	}
248 
249 	/**
250 	 * Main Entry point.
251 	 *
252 	 * @param args
253 	 *            command line arguments
254 	 */
main(final String[] args)255 	public static void main(final String[] args) {
256 		startLogSystem();
257 		MarauroaUncaughtExceptionHandler.setup(false);
258 		initUsableDisplaySizes();
259 		new Startup(args);
260 	}
261 
262 	private static class Startup {
263 		StendhalFirstScreen splash;
264 
Startup(String[] args)265 		Startup(String[] args) {
266 			final UserContext userContext = new UserContext();
267 			final PerceptionDispatcher perceptionDispatch = new PerceptionDispatcher();
268 			final StendhalClient client = new StendhalClient(userContext, perceptionDispatch);
269 
270 			try {
271 				WtWindowManager wm = WtWindowManager.getInstance();
272 				String style = wm.getProperty("ui.style", "Wood (default)");
273 				StyledLookAndFeel look = new StyledLookAndFeel(StyleFactory.createStyle(style));
274 				UIManager.setLookAndFeel(look);
275 				/*
276 				 * Prevents the click event at closing a popup menu by clicking
277 				 * outside it being passed to the component underneath.
278 				 */
279 				UIManager.put("PopupMenu.consumeEventOnClose", Boolean.TRUE);
280 				int fontSize = wm.getPropertyInt("ui.font_size", 12);
281 				look.setDefaultFontSize(fontSize);
282 			} catch (UnsupportedLookAndFeelException e) {
283 				/*
284 				 * Should not happen as StyledLookAndFeel always returns true for
285 				 * isSupportedLookAndFeel()
286 				 */
287 				logger.error("Failed to set Look and Feel", e);
288 			}
289 
290 			UIManager.getLookAndFeelDefaults().put("ClassLoader", stendhal.class.getClassLoader());
291 
292 			final Profile profile = Profile.createFromCommandline(args);
293 
294 			SwingUtilities.invokeLater(new Runnable() {
295 				@Override
296 				public void run() {
297 					if (profile.isValid()) {
298 						new LoginDialog(null, client).connect(profile);
299 					} else {
300 						splash = new StendhalFirstScreen(client);
301 					}
302 				}
303 			});
304 
305 			waitForLogin();
306 			CStatusSender.send();
307 
308 			/*
309 			 * Pass the continuous movement setting is to the server.
310 			 * It is done in game loop to ensure that the server version is
311 			 * known before sending the command, to avoid sending invalid
312 			 * commands.
313 			 */
314 			GameLoop.get().runOnce(new Runnable() {
315 				@Override
316 				public void run() {
317 					boolean moveContinuous = WtWindowManager.getInstance().getPropertyBoolean(MOVE_CONTINUOUS, false);
318 					if (moveContinuous) {
319 						new MoveContinuousAction().sendAction(true, false);
320 					}
321 				}
322 			});
323 
324 			SwingUtilities.invokeLater(new Runnable() {
325 				@Override
326 				public void run() {
327 					j2DClient locclient = new j2DClient(client, userContext, splash);
328 					perceptionDispatch.register(locclient.getPerceptionListener());
329 					locclient.startGameLoop();
330 				}
331 			});
332 		}
333 	}
334 }
335