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
69
70
71
72
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
79
80 private CONTROLLER controller;
81
82
83
84
85 private BusAwareCountDownLatch endMessageLatch;
86
87
88
89
90 private boolean botStoppedCalled = false;
91
92 private EventReact<HelloBotHandshake> helloBotReaction;
93
94
95
96
97 private UDKAgentParameters params;
98
99
100
101
102
103
104
105
106
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
133
134
135 endMessageLatch = new BusAwareCountDownLatch(1, getEventBus(), getWorldView());
136 }
137
138
139
140
141
142 public CONTROLLER getController() {
143 return controller;
144 }
145
146
147
148
149
150
151
152
153
154 public UDKAgentParameters getParams() {
155 return params;
156 }
157
158
159
160
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
205
206
207
208
209
210
211
212 protected void readyCommandRequested() {
213 getAct().act(new Ready());
214 }
215
216
217
218
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
234
235
236
237
238
239
240
241
242
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
251 initializeCommand.setName(getComponentId().getName().getFlag());
252 } else {
253
254 getComponentId().getName().setFlag(initializeCommand.getName());
255 }
256 try {
257
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
268
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
283
284
285
286
287
288
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
309
310
311
312
313
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
329
330
331
332
333
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
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
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
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
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
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
422
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
432
433 Logger.getLogger(UDKBot.class.getName()).log(Level.SEVERE, null, ex);
434 return false;
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 }