View Javadoc

1   package cz.cuni.amis.pogamut.ut2004.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.ut2004.agent.module.utils.ProjectileCleanUp;
40  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004Bot;
41  import cz.cuni.amis.pogamut.ut2004.bot.IUT2004BotController;
42  import cz.cuni.amis.pogamut.ut2004.bot.jmx.BotJMXMBeanAdapter;
43  import cz.cuni.amis.pogamut.ut2004.bot.params.UT2004BotParameters;
44  import cz.cuni.amis.pogamut.ut2004.bot.state.impl.BotStateHelloBotReceived;
45  import cz.cuni.amis.pogamut.ut2004.bot.state.impl.BotStateInited;
46  import cz.cuni.amis.pogamut.ut2004.bot.state.impl.BotStatePassword;
47  import cz.cuni.amis.pogamut.ut2004.bot.state.impl.BotStateSendingInit;
48  import cz.cuni.amis.pogamut.ut2004.bot.state.impl.BotStateSpawned;
49  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Configuration;
50  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.DisconnectBot;
51  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Initialize;
52  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.PasswordReply;
53  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Ready;
54  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbcommands.Respawn;
55  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.BotKilled;
56  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.ConfigChange;
57  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.EndMessage;
58  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.GameInfo;
59  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.HelloBotHandshake;
60  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.IncomingProjectile;
61  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.InitedMessage;
62  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Password;
63  import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Self;
64  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.InitCommandRequest;
65  import cz.cuni.amis.pogamut.ut2004.communication.translator.shared.events.ReadyCommandRequest;
66  import cz.cuni.amis.utils.ExceptionToString;
67  import cz.cuni.amis.utils.NullCheck;
68  import cz.cuni.amis.utils.exception.PogamutException;
69  import cz.cuni.amis.utils.exception.PogamutJMXException;
70  
71  /**
72   * Ancestor of all UT2004 bots.
73   * <p><p>
74   * TODO: [comment me!]
75   *
76   * @author Jimmy
77   */
78  @AgentScoped
79  public class UT2004Bot<WORLD_VIEW extends IVisionWorldView, ACT extends IAct, CONTROLLER extends IUT2004BotController> extends AbstractAgent3D<WORLD_VIEW, ACT> implements IUT2004Bot {
80  
81      /**
82       * If specified - used for the construction of the PasswordReply in createPasswordReply() method.
83       */
84  	private CONTROLLER controller;
85  	
86  	/**
87  	 * Latch that is raised when {@link InitedMessage} comes.
88  	 */
89  	private BusAwareCountDownLatch endMessageLatch;
90  
91  	/**
92  	 * Whether the {@link IUT2004BotController#botStopped()} was called during the running-phase of the agent.
93  	 */
94  	private boolean botStoppedCalled = false;
95  	
96  	private EventReact<HelloBotHandshake> helloBotReaction;
97  
98  	/**
99  	 * Parameters passed into the constructor/factory/runner (by whatever means the agent has been started).
100 	 */
101 	private UT2004BotParameters params;
102 	
103 	/**
104 	 * Provides clean-up behavior for {@link IncomingProjectile}. Whenever projectile disappears from the view, it is immediately destroyed
105 	 * as bot does not know what happens to it afterwards (whether it is still flying or already hit something). If we would not do this,
106 	 * world view would get littered with hanging {@link IncomingProjectile} instances leaking memory. 
107 	 */
108 	private ProjectileCleanUp projectileCleanUp;
109 
110 	/**
111 	 * 
112 	 * @param agentId
113 	 * @param eventBus
114 	 * @param logger
115 	 * @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
116 	 * @param act due to Guice nature, this can't be templated with ACT - Guice can't use it as a key for the injection
117 	 * @param init due to Guice nature, this can't be templated with CONTROLLER - Guice can't use it as a key for the injection
118 	 */
119     @Inject
120     public UT2004Bot(UT2004BotParameters parameters, IComponentBus eventBus, IAgentLogger logger, IWorldView worldView, IAct act, IUT2004BotController init) {
121         super(parameters.getAgentId(), eventBus, logger, (WORLD_VIEW)worldView, (ACT)act);
122 
123         this.params = parameters;
124         this.controller = (CONTROLLER) init;
125         NullCheck.check(this.controller, "init");
126         if (log.isLoggable(Level.FINER)) log.finer("Initializing the controller...");
127         this.controller.initializeController(this);
128         if (log.isLoggable(Level.FINER)) log.finer("Preparing the controller...");
129         this.controller.prepareBot(this);
130         if (log.isLoggable(Level.FINE)) log.fine("Controller initialized.");
131         
132         helloBotReaction = new EventReact<HelloBotHandshake>(HelloBotHandshake.class, worldView) {
133 			@Override
134 			protected void react(HelloBotHandshake event) {
135 				if (event.isServerFull()) throw new ComponentCantStartException("Server is full.", UT2004Bot.this);
136 			}
137         };
138         
139         getWorldView().addEventListener(ReadyCommandRequest.class, readyCommandRequestListener);
140         getWorldView().addEventListener(InitCommandRequest.class, initCommandRequestListener);
141         getWorldView().addEventListener(Password.class, passwordRequestedListener);
142         getWorldView().addObjectListener(InitedMessage.class, WorldObjectUpdatedEvent.class, initedMessageListener);
143         getWorldView().addEventListener(BotKilled.class, killedListener);
144         // endListener must be attached inside startAgent() as it is removed from the worldview in the end
145         // and we want the bot to be restartable
146         
147         endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
148         
149         projectileCleanUp = new ProjectileCleanUp(this);
150     }
151     
152     /**
153      * Returns the bot controller passed inside {@link UT2004Bot#AbstractUT2004Bot(IAgentId, IComponentBus, IAgentLogger, IVisionWorldView, IAct, IUT2004BotInitialization)}.
154      * @return
155      */
156     public CONTROLLER getController() {
157     	return controller;
158     }
159     
160     /**
161      * Returns parameters that were passed into the agent during the construction. 
162      * <p><p>
163      * This is a great place to parametrize your agent. Note that you may pass arbitrary subclass of {@link UT2004BotParameters}
164      * to the constructor/factory/runner and pick them up here.
165      * 
166      * @return parameters
167      */
168     public UT2004BotParameters getParams() {
169 		return params;
170 	}
171     
172     ////////  
173     //
174     // BOT CONTROL METHODS
175     //
176     ////////    
177 
178 	@Override
179 	protected void startAgent() {
180     	botStoppedCalled = false;
181     	super.startAgent();
182     	getWorldView().addEventListener(EndMessage.class, endListener);
183    		if (log.isLoggable(Level.INFO)) log.info("Waiting for the handshake to finish for 60s.");
184 		if (!endMessageLatch.await(60000, TimeUnit.MILLISECONDS)) {
185 			throw new ComponentCantStartException("The bot did not received first EndMessage in 60 seconds.", this);
186 		}
187 		if (log.isLoggable(Level.INFO)) log.info("Handshake finished.");
188     }
189 	
190 	@Override
191 	protected void startPausedAgent() {
192 		botStoppedCalled = false;
193 		super.startPausedAgent();
194 		getWorldView().addEventListener(EndMessage.class, endListener);
195    		if (log.isLoggable(Level.INFO)) log.info("Waiting for the handshake to finish for 60s.");
196 		if (!endMessageLatch.await(60000, TimeUnit.MILLISECONDS)) {
197 			throw new ComponentCantStartException("The bot did not received first EndMessage in 60 seconds.", this);
198 		}
199 		if (log.isLoggable(Level.INFO)) log.info("Handshake finished.");
200 	}
201     
202 	@Override
203 	protected void preStopAgent() {
204 		super.preStopAgent();
205 		try {
206 			tryDisconnect();
207     	} catch (Exception e) {
208 		}
209 	}
210 	
211     @Override
212     protected void stopAgent() {
213     	try {
214 	    	if (!botStoppedCalled) {
215 	    		botStoppedCalled = true;
216 	    		controller.botShutdown();	    		
217 	    	}	    	
218     	} finally {
219 			try {
220 				removeBotDisconnector();
221 			} finally {
222 				try {
223 					super.stopAgent();
224 				} finally {
225 					endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
226 				}
227 			}
228     	}
229     }
230     
231     @Override
232     protected void preKillAgent() {
233     	super.preKillAgent();
234     	try {
235 			tryDisconnect();
236     	} catch (Exception e) {
237 		}
238     }
239     
240     @Override
241     protected void killAgent() {
242        	try {
243 	    	if (!botStoppedCalled) {
244 	    		botStoppedCalled = true;
245 	    		controller.botShutdown();	    		
246 	    	}
247     	} finally {
248 			try {
249 				removeBotDisconnector();
250 			} finally {
251 				try {
252 					super.killAgent();
253 				} finally {
254 					endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
255 				}
256 			}
257     	}
258     }
259     
260     /**
261      * Disconnector thread serves as a last resort for shutting down the bot inside GB2004 in case of JVM failures.
262      */
263     protected Thread botDisconnectorThread;
264     
265     /**
266      * Sends {@link DisconnectBot} commands to GB2004, eats up all exceptions.
267      */
268     protected void tryDisconnect() {
269     	try {
270     		DisconnectBot cmd = new DisconnectBot();
271     		try {
272     			log.info("Sending " + cmd + " to destroy bot inside UT2004.");
273     		} finally {
274     			getAct().act(cmd);
275     			Thread.sleep(1000);    			
276     		}
277     	} catch (Exception e) {
278     		log.warning(ExceptionToString.process("Failed to disconnect the bot, we hope that GB2004 will remove it when the socket gets closed.", e));
279     	}
280     }
281     
282     /**
283      * Initializes & registers {@link UT2004Bot#botDisconnectorThread} as a {@link Runtime#addShutdownHook(Thread)}.
284      */
285     protected void addBotDisconnector() {
286     	if (botDisconnectorThread == null) {
287     		botDisconnectorThread = new Thread(
288     			new Runnable() {
289 					@Override
290 					public void run() {
291 						tryDisconnect();
292 					}    				
293     			},
294     			getName() + "-Disconnector"
295     		);
296     		try {
297     			Runtime.getRuntime().addShutdownHook(botDisconnectorThread);
298     		} catch (Exception e) {
299     			throw new PogamutException("Failed to add BotDisconnectorThread as a JVM shutdown hook.", e, this);
300     		}
301     	}
302     }
303     
304     /**
305      * Removes {@link UT2004Bot#botDisconnectorThread} as a {@link Runtime#removeShutdownHook(Thread)} and nullify the field.
306      */
307     protected void removeBotDisconnector() {
308     	if (botDisconnectorThread != null) {
309     		try {
310     			Runtime.getRuntime().removeShutdownHook(botDisconnectorThread);
311     		} catch (Exception e) {
312     			log.warning(ExceptionToString.process("Failed to remove BotDisconnectorThread as a JVM shutdown hook.", e));
313     		}
314     		botDisconnectorThread = null;
315     	}
316     }
317 
318     // --------------
319     // -=-=-=-=-=-=-=
320     // READY LISTENER
321     // -=-=-=-=-=-=-=
322     // --------------
323     
324     /**
325      * This method is called whenever HelloBot message is parsed - the GameBots2004 is awaiting
326      * the bot to reply with Ready command to begin the handshake.
327      */
328     protected void readyCommandRequested() {
329         getAct().act(new Ready());
330     }
331     
332     /**
333      * Listener that is hooked to WorldView awaiting event ReadyCommandRequest calling
334      * setupWorldViewListeners() and then readyCommandRequested() method upon receiving the event.
335      */
336     private IWorldEventListener<ReadyCommandRequest> readyCommandRequestListener =
337             new IWorldEventListener<ReadyCommandRequest>() {
338 
339                 @Override
340                 public void notify(ReadyCommandRequest event) {
341                 	controller.getLog().setLevel(Level.ALL);
342                 	setState(new BotStateHelloBotReceived("GameBots2004 greeted us, adding custom listeners onto the worldview."));
343                     readyCommandRequested();
344                     setState(new BotStateHelloBotReceived("READY sent, handshaking."));
345                 }
346             };
347             
348     // --------------------
349     // -=-=-=-=-=-=-=-=-=-=
350     // INITIALIZER LISTENER
351     // -=-=-=-=-=-=-=-=-=-=
352     // --------------------
353 
354     /**
355      * This method is called whenever handshake with GameBots2004 is over - the GameBots2004 is awaiting
356      * the bot to reply with Ready command to begin the handshake. It calls setUpInit() method
357      * to obtains Initialize message that is then sent to GameBots2004.
358      * <p><p>
359      * Left as protected if you need to override it - but you probably wouldn't.
360      */
361     protected void initCommandRequested() {
362         Initialize initializeCommand = getController().getInitializeCommand();
363         if (initializeCommand == null) {
364         	throw new AgentException("getBotInit().getInitializeCommand() method returned null message, can't initialize the agent!", log, this);
365         }
366         if(initializeCommand.getName() == null) {
367             // set agent name shown in Unreal
368             initializeCommand.setName(getComponentId().getName().getFlag());
369         } else {
370             // override original name
371             getComponentId().getName().setFlag(initializeCommand.getName());
372         }
373         if (initializeCommand.getTeam() == null) {
374         	initializeCommand.setTeam(params.getTeam());
375         }
376         if (initializeCommand.getLocation() == null) {
377         	initializeCommand.setLocation(params.getInitialLocation());
378         }
379         if (initializeCommand.getRotation() == null) {
380         	initializeCommand.setRotation(params.getInitialRotation());
381         }
382         try {
383             // set the JMX name
384             initializeCommand.setJmx(getJMX().enableJMX());
385         } catch (Exception e) {
386             throw new PogamutJMXException("Error seting up JMX name of the agent.", e, log, this);
387         }
388 
389         getAct().act(initializeCommand);
390     }
391     
392     /**
393      * Listener that is hooked to WorldView awaiting event InitCommandRequest calling
394      * initCommandRequested() method upon receiving the event.
395      */
396     private IWorldEventListener<InitCommandRequest> initCommandRequestListener =
397             new IWorldEventListener<InitCommandRequest>() {
398                 @Override
399                 public void notify(InitCommandRequest event) {
400                 	setState(new BotStateSendingInit("Handshake over, sending INIT."));
401                 	initCommandRequested();
402                 	setState(new BotStateSendingInit("Handshake over, INIT sent."));
403                 }
404             };
405             
406     // -----------------
407     // -=-=-=-=-=-=-=-=-
408     // PASSWORD LISTENER
409     // -=-=-=-=-=-=-=-=-
410     // -----------------
411     
412     /**
413      * Listener that is hooked to WorldView awaiting event InitCommandRequest calling
414      * initCommandRequested() method upon receiving the event.
415      */
416     private IWorldEventListener<Password> passwordRequestedListener =
417             new IWorldEventListener<Password>() {
418                 @Override
419                 public void notify(Password event) {
420                     setState(new BotStatePassword("Password requested by the world."));
421                     PasswordReply passwordReply = getController().getPassword();
422                     if (passwordReply == null) {
423                     	if (log.isLoggable(Level.WARNING)) log.warning("createPasswordReply() returned null");
424                         passwordReply = new PasswordReply("");
425                     }
426                     if (log.isLoggable(Level.INFO)) log.info("Password required for the world, replying with '" + passwordReply.getPassword() + "'.");
427                     getAct().act(passwordReply);
428                     setState(new BotStatePassword("Password sent."));
429                 }
430             };
431 
432     // -----------------------
433     // -=-=-=-=-=-=-=-=-=-=-=-
434     // INITED MESSAGE LISTENER
435     // -=-=-=-=-=-=-=-=-=-=-=-
436     // -----------------------
437     /**
438      * Listener that is hooked to WorldView awaiting event InitedMessage calling
439      * botInitialized method upon receiving the event.
440      */
441     private IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>> initedMessageListener =
442             new IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>>() {
443 
444                 @Override
445                 public void notify(WorldObjectUpdatedEvent<InitedMessage> event) {
446                 	setState(new BotStateInited("InitedMessage received, calling botInitialized()."));
447                 	controller.botInitialized(getWorldView().getSingle(GameInfo.class), getWorldView().getSingle(ConfigChange.class), event.getObject());
448                     setState(new BotStateInited("Bot initialized."));                    
449                 }
450             };
451             
452 	// ---------------------------
453 	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
454 	// BOT KILLED MESSAGE LISTENER
455 	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
456 	// ---------------------------
457 	/**
458 	 * Listener that is hooked to WorldView awaiting event {@link BotKilled} calling
459 	 * botKilled method upon receiving the event.
460 	 */
461 	private IWorldEventListener<BotKilled> killedListener =
462 	        new IWorldEventListener<BotKilled>() {
463 	            @Override
464 	            public void notify(BotKilled event) {
465 	                getController().botKilled(event);
466 	            }
467 	        };
468 	        
469     // ------------------
470 	// -=-=-=-=--=-=-=-=-
471 	// FIRST END LISTENER
472 	// -=-=-=-=--=-=-=-=-
473 	// ------------------
474 	private IWorldEventListener<EndMessage> endListener = 
475 			new IWorldEventListener<EndMessage>() {
476 
477 				@Override
478 				public void notify(EndMessage event) {
479 					setState(new BotStateSpawned("First batch of informations received - calling botSpawned()."));
480 					controller.botFirstSpawn(getWorldView().getSingle(GameInfo.class), getWorldView().getSingle(ConfigChange.class), getWorldView().getSingle(InitedMessage.class), getWorldView().getSingle(Self.class));
481 					setState(new BotStateSpawned("botSpawned() finished, finalizing controller initialization..."));					
482 					controller.finishControllerInitialization();
483 					setState(new BotStateSpawned("finishControllerInitialization() finished, UT2004Bot is running."));
484 					getWorldView().removeEventListener(EndMessage.class, this);
485 					endMessageLatch.countDown();
486 				}
487 		
488 	};
489 
490 	//
491 	// UTILITY METHODS
492 	//
493 	
494 	/**
495 	 * Return's bot's {@link Self} object.
496 	 * 
497 	 * WARNING: this returns NULL until the first sync-batch is sent by GB2004 (first logic() called).
498 	 */
499 	public Self getSelf() {
500 		return getWorldView().getSingle(Self.class);
501 	}
502 	
503     /**
504      * @return Location of the agent. Null if not set yet.
505      */
506     public Location getLocation() {
507         Self self = getWorldView().getSingle(Self.class);
508         if (self != null) {
509             return self.getLocation();
510         }
511         return null;
512     }
513 
514     /**
515      * @return Rotation of the agent. Null if not set yet.
516      */
517     public Rotation getRotation() {
518         Self self = getWorldView().getSingle(Self.class);
519         if (self != null) {
520             return self.getRotation();
521         }
522         return null;
523     }
524 
525     /**
526      * @return Velocity of the agent. Null if not set yet.
527      */
528     public Velocity getVelocity() {
529         Self self = getWorldView().getSingle(Self.class);
530         if (self != null) {
531             return self.getVelocity();
532         }
533         return null;
534     }
535 
536     public void respawn() throws PogamutException {
537         getAct().act(new Respawn());
538     }
539 
540     @Override
541     protected AgentJMXComponents createAgentJMX() {
542         return new AgentJMXComponents<IUT2004Bot>(this) {
543 
544             @Override
545             protected AgentMBeanAdapter createAgentMBean(ObjectName objectName, MBeanServer mbs) throws MalformedObjectNameException, InstanceAlreadyExistsException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
546                 return new BotJMXMBeanAdapter(UT2004Bot.this, objectName, mbs);
547             }
548         };
549     }
550 
551     public void setBoolConfigure(BoolBotParam param, boolean value) {
552         try {
553             Configuration configuration = new Configuration();
554             // uff, copy all values manually
555             ConfigChange confCh = getWorldView().getSingle(ConfigChange.class);
556             configuration.copy(confCh);
557 
558             param.set(configuration, value);
559             param.setField(confCh, value);
560             getAct().act(configuration);
561         } catch (Exception ex) {
562         	// TODO: jimmy - co to je za logging?! ... mame log.severe() ...
563         	//       a nemel by se volat FATAL ERROR?! ... nebo tu vyjimku propagovat?
564             Logger.getLogger(UT2004Bot.class.getName()).log(Level.SEVERE, null, ex);
565         }
566     }
567 
568     public boolean getBoolConfigure(BoolBotParam param) {
569         try {
570             return param.get(getWorldView().getSingle(ConfigChange.class));
571         } catch (Exception ex) {
572         	// TODO: jimmy - co to je za logging?! ... mame log.severe() ...
573         	//     	 a nemel by se volat FATAL ERROR?! ... nebo tu vyjimku propagovat?
574             Logger.getLogger(UT2004Bot.class.getName()).log(Level.SEVERE, null, ex);
575             return false; // TODO
576         }
577     }
578 
579     @Override
580     protected Folder createIntrospection() {
581         return new ReflectionObjectFolder(AbstractAgent.INTROSPECTION_ROOT_NAME, controller);
582     }
583     
584     @Override
585     public WORLD_VIEW getWorldView() {
586     	return super.getWorldView();
587     }
588     
589 }