1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package net.sf.btw.cervii;
16
17 import java.io.DataInputStream;
18 import java.util.Random;
19 import java.util.Vector;
20
21 import javax.bluetooth.UUID;
22 import javax.microedition.lcdui.Alert;
23 import javax.microedition.lcdui.AlertType;
24 import javax.microedition.lcdui.Command;
25 import javax.microedition.lcdui.CommandListener;
26 import javax.microedition.lcdui.Display;
27 import javax.microedition.lcdui.Displayable;
28 import javax.microedition.midlet.MIDlet;
29 import javax.microedition.midlet.MIDletStateChangeException;
30
31 import net.sf.btw.btlib.Client;
32 import net.sf.btw.btlib.IClientListener;
33 import net.sf.btw.btlib.IServerListener;
34 import net.sf.btw.btlib.Peer;
35 import net.sf.btw.btlib.Server;
36 import net.sf.btw.btlib.ServerInfo;
37 import net.sf.btw.btlib.ui.ConnectorCanvas;
38 import net.sf.btw.btlib.ui.IConnectorListener;
39 import net.sf.btw.cervii.comm.Packet;
40 import net.sf.btw.cervii.game.GameController;
41 import net.sf.btw.cervii.menu.PlayerSetupCanvas;
42 import net.sf.btw.cervii.menu.SplashScreen;
43 import net.sf.btw.commons.Logger;
44
45 /***
46 * The main Midlet of the Cervii game. Handles canvas management and messaging
47 * management.
48 *
49 * @author Martin Vysny
50 */
51 public final class Cervii extends MIDlet implements IClientListener,
52 IServerListener, IConnectorListener, CommandListener {
53
54 /***
55 * The UUID of the game.
56 */
57 public static final UUID CERVII_UUID = new UUID(
58 "c2b6f262c8f411db88c00016d45fb918", false);
59
60 /***
61 * Constructor.
62 */
63 public Cervii() {
64 super();
65 instance = this;
66 }
67
68 /***
69 * The singleton instance.
70 */
71 private static Cervii instance = null;
72
73 /***
74 * Returns the instance of main class.
75 *
76 * @return instance of Cervii, never <code>null</code>.
77 */
78 public static Cervii getInstance() {
79 return instance;
80 }
81
82
83
84
85
86
87 protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
88
89 }
90
91 protected void pauseApp() {
92
93 }
94
95 /***
96 * Player setup screen, valid always.
97 */
98 private PlayerSetupCanvas playerSetupCanvas;
99
100 /***
101 * Connector canvas, valid always.
102 */
103 private ConnectorCanvas connectorCanvas;
104
105 /***
106 * Game controller, valid only while playing.
107 */
108 private GameController gameController;
109
110 /***
111 * The communicating peer. Valid after the connector form closes.
112 */
113 private Peer peer;
114
115 /***
116 * The X screen size, valid always.
117 */
118 private int screenxsize;
119
120 /***
121 * The Y screen size, always valid.
122 */
123 private int screenysize;
124
125
126
127
128
129
130 protected void startApp() throws MIDletStateChangeException {
131
132 try {
133 Peer.initializeBluetooth();
134 } catch (Exception e) {
135 Logger.error("Failed to initialize bluetooth", e);
136 final Alert error = new Alert(I18n.getMessage("ERROR"),
137 I18n.getMessage("BT_ERROR"),
138 null, AlertType.ERROR);
139 error.setCommandListener(this);
140 error.setTimeout(Alert.FOREVER);
141 Display.getDisplay(this).setCurrent(error);
142 return;
143 }
144
145 final SplashScreen splash=new SplashScreen();
146
147 connectorCanvas = new ConnectorCanvas(CERVII_UUID, Display
148 .getDisplay(this), this, I18n.getMessage("START_SERVER"), I18n
149 .getMessage("CONNECT_TO"));
150 splash.setNextDisplayable(connectorCanvas);
151 connectorCanvas.setFullScreenMode(true);
152 screenxsize = connectorCanvas.getWidth();
153 screenysize = connectorCanvas.getHeight();
154 connectorCanvas.setFullScreenMode(false);
155 }
156
157 /***
158 * Private method, not intended to be called by clients.
159 *
160 * @see net.sf.btw.btlib.IClientListener#connected(byte)
161 */
162 public void connected(byte peerId) {
163 Logger.info("Cervii.connected(" + peerId + ")", null);
164 playerSetupCanvas.setThisPlayerId(peerId, screenxsize, screenysize);
165 final Packet initPacket = new Packet(Packet.PACKET_INIT);
166 initPacket.resolutionX = screenxsize;
167 initPacket.resolutionY = screenysize;
168 ((Client) peer).send(initPacket.toByteArray());
169 }
170
171 /***
172 * Private method, not intended to be called by clients.
173 *
174 * @see net.sf.btw.btlib.IClientListener#disconnected()
175 */
176 public void disconnected() {
177 Logger.info("Cervii.disconnected()", null);
178 handleCriticalError(I18n.getMessage("DISCONNECTED"), null);
179 }
180
181 /***
182 * Private method, not intended to be called by clients.
183 *
184 * @see net.sf.btw.btlib.IClientListener#messageArrived(byte[], int,
185 * java.io.DataInputStream)
186 */
187 public void messageArrived(byte[] message, int messageLength,
188 DataInputStream stream) {
189 final Packet packet;
190 try {
191 packet = Packet.fromStream(stream);
192 } catch (Exception ex) {
193 clientCriticalError(ex);
194 return;
195 }
196 if ((packet.packetType == Packet.PACKET_PLAYER_JOINED)
197 || (packet.packetType == Packet.PACKET_PLAYER_LEFT)) {
198 playerSetupCanvas.clientOnPacket(packet);
199 } else if (packet.packetType == Packet.PACKET_PING) {
200 ((Client) peer).send(new Packet(Packet.PACKET_PONG).toByteArray());
201 } else if (packet.packetType == Packet.PACKET_GAMEINIT) {
202
203 final byte thisPlayerId = playerSetupCanvas.getThisPlayerId();
204 final Vector players = playerSetupCanvas.getPlayers();
205 gameController = new GameController(peer, packet, players,
206 thisPlayerId);
207 return;
208 } else if (packet.packetType == Packet.PACKET_SYNC) {
209 if (gameController != null)
210 gameController.clientOnPacket(packet);
211 return;
212 } else
213 throw new IllegalArgumentException("Invalid packet: "
214 + packet.packetType);
215 }
216
217 /***
218 * Not intended to be called by clients.
219 *
220 * @see net.sf.btw.btlib.IErrorListener#errorOccured(byte, int, boolean,
221 * java.lang.Exception)
222 */
223 public void errorOccured(byte clientID, int errorHint,
224 boolean listenerError, Exception ex) {
225 if (peer instanceof Client) {
226 clientCriticalError(ex);
227 }
228 }
229
230 /***
231 * A critical error occured. The client will disconnect and return to
232 * connector form.
233 *
234 * @param ex
235 * the exception.
236 */
237 private void clientCriticalError(Exception ex) {
238 ((Client) peer).disconnect();
239 handleCriticalError(I18n.getMessage("COMM_ERR"), ex);
240 }
241
242 /***
243 * Private method not intended to be called by clients.
244 * @see net.sf.btw.btlib.IServerListener#clientConnected(byte, java.lang.String)
245 */
246 public void clientConnected(byte id, String name) {
247 playerSetupCanvas.serverOnClientConnect(id, name, true);
248 }
249
250 /***
251 * Private method not intended to be called by clients.
252 * @see net.sf.btw.btlib.IServerListener#clientDisconnected(byte, java.lang.String)
253 */
254 public void clientDisconnected(byte id, String name) {
255 playerSetupCanvas.serverOnClientConnect(id, name, false);
256 }
257
258 /***
259 * Private method not intended to be called by clients.
260 * @see net.sf.btw.btlib.IServerListener#messageArrived(byte, java.lang.String, byte[], int, java.io.DataInputStream)
261 */
262 public void messageArrived(byte clientID, String name, byte[] message,
263 int messageLength, DataInputStream stream) {
264 try {
265 final Packet packet = Packet.fromStream(stream);
266 if ((packet.packetType == Packet.PACKET_INIT)
267 || (packet.packetType == Packet.PACKET_PONG)) {
268 playerSetupCanvas.serverOnPacket(clientID, packet);
269 return;
270 } else if (packet.packetType == Packet.PACKET_TURN_REQUEST) {
271 gameController.serverOnPacket(packet, clientID);
272 return;
273 } else
274 throw new IllegalArgumentException(
275 "Cervii.messageArrived(): Invalid packet type "
276 + packet.packetType);
277 } catch (Exception ex) {
278 Logger.error("Error deserializing packet", ex);
279 ((Server) peer).disconnectClient(clientID);
280 }
281 }
282
283 /***
284 * Private method not intended to be called by clients.
285 */
286 public void closeConnector() {
287 notifyDestroyed();
288 }
289
290 /***
291 * Private method, not intended to be called by clients.
292 *
293 * @see net.sf.btw.ui.IConnectorListener#connectToServer(net.sf.btw.btlib.ServerInfo)
294 */
295 public void connectToServer(ServerInfo info) {
296 peer = new Client(CERVII_UUID, Packet.MAX_PACKET_SIZE,
297 Packet.MAX_PACKET_SIZE, this);
298 playerSetupCanvas = new PlayerSetupCanvas(peer);
299 Display.getDisplay(this).setCurrent(playerSetupCanvas);
300 ((Client) peer).connect(info);
301 }
302
303 /***
304 * Private method, not intended to be called by clients.
305 *
306 * @see net.sf.btw.ui.IConnectorListener#startServer()
307 */
308 public void startServer() {
309 peer = new Server(Packet.MAX_PACKET_SIZE, Packet.MAX_PACKET_SIZE,
310 CERVII_UUID, this);
311 playerSetupCanvas = new PlayerSetupCanvas(peer);
312 playerSetupCanvas.setThisPlayerId(Peer.SERVER_ID, screenxsize,
313 screenysize);
314 Display.getDisplay(this).setCurrent(playerSetupCanvas);
315 try {
316 ((Server) peer).startServer();
317 } catch (Exception ex) {
318 handleCriticalError(I18n.getMessage("ERR_CREATING_SERVER"), ex);
319 }
320 }
321
322 /***
323 * Returns the display instance.
324 *
325 * @return the display instance, never <code>null</code>.
326 */
327 public static Display getDisplay() {
328 return Display.getDisplay(getInstance());
329 }
330
331 /***
332 * Shows a big error dialog, logs the error and bails out back to the
333 * connector canvas.
334 *
335 * @param message
336 * the message to show
337 * @param ex
338 * error.
339 */
340 public void handleCriticalError(final String message, final Exception ex) {
341 Logger.error(message, ex);
342 String msg = message;
343 if (ex != null) {
344 msg += ": " + ex.getClass().getName() + ":" + ex.getMessage();
345 }
346 getDisplay().setCurrent(new Alert(I18n.getMessage("ERROR"), msg,
347 null, AlertType.ERROR), connectorCanvas);
348 if (gameController != null) {
349 gameController.cancel();
350 gameController = null;
351 }
352 if (playerSetupCanvas != null) {
353 playerSetupCanvas.cancelPingTimers();
354 playerSetupCanvas = null;
355 }
356 peer = null;
357 }
358
359 /***
360 * Private method, not intended to be called by clients.
361 *
362 * @see javax.microedition.lcdui.CommandListener#commandAction(javax.microedition.lcdui.Command,
363 * javax.microedition.lcdui.Displayable)
364 */
365 public void commandAction(Command c, Displayable d) {
366 if (c == Alert.DISMISS_COMMAND) {
367 notifyDestroyed();
368 }
369 }
370
371 /***
372 * Private method, not intended to be called by clients.
373 * Called by {@link #playerSetupCanvas} when the server chose to start the
374 * game.
375 */
376 public void gameStart() {
377
378 final Packet packet = playerSetupCanvas.getGameInitPacket();
379 final byte thisPlayerId = playerSetupCanvas.getThisPlayerId();
380 final Vector players = playerSetupCanvas.getPlayers();
381 ((Server) peer).sendBuffer(((Server) peer).getClientIDs().keys(),
382 (byte) -1, packet.toByteArray());
383
384 gameController = new GameController(peer, packet, players, thisPlayerId);
385 }
386
387 /***
388 * Not a public method.
389 * Called by {@link #playerSetupCanvas} when back button is pressed.
390 */
391 public void showConnectorCanvas() {
392 getDisplay().setCurrent(connectorCanvas);
393 playerSetupCanvas = null;
394 peer = null;
395 }
396
397 /***
398 * <p>
399 * Do not call.
400 * </p>
401 * <p>
402 * Called by {@link GameController} when the game finishes.
403 * </p>
404 */
405 public void gameOver() {
406 gameController = null;
407 getDisplay().setCurrent(playerSetupCanvas);
408 }
409
410 /***
411 * Returns next random int in the range 0 (including) .. maxValue
412 * (excluding).
413 *
414 * @param random
415 * the random instance.
416 * @param maxValue
417 * max. value. returned value is always less than this value.
418 * @return the value.
419 */
420 public static int nextRandomInt(final Random random, final int maxValue) {
421 int result = random.nextInt();
422 if (result < 0)
423 result = -result;
424 if (result < 0)
425 result = Integer.MAX_VALUE;
426 return result % maxValue;
427 }
428
429 }