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 private UT2004BotName botName;
107
108
109
110
111
112
113 private ProjectileCleanUp projectileCleanUp;
114
115
116
117
118
119
120
121
122
123
124 @Inject
125 public UT2004Bot(UT2004BotParameters parameters, IComponentBus eventBus, IAgentLogger logger, IWorldView worldView, IAct act, IUT2004BotController init) {
126 super(parameters.getAgentId(), eventBus, logger, (WORLD_VIEW)worldView, (ACT)act);
127
128 this.params = parameters;
129 this.controller = (CONTROLLER) init;
130 NullCheck.check(this.controller, "init");
131 if (log.isLoggable(Level.FINER)) log.finer("Initializing the controller...");
132 this.controller.initializeController(this);
133 if (log.isLoggable(Level.FINER)) log.finer("Preparing the controller...");
134 this.controller.prepareBot(this);
135 if (log.isLoggable(Level.FINE)) log.fine("Controller initialized.");
136
137 helloBotReaction = new EventReact<HelloBotHandshake>(HelloBotHandshake.class, worldView) {
138 @Override
139 protected void react(HelloBotHandshake event) {
140 if (event.isServerFull()) throw new ComponentCantStartException("Server is full.", UT2004Bot.this);
141 }
142 };
143
144 getWorldView().addEventListener(ReadyCommandRequest.class, readyCommandRequestListener);
145 getWorldView().addEventListener(InitCommandRequest.class, initCommandRequestListener);
146 getWorldView().addEventListener(Password.class, passwordRequestedListener);
147 getWorldView().addObjectListener(InitedMessage.class, WorldObjectUpdatedEvent.class, initedMessageListener);
148 getWorldView().addEventListener(BotKilled.class, killedListener);
149
150
151
152 endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
153
154 projectileCleanUp = new ProjectileCleanUp(this);
155
156 botName = new UT2004BotName(this, "undefined");
157 }
158
159
160
161
162
163 public CONTROLLER getController() {
164 return controller;
165 }
166
167
168
169
170
171
172
173
174
175 public UT2004BotParameters getParams() {
176 return params;
177 }
178
179
180
181
182
183
184
185 @Override
186 protected void startAgent() {
187 botStoppedCalled = false;
188 super.startAgent();
189 getWorldView().addEventListener(EndMessage.class, endListener);
190 if (log.isLoggable(Level.INFO)) log.info("Waiting for the handshake to finish for 60s.");
191 if (!endMessageLatch.await(60000, TimeUnit.MILLISECONDS)) {
192 throw new ComponentCantStartException("The bot did not received first EndMessage in 60 seconds.", this);
193 }
194 if (log.isLoggable(Level.INFO)) log.info("Handshake finished.");
195 }
196
197 @Override
198 protected void startPausedAgent() {
199 botStoppedCalled = false;
200 super.startPausedAgent();
201 getWorldView().addEventListener(EndMessage.class, endListener);
202 if (log.isLoggable(Level.INFO)) log.info("Waiting for the handshake to finish for 60s.");
203 if (!endMessageLatch.await(60000, TimeUnit.MILLISECONDS)) {
204 throw new ComponentCantStartException("The bot did not received first EndMessage in 60 seconds.", this);
205 }
206 if (log.isLoggable(Level.INFO)) log.info("Handshake finished.");
207 }
208
209 @Override
210 protected void preStopAgent() {
211 super.preStopAgent();
212 try {
213 tryDisconnect();
214 } catch (Exception e) {
215 }
216 }
217
218 @Override
219 protected void stopAgent() {
220 try {
221 if (!botStoppedCalled) {
222 botStoppedCalled = true;
223 controller.botShutdown();
224 }
225 } finally {
226 try {
227 removeBotDisconnector();
228 } finally {
229 try {
230 super.stopAgent();
231 } finally {
232 endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
233 }
234 }
235 }
236 }
237
238 @Override
239 protected void preKillAgent() {
240 super.preKillAgent();
241 try {
242 tryDisconnect();
243 } catch (Exception e) {
244 }
245 }
246
247 @Override
248 protected void killAgent() {
249 try {
250 if (!botStoppedCalled) {
251 botStoppedCalled = true;
252 controller.botShutdown();
253 }
254 } finally {
255 try {
256 removeBotDisconnector();
257 } finally {
258 try {
259 super.killAgent();
260 } finally {
261 endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
262 }
263 }
264 }
265 }
266
267
268
269
270 protected Thread botDisconnectorThread;
271
272
273
274
275 protected void tryDisconnect() {
276 try {
277 DisconnectBot cmd = new DisconnectBot();
278 try {
279 log.info("Sending " + cmd + " to destroy bot inside UT2004.");
280 } finally {
281 getAct().act(cmd);
282 Thread.sleep(1000);
283 }
284 } catch (Exception e) {
285 log.warning(ExceptionToString.process("Failed to disconnect the bot, we hope that GB2004 will remove it when the socket gets closed.", e));
286 }
287 }
288
289
290
291
292 protected void addBotDisconnector() {
293 if (botDisconnectorThread == null) {
294 botDisconnectorThread = new Thread(
295 new Runnable() {
296 @Override
297 public void run() {
298 tryDisconnect();
299 }
300 },
301 getName() + "-Disconnector"
302 );
303 try {
304 Runtime.getRuntime().addShutdownHook(botDisconnectorThread);
305 } catch (Exception e) {
306 throw new PogamutException("Failed to add BotDisconnectorThread as a JVM shutdown hook.", e, this);
307 }
308 }
309 }
310
311
312
313
314 protected void removeBotDisconnector() {
315 if (botDisconnectorThread != null) {
316 try {
317 Runtime.getRuntime().removeShutdownHook(botDisconnectorThread);
318 } catch (Exception e) {
319 log.warning(ExceptionToString.process("Failed to remove BotDisconnectorThread as a JVM shutdown hook.", e));
320 }
321 botDisconnectorThread = null;
322 }
323 }
324
325
326
327
328
329
330
331
332
333
334
335 protected void readyCommandRequested() {
336 getAct().act(new Ready());
337 }
338
339
340
341
342
343 private IWorldEventListener<ReadyCommandRequest> readyCommandRequestListener =
344 new IWorldEventListener<ReadyCommandRequest>() {
345
346 @Override
347 public void notify(ReadyCommandRequest event) {
348 controller.getLog().setLevel(Level.ALL);
349 setState(new BotStateHelloBotReceived("GameBots2004 greeted us, adding custom listeners onto the worldview."));
350 readyCommandRequested();
351 setState(new BotStateHelloBotReceived("READY sent, handshaking."));
352 }
353 };
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368 protected void initCommandRequested() {
369 Initialize initializeCommand = getController().getInitializeCommand();
370 if (initializeCommand == null) {
371 throw new AgentException("getBotInit().getInitializeCommand() method returned null message, can't initialize the agent!", log, this);
372 }
373 if(initializeCommand.getName() == null) {
374
375 initializeCommand.setName(getComponentId().getName().getFlag());
376 } else {
377
378 getComponentId().getName().setFlag(initializeCommand.getName());
379 }
380 botName.setNameBase(initializeCommand.getName());
381 if (initializeCommand.getTeam() == null) {
382 initializeCommand.setTeam(params.getTeam());
383 }
384 if (initializeCommand.getLocation() == null) {
385 initializeCommand.setLocation(params.getInitialLocation());
386 }
387 if (initializeCommand.getRotation() == null) {
388 initializeCommand.setRotation(params.getInitialRotation());
389 }
390 try {
391
392 initializeCommand.setJmx(getJMX().enableJMX());
393 } catch (Exception e) {
394 throw new PogamutJMXException("Error seting up JMX name of the agent.", e, log, this);
395 }
396
397 getAct().act(initializeCommand);
398 }
399
400
401
402
403
404 private IWorldEventListener<InitCommandRequest> initCommandRequestListener =
405 new IWorldEventListener<InitCommandRequest>() {
406 @Override
407 public void notify(InitCommandRequest event) {
408 setState(new BotStateSendingInit("Handshake over, sending INIT."));
409 initCommandRequested();
410 setState(new BotStateSendingInit("Handshake over, INIT sent."));
411 }
412 };
413
414
415
416
417
418
419
420
421
422
423
424 private IWorldEventListener<Password> passwordRequestedListener =
425 new IWorldEventListener<Password>() {
426 @Override
427 public void notify(Password event) {
428 setState(new BotStatePassword("Password requested by the world."));
429 PasswordReply passwordReply = getController().getPassword();
430 if (passwordReply == null) {
431 if (log.isLoggable(Level.WARNING)) log.warning("createPasswordReply() returned null");
432 passwordReply = new PasswordReply("");
433 }
434 if (log.isLoggable(Level.INFO)) log.info("Password required for the world, replying with '" + passwordReply.getPassword() + "'.");
435 getAct().act(passwordReply);
436 setState(new BotStatePassword("Password sent."));
437 }
438 };
439
440
441
442
443
444
445
446
447
448
449 private IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>> initedMessageListener =
450 new IWorldObjectEventListener<InitedMessage, WorldObjectUpdatedEvent<InitedMessage>>() {
451
452 @Override
453 public void notify(WorldObjectUpdatedEvent<InitedMessage> event) {
454 setState(new BotStateInited("InitedMessage received, calling botInitialized()."));
455 controller.botInitialized(getWorldView().getSingle(GameInfo.class), getWorldView().getSingle(ConfigChange.class), event.getObject());
456 setState(new BotStateInited("Bot initialized."));
457 }
458 };
459
460
461
462
463
464
465
466
467
468
469 private IWorldEventListener<BotKilled> killedListener =
470 new IWorldEventListener<BotKilled>() {
471 @Override
472 public void notify(BotKilled event) {
473 getController().botKilled(event);
474 }
475 };
476
477
478
479
480
481
482 private IWorldEventListener<EndMessage> endListener =
483 new IWorldEventListener<EndMessage>() {
484
485 @Override
486 public void notify(EndMessage event) {
487 setState(new BotStateSpawned("First batch of informations received - calling botSpawned()."));
488 controller.botFirstSpawn(getWorldView().getSingle(GameInfo.class), getWorldView().getSingle(ConfigChange.class), getWorldView().getSingle(InitedMessage.class), getWorldView().getSingle(Self.class));
489 setState(new BotStateSpawned("botSpawned() finished, finalizing controller initialization..."));
490 controller.finishControllerInitialization();
491 setState(new BotStateSpawned("finishControllerInitialization() finished, UT2004Bot is running."));
492 getWorldView().removeEventListener(EndMessage.class, this);
493 endMessageLatch.countDown();
494 }
495
496 };
497
498
499
500
501
502
503
504
505
506
507 public Self getSelf() {
508 return getWorldView().getSingle(Self.class);
509 }
510
511
512
513
514 public Location getLocation() {
515 Self self = getWorldView().getSingle(Self.class);
516 if (self != null) {
517 return self.getLocation();
518 }
519 return null;
520 }
521
522
523
524
525 public Rotation getRotation() {
526 Self self = getWorldView().getSingle(Self.class);
527 if (self != null) {
528 return self.getRotation();
529 }
530 return null;
531 }
532
533
534
535
536 public Velocity getVelocity() {
537 Self self = getWorldView().getSingle(Self.class);
538 if (self != null) {
539 return self.getVelocity();
540 }
541 return null;
542 }
543
544 public void respawn() throws PogamutException {
545 getAct().act(new Respawn());
546 }
547
548 @Override
549 protected AgentJMXComponents createAgentJMX() {
550 return new AgentJMXComponents<IUT2004Bot>(this) {
551
552 @Override
553 protected AgentMBeanAdapter createAgentMBean(ObjectName objectName, MBeanServer mbs) throws MalformedObjectNameException, InstanceAlreadyExistsException, InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException {
554 return new BotJMXMBeanAdapter(UT2004Bot.this, objectName, mbs);
555 }
556 };
557 }
558
559 public void setBoolConfigure(BoolBotParam param, boolean value) {
560 try {
561 Configuration configuration = new Configuration();
562
563 ConfigChange confCh = getWorldView().getSingle(ConfigChange.class);
564 configuration.copy(confCh);
565
566 param.set(configuration, value);
567 param.setField(confCh, value);
568 getAct().act(configuration);
569 } catch (Exception ex) {
570
571
572 Logger.getLogger(UT2004Bot.class.getName()).log(Level.SEVERE, null, ex);
573 }
574 }
575
576 public boolean getBoolConfigure(BoolBotParam param) {
577 try {
578 return param.get(getWorldView().getSingle(ConfigChange.class));
579 } catch (Exception ex) {
580
581
582 Logger.getLogger(UT2004Bot.class.getName()).log(Level.SEVERE, null, ex);
583 return false;
584 }
585 }
586
587 @Override
588 protected Folder createIntrospection() {
589 return new ReflectionObjectFolder(AbstractAgent.INTROSPECTION_ROOT_NAME, controller);
590 }
591
592 @Override
593 public WORLD_VIEW getWorldView() {
594 return super.getWorldView();
595 }
596
597 public UT2004BotName getBotName() {
598 return botName;
599 }
600
601 }