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
73
74
75
76
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
83
84 private CONTROLLER controller;
85
86
87
88
89 private BusAwareCountDownLatch endMessageLatch;
90
91
92
93
94 private boolean botStoppedCalled = false;
95
96 private EventReact<HelloBotHandshake> helloBotReaction;
97
98
99
100
101 private UT2004BotParameters params;
102
103
104
105
106
107
108 private ProjectileCleanUp projectileCleanUp;
109
110
111
112
113
114
115
116
117
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
145
146
147 endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
148
149 projectileCleanUp = new ProjectileCleanUp(this);
150 }
151
152
153
154
155
156 public CONTROLLER getController() {
157 return controller;
158 }
159
160
161
162
163
164
165
166
167
168 public UT2004BotParameters getParams() {
169 return params;
170 }
171
172
173
174
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
262
263 protected Thread botDisconnectorThread;
264
265
266
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
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
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
321
322
323
324
325
326
327
328 protected void readyCommandRequested() {
329 getAct().act(new Ready());
330 }
331
332
333
334
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
351
352
353
354
355
356
357
358
359
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
368 initializeCommand.setName(getComponentId().getName().getFlag());
369 } else {
370
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
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
394
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
409
410
411
412
413
414
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
435
436
437
438
439
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
455
456
457
458
459
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
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
492
493
494
495
496
497
498
499 public Self getSelf() {
500 return getWorldView().getSingle(Self.class);
501 }
502
503
504
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
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
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
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
563
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
573
574 Logger.getLogger(UT2004Bot.class.getName()).log(Level.SEVERE, null, ex);
575 return false;
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 }