View Javadoc

1   /*
2    * AI Soccer Project - network gaming environment for AI warriors.
3    * Copyright (C) 2001-2004  Marcin Werla, Pawel Widera
4    *
5    * This program is free software; you can redistribute it and/or
6    * modify it under the terms of the GNU General Public License
7    * as published by the Free Software Foundation; either version 2
8    * of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, you can find it here:
17   * http://www.gnu.org/licenses/gpl.html
18   */
19  
20  package aigames.soccer.application;
21  
22  import aigames.soccer.Move;
23  
24  import org.dom4j.Document;
25  import org.dom4j.DocumentException;
26  import org.dom4j.DocumentHelper;
27  
28  import snifos.common.UserId;
29  
30  import snifos.server.Server;
31  
32  import java.util.Calendar;
33  import java.util.Collections;
34  import java.util.Date;
35  import java.util.GregorianCalendar;
36  import java.util.HashMap;
37  import java.util.Iterator;
38  import java.util.Map;
39  
40  
41  /***
42   * This is a helper class used to send messages to users. It maintains
43   * confirmation mechanism and unique message ids.
44   * @version $Id: MessageSender.java,v 1.19 2004/05/08 21:55:26 mwerla Exp $
45   */
46  public class MessageSender {
47      private static Document ACCEPTED_TEMPLATE;
48      private static Document REFUSED_TEMPLATE;
49      private static Document PARAMS_TEMPLATE;
50      private static Document GAME_OVER_TEMPLATE;
51      private static final int MESSAGE_TIMEOUT = 30000;
52  
53      static {
54          ACCEPTED_TEMPLATE = defineTemplate(
55                  "<message type=\"confirmation\" id=\"0\">" + "<accepted/>" +
56                  "</message>");
57  
58          REFUSED_TEMPLATE = defineTemplate(
59                  "<message type=\"confirmation\" id=\"0\">" + "<refused/>" +
60                  "</message>");
61  
62          PARAMS_TEMPLATE = defineTemplate("<message type=\"params\" id=\"0\">" +
63                  "<map width=\"10\" height=\"8\" goal=\"2\"/>" +
64                  "<attack side=\"-1\" begin=\"true\"/>" + "</message>");
65          GAME_OVER_TEMPLATE = defineTemplate(
66                  "<message type=\"game-over\" id=\"0\">" +
67                  "<name>Wymiatacz</name>" + "</message>");
68      }
69  
70      private Server server;
71      private int counter = 0;
72      private Map unconfirmedMessagesByIdMap = Collections.synchronizedMap(new HashMap());
73      private Object counterSemaphore = new Object();
74  
75      /***
76       * Constructo which sets server that will be used to send messages.
77       * @param server Server to set.
78       */
79      public MessageSender(Server server) {
80          this.server = server;
81      }
82  
83      /***
84      * Returns message id counter.
85       * @return Message id counter.
86       */
87      public int getCounter() {
88          synchronized (counterSemaphore) {
89              return counter;
90          }
91      }
92  
93      private static Document defineTemplate(String body) {
94          Document template = null;
95  
96          try {
97              template = DocumentHelper.parseText(body);
98          } catch (DocumentException e) {
99              //it should not be thrown
100         }
101 
102         return template;
103     }
104 
105     /***
106      * Sends game parameters to given user.
107      * @param userId User id.
108      * @param width Field width.
109      * @param height Field height.
110      * @param goal Goal height.
111      * @param side Attack side.
112      * @param begin True if user starts the game.
113      */
114     public void sendGameParameters(UserId userId, int width, int height,
115         int goal, int side, boolean begin) {
116         Document message = (Document) PARAMS_TEMPLATE.clone();
117 
118         message.selectSingleNode("//message/map/@width").setText(String.valueOf(
119                 width));
120         message.selectSingleNode("//message/map/@height").setText(String.valueOf(
121                 height));
122         message.selectSingleNode("//message/map/@goal").setText(String.valueOf(
123                 goal));
124         message.selectSingleNode("//message/attack/@side").setText(String.valueOf(
125                 side));
126         message.selectSingleNode("//message/attack/@begin").setText(String.valueOf(
127                 begin));
128 
129         sendWithConfirmation(userId, message);
130     }
131 
132     private synchronized int getNextMessageId() {
133         return counter++;
134     }
135 
136     private void recordMessage(UserId userId, int messageId, Document message) {
137         Message sentMessage = new Message(userId, message);
138         sentMessage.setSendDate(new Date());
139 
140         synchronized (unconfirmedMessagesByIdMap) {
141             unconfirmedMessagesByIdMap.put(new Integer(messageId), sentMessage);
142         }
143     }
144 
145     /***
146      * Sends refused message to given user.
147     * @param message Message that is refused.
148     * @param reason Refuesd reason.
149     */
150     public void sendRefused(Message message, String reason) {
151         Document confirmation = (Document) REFUSED_TEMPLATE.clone();
152         confirmation.selectSingleNode("//message/refused").setText(reason);
153         sendConfirmation(message, confirmation);
154     }
155 
156     private void sendConfirmation(Message message, Document confirmation) {
157         confirmation.selectSingleNode("//message/@id").setText(message.getMessageIdAsString());
158         server.sendMessage(message.getUserId(), confirmation);
159     }
160 
161     /***
162      * Sends accepted message.
163      * @param message Message that is accepted.
164      */
165     public void sendAccepted(Message message) {
166         Document confirmation = (Document) ACCEPTED_TEMPLATE.clone();
167         sendConfirmation(message, confirmation);
168     }
169 
170     /***
171      * Sends game over message.
172      * @param winner Name of the winner.
173      * @param userId User that should receive this message.
174      */
175     public void sendGameOver(String winner, UserId userId) {
176         Document gameOverMessage = (Document) GAME_OVER_TEMPLATE.clone();
177         gameOverMessage.selectSingleNode("//message/name").setText(winner);
178         sendWithConfirmation(userId, gameOverMessage);
179     }
180 
181     /***
182      * Checks message confirmation timeouts, and resend messages if needed.
183      */
184     public void checkConfirmationTimeouts() {
185         Calendar calendar = new GregorianCalendar();
186 
187         synchronized (unconfirmedMessagesByIdMap) {
188             for (Iterator iter = unconfirmedMessagesByIdMap.values().iterator();
189                     iter.hasNext();) {
190                 Message m = (Message) iter.next();
191                 calendar.setTime(m.getSendDate());
192                 calendar.add(Calendar.MILLISECOND, MESSAGE_TIMEOUT);
193 
194                 if (calendar.before(new Date())) {
195                     //resend message
196                     server.sendMessage(m.getUserId(), m.getMessage());
197                     m.setSendDate(new Date());
198                     m.incResendCounter();
199                 }
200             }
201         }
202     }
203 
204     /***
205      * Confirms message.
206      * @param confirmationMessage Message confirmation.
207      */
208     public void setMessageConfirmation(Message confirmationMessage) {
209         synchronized (unconfirmedMessagesByIdMap) {
210             int id = confirmationMessage.getMessageId();
211             Integer intId = new Integer(id);
212 
213             if (unconfirmedMessagesByIdMap.containsKey(intId)) {
214                 unconfirmedMessagesByIdMap.remove(intId);
215             }
216         }
217     }
218 
219     /***
220      * Forwards incoming message to other user.
221      * @param message Message that should be forwarded.
222      * @param userId User that shoud recieve the message.
223      */
224     public void forwardIncoming(Document message, UserId userId) {
225         Document messageToSend = (Document) message.clone();
226         sendWithConfirmation(userId, messageToSend);
227     }
228 
229     /***
230      * Sends message and recods it to await for its confirmation.
231      * @param userId User that shoud recieve the message.
232      * @param messageToSend Message that should be sent.
233      */
234     public void sendWithConfirmation(UserId userId, Document messageToSend) {
235         int id = getNextMessageId();
236         messageToSend.selectSingleNode("//message/@id").setText(String.valueOf(
237                 id));
238         server.sendMessage(userId, messageToSend);
239         recordMessage(userId, id, messageToSend);
240     }
241 
242     /***
243      * Checks if there are unconfirmed messages.
244      * @return True if there are unconfirmed messages, otherwise false.
245      */
246     public boolean areUnconfirmedMessages() {
247         return unconfirmedMessagesByIdMap.size() > 0;
248     }
249 
250     /***
251      * Clears all unconfirmed messages.
252      */
253     public void clearUnconfirmedMessages() {
254         unconfirmedMessagesByIdMap.clear();
255     }
256 
257     /***
258      * Sends game over message as log message.
259      * @param winner Winner name.
260      * @param id User that shoud recieve the message.
261      */
262     public void sendLogGameOver(String winner, UserId id) {
263         LogMessageBuilder lmb = new LogMessageBuilder();
264         lmb.addWinner(winner);
265         sendWithConfirmation(id, lmb.getMessage());
266     }
267 
268     /***
269      * Sends move message as log message.
270      * @param move Move to be sent.
271      * @param id User that shoud recieve the message.
272      * @param date Date at which move was made.
273      */
274     public void sendLogMove(Move move, UserId id, Date date) {
275         LogMessageBuilder lmb = new LogMessageBuilder();
276         lmb.addMove(move, date);
277         sendWithConfirmation(id, lmb.getMessage());
278     }
279 }