View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.server.impl;
2   
3   import java.util.Collection;
4   import java.util.List;
5   import java.util.logging.Level;
6   import java.util.logging.Logger;
7   
8   import com.google.inject.Inject;
9   
10  import cz.cuni.amis.pogamut.base.agent.IAgentId;
11  import cz.cuni.amis.pogamut.base.agent.state.impl.AgentStateStarting;
12  import cz.cuni.amis.pogamut.base.communication.command.IAct;
13  import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnection;
14  import cz.cuni.amis.pogamut.base.communication.connection.impl.socket.SocketConnectionAddress;
15  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
16  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
17  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
18  import cz.cuni.amis.pogamut.base.server.AbstractWorldServer;
19  import cz.cuni.amis.pogamut.base.utils.collections.adapters.WVObjectsSetAdapter;
20  import cz.cuni.amis.pogamut.base.utils.collections.adapters.WVVisibleObjectsSetAdapter;
21  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
22  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
23  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004Bot;
24  import cz.cuni.amis.pogamut.ut2004.bot.impl.NativeUT2004BotAdapter;
25  import cz.cuni.amis.pogamut.ut2004.bot.jmx.BotJMXProxy;
26  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.AddBot;
27  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.GetMaps;
28  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.PasswordReply;
29  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Ready;
30  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo;
31  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.MapList;
32  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Mutator;
33  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Password;
34  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
35  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.MapPointListObtained;
36  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.PlayerListObtained;
37  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.ReadyCommandRequest;
38  import cz.cuni.amis.pogamut.ut2004.communication.worldview.map.UT2004Map;
39  import cz.cuni.amis.pogamut.ut2004.server.IUT2004Server;
40  import cz.cuni.amis.utils.collections.ObservableCollection;
41  import cz.cuni.amis.utils.collections.TranslatedObservableCollection;
42  import cz.cuni.amis.utils.flag.Flag;
43  
44  /**
45   * Abstract class - ancestor of all UT2004 server controls.
46   * <p>
47   * <p>
48   * It counts with GameBots2004 protocol therefore taking care of:
49   * <ol>
50   * <li>ReadyCommandRequest - sending automatically ready(), override
51   * readyCommandRequested() if you're not comfortable with this</li>
52   * <li>Password - when password is requested it calls method
53   * createPasswordReply()</li>
54   * </ol>
55   * <p>
56   * <p>
57   * Also introducing user-method for setting up custom worldview listeners that
58   * is called before Ready message is sent - prePrepareServer().
59   * <p>
60   * <p>
61   * You may use setPassword() method to specify the password before starting the
62   * agent.
63   * 
64   * @author Jimmy
65   */
66  @AgentScoped
67  public abstract class AbstractUT2004Server<WORLD_VIEW extends IWorldView, ACT extends IAct> extends AbstractWorldServer<WORLD_VIEW, ACT, IUT2004Bot> implements IUT2004Server {
68  
69  	ObservableCollection<Player> players = null;
70  	
71  	List<Mutator> mutators = null;
72  	
73  	Flag<Double> gameSpeed = new Flag<Double>();
74  	
75  	List<MapList> maps = null;
76  	
77  	Flag<String> mapName = new Flag<String>();
78  	
79  	private UT2004Map map;
80  	
81  	ObservableCollection<? extends NativeUT2004BotAdapter> nativeAgents = null;
82  	
83  	/**
84  	 * Collection of all connected Pogamut bots.
85  	 */
86  	ObservableCollection<IUT2004Bot> agents = null;
87  	
88  	/**
89  	 * If specified - used for the construction of the PasswordReply in
90  	 * createPasswordReply() method.
91  	 */
92  	private String desiredPassword = null;
93  	
94  	private IWorldEventListener<PlayerListObtained> playerListObtainedListener = new IWorldEventListener<PlayerListObtained>() {
95  
96  		@Override
97  		public void notify(PlayerListObtained event) {
98  			players.addAll(event.getPlayers());
99  			// players list is received as the last in initial communication
100 		}
101 	};
102 
103 	private IWorldEventListener<MapPointListObtained> mapPointListObtainedListener = new IWorldEventListener<MapPointListObtained>() {
104 
105 		@Override
106 		public void notify(MapPointListObtained event) {
107 			// TODO process the navpoints
108 			// ask for maps on the server
109 			getAct().act(new GetMaps());
110 		}
111 	};
112 	private SocketConnection connection;
113 
114 	@Inject
115 	public AbstractUT2004Server(IAgentId agentId, IAgentLogger agentLogger,
116 			IComponentBus bus, SocketConnection connection,
117 			WORLD_VIEW worldView, ACT act) {
118 		super(agentId, agentLogger, bus, worldView, act);
119 
120 		this.connection = connection;
121 
122 		getWorldView().addEventListener(ReadyCommandRequest.class, readyCommandRequestListener);
123 		getWorldView().addEventListener(Password.class, passwordRequestedListener);
124 
125 		// listen for initial players list
126 		getWorldView().addEventListener(PlayerListObtained.class, playerListObtainedListener);
127 
128 		getWorldView().addEventListener(MapPointListObtained.class, mapPointListObtainedListener);
129 		
130 		players = new WVVisibleObjectsSetAdapter<Player>(Player.class, getWorldView());
131 	}
132 
133 	public void setAddress(String host, int port) { 
134 		if (log.isLoggable(Level.WARNING)) log.warning("Setting address to: " + host + ":" + port);
135 		this.connection.setAddress(new SocketConnectionAddress(host, port));
136 	}
137 
138 	/**
139 	 * Specify the password that should be used if required by the world.
140 	 * 
141 	 * @param password
142 	 */
143 	public void setPassword(String password) {
144 		this.desiredPassword = password;
145 	}
146 
147 	// --------------
148 	// -=-=-=-=-=-=-=
149 	// READY LISTENER
150 	// -=-=-=-=-=-=-=
151 	// --------------
152 	/**
153 	 * This method is called whenever HelloBot message is parsed - the
154 	 * GameBots2004 is awaiting the bot to reply with Ready command to begin the
155 	 * handshake.
156 	 */
157 	protected void readyCommandRequested() {
158 		getAct().act(new Ready());
159 	}
160 
161 	/**
162 	 * Listener that is hooked to WorldView awaiting event ReadyCommandRequest
163 	 * calling setupWorldViewListeners() and then readyCommandRequested() method
164 	 * upon receiving the event.
165 	 */
166 	private IWorldEventListener<ReadyCommandRequest> readyCommandRequestListener = new IWorldEventListener<ReadyCommandRequest>() {
167 
168 		@Override
169 		public void notify(ReadyCommandRequest event) {
170 			setState(new AgentStateStarting("GameBots2004 greeted us, sending READY."));
171 			readyCommandRequested();
172 			setState(new AgentStateStarting("READY sent."));
173 		}
174 	};
175 	// -----------------
176 	// -=-=-=-=-=-=-=-=-
177 	// PASSWORD LISTENER
178 	// -=-=-=-=-=-=-=-=-
179 	// -----------------
180 	/**
181 	 * Instance of the password reply command that was sent upon receivieng
182 	 * request for the password (the world is locked).
183 	 * <p>
184 	 * <p>
185 	 * If null the password was not required by the time the bot connected to
186 	 * the world.
187 	 */
188 	private PasswordReply passwordReply = null;
189 
190 	/**
191 	 * Instance of the password reply command that was sent upon receivieng
192 	 * request for the password (the world is locked).
193 	 * <p>
194 	 * <p>
195 	 * If null the password was not required by the time the bot connected to
196 	 * the world.
197 	 * 
198 	 * @return
199 	 */
200 	public PasswordReply getPasswordReply() {
201 		return passwordReply;
202 	}
203 
204 	/**
205 	 * This method is called whenever the Password event is caught telling us
206 	 * the world is locked and is requiring a password.
207 	 * <p>
208 	 * <p>
209 	 * May return null - in that case an empty password is sent to the server
210 	 * (which will probably result in closing the connection and termination of
211 	 * the agent).
212 	 * <p>
213 	 * <p>
214 	 * This message is then saved to private field passwordReply and is
215 	 * accessible via getPasswordReply() method if required to be probed during
216 	 * the bot's runtime.
217 	 * <p>
218 	 * <p>
219 	 * Note that if setPassword() method is called before this one it will use
220 	 * provided password via that method.
221 	 */
222 	protected PasswordReply createPasswordReply() {
223 		return desiredPassword != null ? new PasswordReply(desiredPassword)
224 				: null;
225 	}
226 
227 	/**
228 	 * Listener that is hooked to WorldView awaiting event InitCommandRequest
229 	 * calling initCommandRequested() method upon receiving the event.
230 	 */
231 	private IWorldEventListener<Password> passwordRequestedListener = new IWorldEventListener<Password>() {
232 
233 		@Override
234 		public void notify(Password event) {
235 			setState(new AgentStateStarting("Password requested by the world."));
236 			passwordReply = createPasswordReply();
237 			if (passwordReply == null) {
238 				passwordReply = new PasswordReply("");
239 			}
240 			if (log.isLoggable(Level.INFO)) log.info("Password required for the world, replying with '"
241 					+ passwordReply.getPassword() + "'.");
242 			getAct().act(passwordReply);
243 		}
244 	};
245 	// -------------------------
246 	// -=-=-=-=-=-=-=-=-=-=-=-=-
247 	// GAMEINFO MESSAGE LISTENER
248 	// -=-=-=-=-=-=-=-=-=-=-=-=-
249 	// -------------------------
250 	/**
251 	 * Contains information about the game.
252 	 */
253 	private GameInfo gameInfo = null;
254 
255 	public GameInfo getGameInfo() {
256 		if (gameInfo == null) {
257 			gameInfo = getWorldView().getSingle(GameInfo.class);
258 		}
259 		return gameInfo;
260 	}
261 
262 	@Override
263 	public WORLD_VIEW getWorldView() {
264 		return super.getWorldView();
265 	}
266 
267 	@Override
268 	public Collection<MapList> getAvailableMaps() {
269 		return maps;
270 	}
271 
272 	@Override
273 	public Flag<Double> getGameSpeedFlag() {
274 		return gameSpeed;
275 	}
276 
277 	@Override
278 	public String getMapName() {
279 		if (getGameInfo() == null) return null;
280 		return getGameInfo().getLevel();
281 	}
282 
283 	@Override
284 	public ObservableCollection<Player> getPlayers() {
285 		return players;
286 	}
287 	
288 	@Override
289 	public List<Mutator> getMutators() {
290 		return mutators;
291 	}
292 
293 	@Override
294 	public ObservableCollection<IUT2004Bot> getAgents() {
295 		if (agents != null) {
296 			return agents;
297 		}
298 
299 		if (getPlayers() == null) {
300 			// the info has not been initialized yet
301 			return null;
302 		} else {
303 			agents = new TranslatedObservableCollection<IUT2004Bot, Player>(
304 					getPlayers()) {
305 
306 				@Override
307 				protected IUT2004Bot translate(Player obj) {
308 					if (obj.getJmx() != null) {
309 						try {
310 							// the player represents Pogamut agent
311 							return new BotJMXProxy(obj.getJmx());
312 						} catch (Exception ex) {
313 							// communication failed
314 							Logger.getLogger(
315 									AbstractUT2004Server.class.getName()).log(
316 									Level.SEVERE, "JMX error", ex);
317 							throw new RuntimeException(ex);
318 						}
319 					} else {
320 						return null;
321 					}
322 				}
323 
324 				@Override
325 				protected Object getKeyForObj(Player elem) {
326 					return elem.getId();
327 				}
328 			};
329 		}
330 		return agents;
331 	}
332 
333 	@Override
334 	public ObservableCollection<? extends NativeUT2004BotAdapter> getNativeAgents() {
335 		if (nativeAgents != null) {
336 			return nativeAgents;
337 		}
338 
339 		if (getPlayers() == null) {
340 			// the info has not been initialized yet
341 			return null;
342 		} else {
343 
344 			nativeAgents = new TranslatedObservableCollection<NativeUT2004BotAdapter, Player>(
345 					getPlayers()) {
346 
347 				@Override
348 				protected NativeUT2004BotAdapter translate(Player obj) {
349 					if (obj.getJmx() == null) {
350 						try {
351 							// the player representing native bot
352 							return new NativeUT2004BotAdapter(obj,
353 									AbstractUT2004Server.this, getAct(),
354 									getWorldView());
355 						} catch (Exception ex) {
356 							// communication failed
357 							Logger.getLogger(
358 									AbstractUT2004Server.class.getName()).log(
359 									Level.SEVERE, "JMX error", ex);
360 							throw new RuntimeException(ex);
361 						}
362 					} else {
363 						return null;
364 					}
365 				}
366 
367 				@Override
368 				protected Object getKeyForObj(Player elem) {
369 					return elem.getId();
370 				}
371 			};
372 		}
373 		return nativeAgents;
374 
375 	}
376 
377 	@Override
378 	public void connectNativeBot(String botName, String botType, int team) {
379 		getAct().act(new AddBot(botName, null, null, 3, team, botType));
380 	}
381 
382 	@Override
383 	public UT2004Map getMap() {
384 		if (map == null) {
385 			map = new UT2004Map(getWorldView());
386 		}
387 		return map;
388 	}
389 	
390 	/////
391 	//
392 	// LIFECYCLE METHODS (starting/stopping server)
393 	//
394 	/////
395 	
396 	/**
397 	 * Called during stop/kill/reset events.
398 	 */
399 	protected void reset() {
400 		map = null;
401 		gameInfo = null;
402 		if (players != null) players.clear();
403 		if (mutators != null) mutators.clear();
404 		if (maps != null) maps.clear();
405 		if (nativeAgents != null) nativeAgents.clear();
406 		if (agents != null) agents.clear();
407 	}
408 	
409 	@Override
410 	protected void resetAgent() {
411 		super.resetAgent();
412 		reset();
413 	}
414 		
415 	@Override
416 	protected void stopAgent() {
417 		super.stopAgent();
418 		reset();
419 	}
420 	
421 	@Override
422 	protected void killAgent() {
423 		super.killAgent();
424 		reset();
425 	}
426 	
427 	@Override
428 	protected void startAgent() {
429 		super.startAgent();
430 	}	
431 	
432 	@Override
433 	protected void startPausedAgent() {
434 		super.startPausedAgent();
435 	}
436 
437 }