View Javadoc

1   package cz.cuni.amis.pogamut.udk.bot.impl;
2   
3   import java.util.concurrent.TimeUnit;
4   import java.util.logging.Level;
5   import java.util.logging.Logger;
6   
7   import javax.management.InstanceAlreadyExistsException;
8   import javax.management.MBeanRegistrationException;
9   import javax.management.MBeanServer;
10  import javax.management.MalformedObjectNameException;
11  import javax.management.NotCompliantMBeanException;
12  import javax.management.ObjectName;
13  
14  import com.google.inject.Inject;
15  
16  import cz.cuni.amis.introspection.Folder;
17  import cz.cuni.amis.introspection.java.ReflectionObjectFolder;
18  import cz.cuni.amis.pogamut.base.agent.IAgentId;
19  import cz.cuni.amis.pogamut.base.agent.exceptions.AgentException;
20  import cz.cuni.amis.pogamut.base.agent.impl.AbstractAgent;
21  import cz.cuni.amis.pogamut.base.agent.jmx.AgentJMXComponents;
22  import cz.cuni.amis.pogamut.base.agent.jmx.adapter.AgentMBeanAdapter;
23  import cz.cuni.amis.pogamut.base.communication.command.IAct;
24  import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
25  import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEventListener;
26  import cz.cuni.amis.pogamut.base.communication.worldview.object.IWorldObjectEventListener;
27  import cz.cuni.amis.pogamut.base.communication.worldview.object.event.WorldObjectUpdatedEvent;
28  import cz.cuni.amis.pogamut.base.communication.worldview.react.EventReact;
29  import cz.cuni.amis.pogamut.base.component.bus.IComponentBus;
30  import cz.cuni.amis.pogamut.base.component.bus.event.BusAwareCountDownLatch;
31  import cz.cuni.amis.pogamut.base.component.exception.ComponentCantStartException;
32  import cz.cuni.amis.pogamut.base.utils.guice.AgentScoped;
33  import cz.cuni.amis.pogamut.base.utils.logging.IAgentLogger;
34  import cz.cuni.amis.pogamut.base3d.agent.AbstractAgent3D;
35  import cz.cuni.amis.pogamut.base3d.worldview.IVisionWorldView;
36  import cz.cuni.amis.pogamut.base3d.worldview.object.Location;
37  import cz.cuni.amis.pogamut.base3d.worldview.object.Rotation;
38  import cz.cuni.amis.pogamut.base3d.worldview.object.Velocity;
39  import cz.cuni.amis.pogamut.udk.agent.params.UDKAgentParameters;
40  import cz.cuni.amis.pogamut.udk.bot.IUDKBot;
41  import cz.cuni.amis.pogamut.udk.bot.IUDKBotController;
42  import cz.cuni.amis.pogamut.udk.bot.jmx.BotJMXMBeanAdapter;
43  import cz.cuni.amis.pogamut.udk.bot.state.impl.BotStateHelloBotReceived;
44  import cz.cuni.amis.pogamut.udk.bot.state.impl.BotStateInited;
45  import cz.cuni.amis.pogamut.udk.bot.state.impl.BotStatePassword;
46  import cz.cuni.amis.pogamut.udk.bot.state.impl.BotStateSendingInit;
47  import cz.cuni.amis.pogamut.udk.bot.state.impl.BotStateSpawned;
48  import cz.cuni.amis.pogamut.udk.communication.messages.gbcommands.Configuration;
49  import cz.cuni.amis.pogamut.udk.communication.messages.gbcommands.Initialize;
50  import cz.cuni.amis.pogamut.udk.communication.messages.gbcommands.PasswordReply;
51  import cz.cuni.amis.pogamut.udk.communication.messages.gbcommands.Ready;
52  import cz.cuni.amis.pogamut.udk.communication.messages.gbcommands.Respawn;
53  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.BotKilled;
54  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.ConfigChange;
55  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.EndMessage;
56  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.GameInfo;
57  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.HelloBotHandshake;
58  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.InitedMessage;
59  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Password;
60  import cz.cuni.amis.pogamut.udk.communication.messages.gbinfomessages.Self;
61  import cz.cuni.amis.pogamut.udk.communication.translator.shared.events.InitCommandRequest;
62  import cz.cuni.amis.pogamut.udk.communication.translator.shared.events.ReadyCommandRequest;
63  import cz.cuni.amis.utils.NullCheck;
64  import cz.cuni.amis.utils.exception.PogamutException;
65  import cz.cuni.amis.utils.exception.PogamutJMXException;
66  
67  /**
68   * Ancestor of all UT2004 bots.
69   * <p><p>
70   * TODO: [comment me!]
71   *
72   * @author Jimmy
73   */
74  @AgentScoped
75  public class UDKBot<WORLD_VIEW extends IVisionWorldView, ACT extends IAct, CONTROLLER extends IUDKBotController> extends AbstractAgent3D<WORLD_VIEW, ACT> implements IUDKBot {
76  
77      /**
78       * If specified - used for the construction of the PasswordReply in createPasswordReply() method.
79       */
80  	private CONTROLLER controller;
81  	
82  	/**
83  	 * Latch that is raised when {@link InitedMessage} comes.
84  	 */
85  	private BusAwareCountDownLatch endMessageLatch;
86  
87  	/**
88  	 * Whether the {@link IUDKBotController#botStopped()} was called during the running-phase of the agent.
89  	 */
90  	private boolean botStoppedCalled = false;
91  	
92  	private EventReact<HelloBotHandshake> helloBotReaction;
93  
94  	/**
95  	 * Parameters passed into the constructor/factory/runner (by whatever means the agent has been started).
96  	 */
97  	private UDKAgentParameters params;
98  
99  	/**
100 	 * 
101 	 * @param agentId
102 	 * @param eventBus
103 	 * @param logger
104 	 * @param worldView due to Guice nature, this can't be templated with WORLD_VIEW - Guice can't use it as a key for the injection
105 	 * @param act due to Guice nature, this can't be templated with ACT - Guice can't use it as a key for the injection
106 	 * @param init due to Guice nature, this can't be templated with CONTROLLER - Guice can't use it as a key for the injection
107 	 */
108     @Inject
109     public UDKBot(UDKAgentParameters parameters, IComponentBus eventBus, IAgentLogger logger, IWorldView worldView, IAct act, IUDKBotController init) {
110         super(parameters.getAgentId(), eventBus, logger, (WORLD_VIEW)worldView, (ACT)act);
111         this.params = parameters;
112         this.controller = (CONTROLLER) init;
113         NullCheck.check(this.controller, "init");
114         if (log.isLoggable(Level.FINER)) log.finer("Initializing the controller...");
115         this.controller.initializeController(this);
116         if (log.isLoggable(Level.FINER)) log.finer("Preparing the controller...");
117         this.controller.prepareBot(this);
118         if (log.isLoggable(Level.FINE)) log.fine("Controller initialized.");
119         
120         helloBotReaction = new EventReact<HelloBotHandshake>(HelloBotHandshake.class, worldView) {
121 			@Override
122 			protected void react(HelloBotHandshake event) {
123 				if (event.isServerFull()) throw new ComponentCantStartException("Server is full.", UDKBot.this);
124 			}
125         };
126         
127         getWorldView().addEventListener(ReadyCommandRequest.class, readyCommandRequestListener);
128         getWorldView().addEventListener(InitCommandRequest.class, initCommandRequestListener);
129         getWorldView().addEventListener(Password.class, passwordRequestedListener);
130         getWorldView().addObjectListener(InitedMessage.class, WorldObjectUpdatedEvent.class, initedMessageListener);
131         getWorldView().addEventListener(BotKilled.class, killedListener);
132         // endListener must be attached inside startAgent() as it is removed from the worldview in the end
133         // and we want the bot to be restartable
134         
135         endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());        
136     }
137     
138     /**
139      * Returns the bot controller passed inside {@link UDKBot#AbstractUT2004Bot(IAgentId, IComponentBus, IAgentLogger, IVisionWorldView, IAct, IUT2004BotInitialization)}.
140      * @return
141      */
142     public CONTROLLER getController() {
143     	return controller;
144     }
145     
146     /**
147      * Returns parameters that were passed into the agent during the construction. 
148      * <p><p>
149      * This is a great place to parametrize your agent. Note that you may pass arbitrary subclass of {@link UDKAgentParameters}
150      * to the constructor/factory/runner and pick them up here.
151      * 
152      * @return parameters
153      */
154     public UDKAgentParameters getParams() {
155 		return params;
156 	}
157     
158     ////////  
159     //
160     // BOT CONTROL METHODS
161     //
162     ////////    
163 
164 	@Override
165 	protected void startAgent() {
166     	botStoppedCalled = false;
167     	super.startAgent();
168     	getWorldView().addEventListener(EndMessage.class, endListener);
169    		if (log.isLoggable(Level.INFO)) log.info("Waiting for the handshake to finish for 60s.");
170 		if (!endMessageLatch.await(60000, TimeUnit.MILLISECONDS)) {
171 			throw new ComponentCantStartException("The bot did not received first EndMessage in 60 seconds.", this);
172 		}
173 		if (log.isLoggable(Level.INFO)) log.info("Handshake finished.");
174     }
175     
176     @Override
177     protected void stopAgent() {
178     	try {
179 	    	if (!botStoppedCalled) {
180 	    		botStoppedCalled = true;
181 	    		controller.botShutdown();	    		
182 	    	}
183     	} finally {
184     		super.stopAgent();
185     	}
186     	endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
187     }
188     
189     @Override
190     protected void killAgent() {
191        	try {
192 	    	if (!botStoppedCalled) {
193 	    		botStoppedCalled = true;
194 	    		controller.botShutdown();	    		
195 	    	}
196     	} finally {
197     		super.killAgent();
198     	}
199     	endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
200     }
201 
202     // --------------
203     // -=-=-=-=-=-=-=
204     // READY LISTENER
205     // -=-=-=-=-=-=-=
206     // --------------
207     
208     /**
209      * This method is called whenever HelloBot message is parsed - the GameBots2004 is awaiting
210      * the bot to reply with Ready command to begin the handshake.
211      */
212     protected void readyCommandRequested() {
213         getAct().act(new Ready());
214     }
215     
216     /**
217      * Listener that is hooked to WorldView awaiting event ReadyCommandRequest calling
218      * setupWorldViewListeners() and then readyCommandRequested() method upon receiving the event.
219      */
220     private IWorldEventListener<ReadyCommandRequest> readyCommandRequestListener =
221             new IWorldEventListener<ReadyCommandRequest>() {
222 
223                 @Override
224                 public void notify(ReadyCommandRequest event) {
225                 	setState(new BotStateHelloBotReceived("GameBots2004 greeted us, adding custom listeners onto the worldview."));
226                     readyCommandRequested();
227                     setState(new BotStateHelloBotReceived("READY sent, handshaking."));
228                 }
229             };
230             
231     // --------------------
232     // -=-=-=-=-=-=-=-=-=-=
233     // INITIALIZER LISTENER
234     // -=-=-=-=-=-=-=-=-=-=
235     // --------------------
236 
237     /**
238      * This method is called whenever handshake with GameBots2004 is over - the GameBots2004 is awaiting
239      * the bot to reply with Ready command to begin the handshake. It calls setUpInit() method
240      * to obtains Initialize message that is then sent to GameBots2004.
241      * <p><p>
242      * Left as protected if you need to override it - but you probably wouldn't.
243      */
244     protected void initCommandRequested() {
245         Initialize initializeCommand = getController().getInitializeCommand();
246         if (initializeCommand == null) {
247         	throw new AgentException("getBotInit().getInitializeCommand() method returned null message, can't initialize the agent!", log, this);
248         }
249         if(initializeCommand.getName() == null) {
250             // set agent name shown in Unreal
251             initializeCommand.setName(getComponentId().getName().getFlag());
252         } else {
253             // override original name
254             getComponentId().getName().setFlag(initializeCommand.getName());
255         }
256         try {
257             // set the JMX name
258             initializeCommand.setJmx(getJMX().enableJMX());
259         } catch (Exception e) {
260             throw new PogamutJMXException("Error seting up JMX name of the agent.", e, log, this);
261         }
262 
263         getAct().act(initializeCommand);
264     }
265     
266     /**
267      * Listener that is hooked to WorldView awaiting event InitCommandRequest calling
268      * initCommandRequested() method upon receiving the event.
269      */
270     private IWorldEventListener<InitCommandRequest> initCommandRequestListener =
271             new IWorldEventListener<InitCommandRequest>() {
272                 @Override
273                 public void notify(InitCommandRequest event) {
274                 	setState(new BotStateSendingInit("Handshake over, sending INIT."));
275                 	initCommandRequested();
276                 	setState(new BotStateSendingInit("Handshake over, INIT sent."));
277                 }
278             };
279             
280     // -----------------
281     // -=-=-=-=-=-=-=-=-
282     // PASSWORD LISTENER
283     // -=-=-=-=-=-=-=-=-
284     // -----------------
285     
286     /**
287      * Listener that is hooked to WorldView awaiting event InitCommandRequest calling
288      * initCommandRequested() method upon receiving the event.
289      */
290     private IWorldEventListener<Password> passwordRequestedListener =
291             new IWorldEventListener<Password>() {
292                 @Override
293                 public void notify(Password event) {
294                     setState(new BotStatePassword("Password requested by the world."));
295                     PasswordReply passwordReply = getController().getPassword();
296                     if (passwordReply == null) {
297                     	if (log.isLoggable(Level.WARNING)) log.warning("createPasswordReply() returned null");
298                         passwordReply = new PasswordReply("");
299                     }
300                     if (log.isLoggable(Level.INFO)) log.info("Password required for the world, replying with '" + passwordReply.getPassword() + "'.");
301                     getAct().act(passwordReply);
302                     setState(new BotStatePassword("Password sent."));
303                 }
304             };
305 
306     // -----------------------
307     // -=-=-=-=-=-=-=-=-=-=-=-
308     // INITED MESSAGE LISTENER
309     // -=-=-=-=-=-=-=-=-=-=-=-
310     // -----------------------
311     /**
312      * Listener that is hooked to WorldView awaiting event InitedMessage calling
313      * botInitialized method upon receiving the event.
314      */
315     private IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>> initedMessageListener =
316             new IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>>() {
317 
318                 @Override
319                 public void notify(WorldObjectUpdatedEvent<InitedMessage> event) {
320                 	setState(new BotStateInited("InitedMessage received, calling botInitialized()."));
321                 	controller.botInitialized(getWorldView().getSingle(GameInfo.class), getWorldView().getSingle(ConfigChange.class), event.getObject());
322                     setState(new BotStateInited("Bot initialized."));                    
323                 }
324             };
325             
326 	// ---------------------------
327 	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
328 	// BOT KILLED MESSAGE LISTENER
329 	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
330 	// ---------------------------
331 	/**
332 	 * Listener that is hooked to WorldView awaiting event {@link BotKilled} calling
333 	 * botKilled method upon receiving the event.
334 	 */
335 	private IWorldEventListener<BotKilled> killedListener =
336 	        new IWorldEventListener<BotKilled>() {
337 	            @Override
338 	            public void notify(BotKilled event) {
339 	                getController().botKilled(event);
340 	            }
341 	        };
342 	        
343     // ------------------
344 	// -=-=-=-=--=-=-=-=-
345 	// FIRST END LISTENER
346 	// -=-=-=-=--=-=-=-=-
347 	// ------------------
348 	private IWorldEventListener<EndMessage> endListener = 
349 			new IWorldEventListener<EndMessage>() {
350 
351 				@Override
352 				public void notify(EndMessage event) {
353 					setState(new BotStateSpawned("First batch of informations received - calling botSpawned()."));
354 					controller.botSpawned(getWorldView().getSingle(GameInfo.class), getWorldView().getSingle(ConfigChange.class), getWorldView().getSingle(InitedMessage.class), getWorldView().getSingle(Self.class));
355 					setState(new BotStateSpawned("botSpawned() finished, UT2004Bot is running."));					
356 					getWorldView().removeEventListener(EndMessage.class, this);
357 					endMessageLatch.countDown();
358 				}
359 		
360 	};
361 
362     /**
363      * @return Location of the agent. Null if not set yet.
364      */
365     public Location getLocation() {
366         Self self = getWorldView().getSingle(Self.class);
367         if (self != null) {
368             return self.getLocation();
369         }
370         return null;
371     }
372 
373     /**
374      * @return Rotation of the agent. Null if not set yet.
375      */
376     public Rotation getRotation() {
377         Self self = getWorldView().getSingle(Self.class);
378         if (self != null) {
379             return self.getRotation();
380         }
381         return null;
382     }
383 
384     /**
385      * @return Velocity of the agent. Null if not set yet.
386      */
387     public Velocity getVelocity() {
388         Self self = getWorldView().getSingle(Self.class);
389         if (self != null) {
390             return self.getVelocity();
391         }
392         return null;
393     }
394 
395     public void respawn() throws PogamutException {
396         getAct().act(new Respawn());
397     }
398 
399     @Override
400     protected AgentJMXComponents createAgentJMX() {
401         return new AgentJMXComponents<IUDKBot>(this) {
402 
403             @Override
404             protected AgentMBeanAdapter createAgentMBean(ObjectName objectName, MBeanServer mbs) throws MalformedObjectNameException, InstanceAlreadyExistsException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
405                 return new BotJMXMBeanAdapter(UDKBot.this, objectName, mbs);
406             }
407         };
408     }
409 
410     public void setBoolConfigure(BoolBotParam param, boolean value) {
411         try {
412             Configuration configuration = new Configuration();
413             // uff, copy all values manually
414             ConfigChange confCh = getWorldView().getSingle(ConfigChange.class);
415             configuration.copy(confCh);
416 
417             param.set(configuration, value);
418             param.setField(confCh, value);
419             getAct().act(configuration);
420         } catch (Exception ex) {
421         	// TODO: jimmy - co to je za logging?! ... mame log.severe() ...
422         	//       a nemel by se volat FATAL ERROR?! ... nebo tu vyjimku propagovat?
423             Logger.getLogger(UDKBot.class.getName()).log(Level.SEVERE, null, ex);
424         }
425     }
426 
427     public boolean getBoolConfigure(BoolBotParam param) {
428         try {
429             return param.get(getWorldView().getSingle(ConfigChange.class));
430         } catch (Exception ex) {
431         	// TODO: jimmy - co to je za logging?! ... mame log.severe() ...
432         	//     	 a nemel by se volat FATAL ERROR?! ... nebo tu vyjimku propagovat?
433             Logger.getLogger(UDKBot.class.getName()).log(Level.SEVERE, null, ex);
434             return false; // TODO
435         }
436     }
437 
438     @Override
439     protected Folder createIntrospection() {
440         return new ReflectionObjectFolder(AbstractAgent.INTROSPECTION_ROOT_NAME, controller);
441     }
442     
443     @Override
444     public WORLD_VIEW getWorldView() {
445     	return super.getWorldView();
446     }
447     
448 }