1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package aigames.soccer.application;
21
22 import aigames.soccer.InvalidMoveException;
23
24 import aigames.soccer.field.BasicGameField;
25 import aigames.soccer.field.GameField;
26 import aigames.soccer.field.constructor.Constructor;
27 import aigames.soccer.field.constructor.StandardConstructor;
28
29 import org.apache.log4j.Logger;
30
31 import org.dom4j.Document;
32
33 import org.iso_relax.verifier.VerifierConfigurationException;
34
35 import org.xml.sax.SAXException;
36
37 import snifos.application.Application;
38
39 import snifos.common.UserId;
40
41 import snifos.exception.ConfigurationException;
42 import snifos.exception.InvalidUserException;
43
44 import snifos.server.Server;
45
46 import java.io.IOException;
47
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.Collections;
51 import java.util.Date;
52 import java.util.GregorianCalendar;
53 import java.util.HashMap;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Properties;
58
59
60 /***
61 * @version $Id: SoccerApp.java,v 1.36 2004/05/08 21:55:26 mwerla Exp $
62 */
63 public class SoccerApp implements Application {
64 private static Logger logger = Logger.getLogger(SoccerApp.class);
65 private static final int MAX_NO_OF_BAD_MESSAGES = 3;
66 private static final int MAX_NO_OF_OBSERVERS = 2;
67 private static final int MESSAGE_TIMEOUT = 30000;
68 private static final int REQUIRED_NUMBER_OF_PLAYERS = 2;
69 private Server server;
70 private int width;
71 private int height;
72 private int goal;
73 private Validator validator;
74 private MessageSender sender;
75 private GameField field;
76 private String winnerName = "";
77 private int gameOverStatus;
78 private int turn;
79 private Collection messageBuffer = Collections.synchronizedCollection(new ArrayList());
80 private int actualNumberOfPlayers = 0;
81 private Collection allowedNames = new ArrayList();
82 private List registeredUsers = Collections.synchronizedList(new ArrayList());
83 private Map userInfos = new HashMap();
84 private boolean begin;
85 private int direction;
86 private int playerHelloCounter;
87 private Object turnSempahore = new Object();
88 private Object registerSempahore = new Object();
89 private LogMessageBuilder logMessageBuilder = new LogMessageBuilder();
90 private boolean unique;
91 private boolean canRegister = true;
92 private int noOfObservers = 0;
93
94 /***
95 * @see snifos.application.Application#configure(Properties, boolean)
96 */
97 public void configure(final Properties configuration, boolean isUnique)
98 throws ConfigurationException {
99 unique = isUnique;
100 width = Integer.parseInt(configuration.getProperty("width"));
101 height = Integer.parseInt(configuration.getProperty("height"));
102 goal = Integer.parseInt(configuration.getProperty("goal"));
103
104 Constructor fieldConstructor = new StandardConstructor(width, height,
105 goal);
106 field = new BasicGameField(fieldConstructor);
107
108 for (int i = 1; i <= REQUIRED_NUMBER_OF_PLAYERS; i++) {
109 String playerName = configuration.getProperty("player" + i);
110
111 if (playerName != null) {
112 allowedNames.add(playerName);
113 }
114 }
115
116 begin = true;
117 direction = 1;
118
119 try {
120 validator = new Validator(configuration.getProperty("xsd"));
121 } catch (VerifierConfigurationException e) {
122 throw new ConfigurationException(e);
123 } catch (SAXException e) {
124 throw new ConfigurationException(e);
125 } catch (IOException e) {
126 throw new ConfigurationException(e);
127 }
128 }
129
130 /***
131 * @see snifos.application.Application#setServer(snifos.server.Server)
132 */
133 public void setServer(final Server serverToSet) {
134 this.server = serverToSet;
135 sender = new MessageSender(serverToSet);
136 }
137
138 /***
139 * @see snifos.application.Application#unregisterUser(snifos.common.UserId)
140 */
141 public void unregisterUser(final UserId userId) throws InvalidUserException {
142 synchronized (registeredUsers) {
143 if (!registeredUsers.remove(userId)) {
144 throw new InvalidUserException();
145 }
146
147 UserInfo userInfo = (UserInfo) userInfos.remove(userId);
148
149 if (userInfo.getType() == UserInfo.TYPE_OBSERVER) {
150 noOfObservers--;
151 } else {
152 playerHelloCounter--;
153 actualNumberOfPlayers--;
154 }
155
156 if (registeredUsers.size() == 0) {
157 sender.clearUnconfirmedMessages();
158 }
159 }
160 }
161
162 /***
163 * @see snifos.application.Application#receiveMessage(snifos.common.UserId, org.dom4j.Document)
164 */
165 public synchronized void receiveMessage(final UserId userId,
166 final Document messageDoc) throws InvalidUserException {
167 if (registeredUsers.contains(userId)) {
168 Message message = new Message(userId, messageDoc);
169
170 if (message.validate(validator)) {
171 if (message.getMessageType() != Message.CONFIRMATION) {
172 sender.sendAccepted(message);
173 }
174
175 synchronized (messageBuffer) {
176 messageBuffer.add(message);
177 }
178
179 synchronized (this) {
180 notify();
181 }
182 } else {
183 sender.sendRefused(message, message.getLastValidationReason());
184 logMessageBuilder.addError(((UserInfo) userInfos.get(userId)).getName(),
185 message.getLastValidationReason());
186 updateInvalidMessagesCount(userId);
187 }
188 } else {
189 throw new InvalidUserException();
190 }
191 }
192
193 private void updateInvalidMessagesCount(final UserId userId) {
194 UserInfo info = (UserInfo) userInfos.get(userId);
195 int newNoOfBadMessages = info.getNoOfBadMessages() + 1;
196
197 if (newNoOfBadMessages > MAX_NO_OF_BAD_MESSAGES) {
198 try {
199 unregisterUser(userId);
200 } catch (InvalidUserException e) {
201
202 }
203
204 server.disconnectUser(userId);
205 } else {
206 info.setNoOfBadMessages(newNoOfBadMessages);
207 }
208 }
209
210 /***
211 * @see java.lang.Runnable#run()
212 */
213 public void run() {
214 do {
215 try {
216 synchronized (this) {
217 synchronized (registerSempahore) {
218 canRegister = true;
219 }
220
221 wait(MESSAGE_TIMEOUT);
222 }
223 } catch (InterruptedException e) {
224 logger.error(e.getMessage(), e);
225 }
226
227 sender.checkConfirmationTimeouts();
228
229 boolean areNewMessages = false;
230
231 do {
232 areNewMessages = false;
233
234 Message message = null;
235
236 synchronized (messageBuffer) {
237 Iterator iterator = messageBuffer.iterator();
238
239 if (iterator.hasNext()) {
240 message = (Message) iterator.next();
241 areNewMessages = true;
242 }
243 }
244
245 if (areNewMessages) {
246 serviceMessage(message);
247 messageBuffer.remove(message);
248 }
249 } while (areNewMessages);
250
251 checkGameOver();
252
253 synchronized (registerSempahore) {
254 canRegister = false;
255 }
256 } while ((registeredUsers.size() > 0) || unique);
257 }
258
259 private void checkGameOver() {
260 if (!sender.areUnconfirmedMessages() && (gameOverStatus > 0)) {
261 if (gameOverStatus == 1) {
262 sendGameOver(winnerName);
263 gameOverStatus = 2;
264 } else if (gameOverStatus == 2) {
265 disconnectAllUsers();
266 gameOverStatus = 0;
267 }
268 }
269 }
270
271 private void disconnectAllUsers() {
272 synchronized (registeredUsers) {
273 while (registeredUsers.size() > 0) {
274 Iterator iter = registeredUsers.iterator();
275 UserId id = (UserId) iter.next();
276
277 try {
278 unregisterUser(id);
279 } catch (InvalidUserException e) {
280
281 }
282
283 server.disconnectUser(id);
284 }
285
286 sender.clearUnconfirmedMessages();
287 }
288 }
289
290 private void serviceMessage(Message message) {
291 UserInfo userInfo = (UserInfo) userInfos.get(message.getUserId());
292
293 switch (message.getMessageType()) {
294 case Message.MOVE:
295 serviceMove(message);
296
297 break;
298
299 case Message.HELLO:
300 serviceHello(userInfo);
301
302 break;
303
304 case Message.CONFIRMATION:
305 sender.setMessageConfirmation(message);
306
307 break;
308
309 default:
310 break;
311 }
312
313 if (message.getMessageType() != Message.CONFIRMATION) {
314 userInfo.setLastMessageDate(new GregorianCalendar());
315 }
316 }
317
318 private UserId getOtherPlayer(UserId thisPlayer) {
319 synchronized (registeredUsers) {
320 for (Iterator iter = registeredUsers.iterator(); iter.hasNext();) {
321 UserId id = (UserId) iter.next();
322 UserInfo userInfo = (UserInfo) userInfos.get(id);
323
324 if ((userInfo.getType() == UserInfo.TYPE_PLAYER) &&
325 (!userInfo.getUserId().equals(thisPlayer))) {
326 return userInfo.getUserId();
327 }
328 }
329 }
330
331 return null;
332 }
333
334 private void serviceMove(Message message) {
335 UserId winner = null;
336 Date moveDate = new Date();
337
338 try {
339 field.makeMove(message.getMove(), getTurn());
340 } catch (InvalidMoveException e) {
341 winner = getOtherPlayer(message.getUserId());
342 }
343
344 if ((winner == null) && (field.isGameOver())) {
345 int goal = field.isGoal();
346
347 switch (goal) {
348 case -1:
349 winner = getPlayer(2);
350
351 break;
352
353 case 1:
354 winner = getPlayer(1);
355
356 break;
357
358 default:
359 winner = message.getUserId();
360
361 break;
362 }
363 }
364
365 if (winner != null) {
366 UserInfo winnerInfo = (UserInfo) userInfos.get(winner);
367 logger.info("Game over! And the winner is: " +
368 winnerInfo.getName() + " " + winnerInfo.getUserId());
369 winnerName = winnerInfo.getName();
370 forwardMove(message, true, moveDate);
371 logMessageBuilder.addWinner(winnerName);
372 gameOverStatus = 1;
373 } else {
374 logMessageBuilder.addMove(message.getMove(), moveDate);
375 forwardMove(message, false, moveDate);
376 }
377 }
378
379 private UserId getPlayer(int pos) {
380 synchronized (registeredUsers) {
381 int counter = 0;
382
383 for (Iterator iter = registeredUsers.iterator(); iter.hasNext();) {
384 UserId id = (UserId) iter.next();
385 UserInfo userInfo = (UserInfo) userInfos.get(id);
386
387 if (userInfo.getType() == UserInfo.TYPE_PLAYER) {
388 counter++;
389 }
390
391 if (counter == pos) {
392 return id;
393 }
394 }
395 }
396
397 return null;
398 }
399
400 private void forwardMove(Message message, boolean observerOnly,
401 Date moveDate) {
402 synchronized (registeredUsers) {
403 for (Iterator iter = registeredUsers.iterator(); iter.hasNext();) {
404 UserId id = (UserId) iter.next();
405 UserInfo loopUserInfo = (UserInfo) userInfos.get(id);
406
407 switch (loopUserInfo.getType()) {
408 case UserInfo.TYPE_PLAYER:
409
410 if (!((observerOnly) || (message.getUserId().equals(id)))) {
411 sender.forwardIncoming(message.getMessage(), id);
412 }
413
414 break;
415
416 case UserInfo.TYPE_OBSERVER:
417 sender.sendLogMove(message.getMove(), id, moveDate);
418
419 break;
420
421 default:
422 break;
423 }
424 }
425 }
426 }
427
428 private void sendGameOver(String winner) {
429 synchronized (registeredUsers) {
430 for (Iterator iter = registeredUsers.iterator(); iter.hasNext();) {
431 UserId id = (UserId) iter.next();
432 UserInfo loopUserInfo = (UserInfo) userInfos.get(id);
433
434 switch (loopUserInfo.getType()) {
435 case UserInfo.TYPE_PLAYER:
436 sender.sendGameOver(winner, id);
437
438 break;
439
440 case UserInfo.TYPE_OBSERVER:
441 sender.sendLogGameOver(winner, id);
442
443 break;
444
445 default:
446 break;
447 }
448 }
449 }
450 }
451
452 private int getTurn() {
453 synchronized (turnSempahore) {
454 int oldTurn = turn;
455 turn = ((turn + 1) % REQUIRED_NUMBER_OF_PLAYERS) + 1;
456
457 return oldTurn;
458 }
459 }
460
461 private void serviceHello(UserInfo userInfo) {
462 switch (userInfo.getType()) {
463 case UserInfo.TYPE_PLAYER:
464 playerHelloCounter++;
465
466 if (playerHelloCounter == REQUIRED_NUMBER_OF_PLAYERS) {
467 logMessageBuilder = new LogMessageBuilder();
468 sendInitialGameInfo(true);
469 logMessageBuilder.addMap(width, height, goal);
470 logMessageBuilder.addBegin(((UserInfo) userInfos.get(getPlayer(
471 1))).getName());
472 sendInitialGameInfo(false);
473 setTurn(1);
474 gameOverStatus = 0;
475 field.startGame();
476 }
477
478 break;
479
480 case UserInfo.TYPE_OBSERVER:
481
482 if (playerHelloCounter == REQUIRED_NUMBER_OF_PLAYERS) {
483 sender.sendWithConfirmation(userInfo.getUserId(),
484 logMessageBuilder.getMessage());
485 }
486
487 break;
488
489 default:
490 break;
491 }
492 }
493
494 private void setTurn(int i) {
495 synchronized (turnSempahore) {
496 turn = i;
497 }
498 }
499
500 private void sendInitialGameInfo(boolean toPlayers) {
501 synchronized (registeredUsers) {
502 for (Iterator iter = registeredUsers.iterator(); iter.hasNext();) {
503 UserInfo loopUserInfo = (UserInfo) userInfos.get(iter.next());
504
505 switch (loopUserInfo.getType()) {
506 case UserInfo.TYPE_PLAYER:
507
508 if (toPlayers) {
509 logMessageBuilder.addName(loopUserInfo.getName());
510 sender.sendGameParameters(loopUserInfo.getUserId(),
511 width, height, goal, direction, begin);
512 direction = -1 * direction;
513 begin = !begin;
514 }
515
516 break;
517
518 case UserInfo.TYPE_OBSERVER:
519
520 if (!toPlayers) {
521 sender.sendWithConfirmation(loopUserInfo.getUserId(),
522 logMessageBuilder.getMessage());
523 }
524
525 break;
526
527 default:
528 break;
529 }
530 }
531 }
532 }
533
534 /***
535 * @see snifos.application.Application#registerUser(UserId, Document)
536 */
537 public synchronized void registerUser(UserId userId, Document message)
538 throws InvalidUserException {
539 UserInfo userInfo = new UserInfo(userId, message);
540
541 synchronized (registerSempahore) {
542 if (canRegister) {
543 if ((userInfo.getType() == UserInfo.TYPE_OBSERVER) &&
544 (noOfObservers < MAX_NO_OF_OBSERVERS)) {
545 noOfObservers++;
546 registeredUsers.add(userId);
547 userInfos.put(userId, userInfo);
548 } else if ((userInfo.getType() == UserInfo.TYPE_PLAYER) &&
549 (actualNumberOfPlayers < REQUIRED_NUMBER_OF_PLAYERS)) {
550 if (((allowedNames.size() > 0) &&
551 (allowedNames.contains(userInfo.getName()))) ||
552 (allowedNames.size() == 0)) {
553 registeredUsers.add(userId);
554 userInfos.put(userId, userInfo);
555 actualNumberOfPlayers++;
556 } else {
557 throw new InvalidUserException();
558 }
559 } else {
560 throw new InvalidUserException();
561 }
562 } else {
563 throw new InvalidUserException();
564 }
565 }
566 }
567 }