1 package cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client;
2
3 import java.io.Serializable;
4 import java.net.InetSocketAddress;
5 import java.util.Collections;
6 import java.util.HashSet;
7 import java.util.Map;
8 import java.util.Set;
9 import java.util.Timer;
10 import java.util.TimerTask;
11 import java.util.logging.Logger;
12
13 import org.apache.mina.core.future.ConnectFuture;
14 import org.apache.mina.core.future.IoFutureListener;
15 import org.apache.mina.core.service.IoHandler;
16 import org.apache.mina.core.session.IdleStatus;
17 import org.apache.mina.core.session.IoSession;
18 import org.apache.mina.filter.codec.ProtocolCodecFilter;
19 import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
20 import org.apache.mina.transport.socket.nio.NioSocketConnector;
21
22 import cz.cuni.amis.pogamut.base.communication.translator.event.IWorldChangeEvent;
23 import cz.cuni.amis.pogamut.base.communication.worldview.IWorldView;
24 import cz.cuni.amis.pogamut.base.communication.worldview.event.IWorldEvent;
25 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
26 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.Player;
27 import cz.cuni.amis.pogamut.ut2004.teamcomm.bot.UT2004TCClient;
28 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestCreateChannel;
29 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestDestroyChannel;
30 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestGetStatus;
31 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestJoinChannel;
32 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestLeaveChannel;
33 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestRegister;
34 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCInfoData;
35 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCInfoMessage;
36 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCMessage;
37 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRecipient;
38 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRequestMessage;
39 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.model.TCChannel;
40 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.model.TCTeam;
41 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.TCMinaServer;
42 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoBotJoined;
43 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoBotLeft;
44 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoRequestFailed;
45 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoRequestFailedException;
46 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoStatus;
47 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelBotJoined;
48 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelBotLeft;
49 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelCreated;
50 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelDestroyed;
51 import cz.cuni.amis.utils.ExceptionToString;
52 import cz.cuni.amis.utils.NullCheck;
53 import cz.cuni.amis.utils.flag.Flag;
54 import cz.cuni.amis.utils.flag.ImmutableFlag;
55 import cz.cuni.amis.utils.future.FutureWithListeners;
56 import cz.cuni.amis.utils.maps.HashMapMap;
57 import cz.cuni.amis.utils.token.IToken;
58
59 public class TCMinaClient implements IoHandler {
60
61 private static final int RETRY_REGISTER_PERIOD_SECS = 3;
62
63 private Object mutex = new Object();
64
65 private UT2004TCClient owner;
66
67 private IWorldView teamWorldView;
68
69 private UnrealId botId;
70
71 private int botTeam;
72
73 private InetSocketAddress address;
74
75 private Logger log;
76
77 private Flag<Boolean> connected = new Flag<Boolean>(false);
78
79 private Flag<Boolean> connecting = new Flag<Boolean>(false);
80
81 private IoFutureListener<ConnectFuture> connectionListener = new IoFutureListener<ConnectFuture>() {
82
83 @Override
84 public void operationComplete(ConnectFuture event) {
85 connected(event);
86 }
87 };
88
89 private Timer timer;
90
91 private NioSocketConnector ioConnector;
92
93 private ConnectFuture connectFuture;
94
95 private IoSession session;
96
97 private Set<UnrealId> allBots = new HashSet<UnrealId>();
98
99 private TCTeam team;
100
101 private int registerTries = 0;
102
103 public TCMinaClient(UT2004TCClient owner, InetSocketAddress connectToAddress, IWorldView teamWorldView, Logger log) {
104 this.owner = owner;
105 this.teamWorldView = teamWorldView;
106 this.botId = owner.getBotId();
107 this.botTeam = owner.getBotTeam();
108 this.address = connectToAddress;
109 NullCheck.check(this.address, "connectToAddress");
110 this.log = log;
111 NullCheck.check(this.log, "log");
112 }
113
114
115
116
117
118 public String getHost() {
119 return address.getHostName();
120 }
121
122 public int getPort() {
123 return address.getPort();
124 }
125
126
127
128
129
130 public IWorldView getWorldView() {
131 return teamWorldView;
132 }
133
134 public ImmutableFlag<Boolean> getConnected() {
135 return connected.getImmutable();
136 }
137
138 public ImmutableFlag<Boolean> getConnecting() {
139 return connecting.getImmutable();
140 }
141
142 public void connect() {
143 synchronized(mutex) {
144 if (connected.getFlag()) return;
145 if (connecting.getFlag()) return;
146 log.warning("Connecting to TC at " + getHost() + ":" + getPort() + " ...");
147 connecting.setFlag(true);
148 }
149
150 try {
151 ioConnector = new NioSocketConnector();
152
153 ioConnector.setHandler(this);
154
155 ioConnector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
156
157 connectFuture = ioConnector.connect(address);
158
159 connectFuture.addListener(connectionListener);
160 } catch (Exception e1) {
161 try {
162 connecting.setFlag(false);
163 } catch (Exception e2) {
164 }
165 }
166
167 }
168
169
170
171
172
173 public boolean isConnected(UnrealId botId) {
174 if (botId == null) return false;
175 if (!getConnected().getFlag()) return false;
176 return allBots.contains(botId);
177 }
178
179 public boolean isConnected(Player bot) {
180 if (bot == null) return false;
181 if (!getConnected().getFlag()) return false;
182 return allBots.contains(bot.getId());
183 }
184
185 public boolean isConnectedToMyTeam(UnrealId botId) {
186 if (botId == null) return false;
187 if (!getConnected().getFlag()) return false;
188 return team.getConnectedBots().contains(botId);
189 }
190
191 public boolean isConnectedToMyTeam(Player bot) {
192 if (bot == null) return false;
193 if (!getConnected().getFlag()) return false;
194 return team.getConnectedBots().contains(bot.getId());
195 }
196
197 public boolean isConnectedToChannel(UnrealId botId, int channelId) {
198 if (botId == null) return false;
199 if (!getConnected().getFlag()) return false;
200 TCChannel channel = team.getChannels().get(channelId);
201 if (channel == null) return false;
202 return channel.getConnectedBots().contains(botId);
203 }
204
205 public boolean isConnectedToChannel(Player bot, int channelId) {
206 if (bot == null) return false;
207 if (!getConnected().getFlag()) return false;
208 TCChannel channel = team.getChannels().get(channelId);
209 if (channel == null) return false;
210 return channel.getConnectedBots().contains(bot.getId());
211 }
212
213 public boolean isChannelExist(int channelId) {
214 if (!getConnected().getFlag()) return false;
215 TCChannel channel = team.getChannels().get(channelId);
216 if (channel == null) return false;
217 return true;
218 }
219
220
221
222
223
224 public Set<UnrealId> getConnectedAllBots() {
225 if (!getConnected().getFlag()) return new HashSet<UnrealId>();
226 return Collections.unmodifiableSet(allBots);
227 }
228
229
230
231
232
233 public Set<UnrealId> getConnectedTeamBots() {
234 if (!getConnected().getFlag()) return new HashSet<UnrealId>();
235 return Collections.unmodifiableSet(team.getConnectedBots());
236 }
237
238
239
240
241
242 public Set<UnrealId> getConnectedChannelBots(int channelId) {
243 if (!getConnected().getFlag()) return new HashSet<UnrealId>();
244 TCChannel channel = team.getChannels().get(channelId);
245 if (channel == null) return new HashSet<UnrealId>();
246 return Collections.unmodifiableSet(channel.getConnectedBots());
247 }
248
249
250
251
252
253
254
255
256 public TCTeam getTeam() {
257 synchronized(mutex) {
258 if (!getConnected().getFlag()) return null;
259 if (team == null) return null;
260 return team.clone();
261 }
262 }
263
264
265
266
267
268
269
270
271 public TCChannel getChannel(int channelId) {
272 synchronized(mutex) {
273 if (!getConnected().getFlag()) return null;
274 if (team == null) return null;
275 TCChannel channel = team.getChannels().get(channelId);
276 if (channel == null) return null;
277 return channel.clone();
278 }
279 }
280
281
282
283
284
285 public static class RequestFuture<T> extends FutureWithListeners<T> {
286
287 private TCRequestMessage message;
288
289 public RequestFuture(TCRequestMessage message) {
290 this.message = message;
291 }
292 }
293
294
295
296
297 private HashMapMap<IToken, Long, RequestFuture<?>> requestFutures = new HashMapMap<IToken, Long, RequestFuture<?>>();
298
299
300
301
302
303
304
305
306
307 public RequestFuture<TCInfoTeamChannelCreated> requestCreateChannel() {
308 TCRequestCreateChannel data = new TCRequestCreateChannel(owner.getSimTime());
309 TCRequestMessage message = new TCRequestMessage(botId, data);
310 RequestFuture<TCInfoTeamChannelCreated> future = new RequestFuture<TCInfoTeamChannelCreated>(message);
311 synchronized(mutex) {
312 if (!getConnected().getFlag()) return null;
313 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
314 session.write(message);
315
316 }
317 return future;
318 }
319
320
321
322
323
324
325 public RequestFuture<TCInfoTeamChannelDestroyed> requestDestroyChannel(int channelId) {
326 TCRequestDestroyChannel data = new TCRequestDestroyChannel(owner.getSimTime());
327 data.setChannelId(channelId);
328 TCRequestMessage message = new TCRequestMessage(botId, data);
329 RequestFuture<TCInfoTeamChannelDestroyed> future = new RequestFuture<TCInfoTeamChannelDestroyed>(message);
330 synchronized(mutex) {
331 if (!getConnected().getFlag()) return null;
332 TCChannel channel = team.getChannels().get(channelId);
333 if (channel == null) return null;
334 if (!channel.getCreator().equals(botId)) return null;
335 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
336 session.write(message);
337 }
338 return future;
339 }
340
341
342
343
344
345 public RequestFuture<TCInfoStatus> requestGetStatus() {
346 TCRequestGetStatus data = new TCRequestGetStatus(owner.getSimTime());
347 TCRequestMessage message = new TCRequestMessage(botId, data);
348 RequestFuture<TCInfoStatus> future = new RequestFuture<TCInfoStatus>(message);
349 synchronized(mutex) {
350 if (!getConnected().getFlag()) return null;
351 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
352 session.write(message);
353
354 }
355 return future;
356 }
357
358
359
360
361
362
363 public RequestFuture<TCInfoTeamChannelBotJoined> requestJoinChannel(int channelId) {
364 TCRequestJoinChannel data = new TCRequestJoinChannel(owner.getSimTime());
365 data.setChannelId(channelId);
366 TCRequestMessage message = new TCRequestMessage(botId, data);
367 RequestFuture<TCInfoTeamChannelBotJoined> future = new RequestFuture<TCInfoTeamChannelBotJoined>(message);
368 synchronized(mutex) {
369 if (!getConnected().getFlag()) return null;
370 TCChannel channel = team.getChannels().get(channelId);
371 if (channel == null) return null;
372 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
373 session.write(message);
374 }
375 return future;
376 }
377
378
379
380
381
382
383 public RequestFuture<TCInfoTeamChannelBotLeft> requestLeaveChannel(int channelId) {
384 TCRequestLeaveChannel data = new TCRequestLeaveChannel(owner.getSimTime());
385 data.setChannelId(channelId);
386 TCRequestMessage message = new TCRequestMessage(botId, data);
387 RequestFuture<TCInfoTeamChannelBotLeft> future = new RequestFuture<TCInfoTeamChannelBotLeft>(message);
388 synchronized(mutex) {
389 if (!getConnected().getFlag()) return null;
390 TCChannel channel = team.getChannels().get(channelId);
391 if (channel == null) return null;
392 requestFutures.put(data.getMessageType(), data.getRequestId(), future);
393 session.write(message);
394 }
395 return future;
396 }
397
398
399
400
401
402
403
404
405
406
407
408 public boolean sendToAllOthers(IToken messageType, Serializable data) {
409 return sendToAll(messageType, data, false);
410 }
411
412
413
414
415
416
417
418 public boolean sendToAll(IToken messageType, Serializable data) {
419 return sendToAll(messageType, data, true);
420 }
421
422
423
424
425
426
427
428
429 public boolean sendToAll(IToken messageType, Serializable data, boolean includeMe) {
430 if (messageType == null) return false;
431
432 TCMessage message = new TCMessage(botId, TCRecipient.GLOBAL, !includeMe, messageType, data, owner.getSimTime());
433
434 synchronized(mutex) {
435 if (!connected.getFlag() || session == null) return false;
436 try {
437 session.write(message);
438 } catch (Exception e) {
439 log.warning(ExceptionToString.process("Failed to sendToAll: " + data, e));
440 return false;
441 }
442 return true;
443 }
444 }
445
446
447
448
449
450
451
452 public boolean sendToTeamOthers(IToken messageType, Serializable data, boolean includeMe) {
453 return sendToTeam(messageType, data, false);
454 }
455
456
457
458
459
460
461
462 public boolean sendToTeam(IToken messageType, Serializable data) {
463 return sendToTeam(messageType, data, false);
464 }
465
466
467
468
469
470
471
472
473 public boolean sendToTeam(IToken messageType, Serializable data, boolean includeMe) {
474 if (messageType == null) return false;
475
476 TCMessage message = new TCMessage(botId, TCRecipient.TEAM, includeMe, messageType, data, owner.getSimTime());
477
478 synchronized(mutex) {
479 if (!connected.getFlag() || session == null) return false;
480 try {
481 session.write(message);
482 } catch (Exception e) {
483 log.warning(ExceptionToString.process("Failed to sendToTeam: " + data, e));
484 return false;
485 }
486 return true;
487 }
488 }
489
490
491
492
493
494
495
496
497
498
499 public boolean sendToChannelOthers(int channelId, IToken messageType, Serializable data) {
500 return sendToChannel(channelId, messageType, data, false);
501 }
502
503
504
505
506
507
508
509
510
511
512 public boolean sendToChannel(int channelId, IToken messageType, Serializable data) {
513 return sendToChannel(channelId, messageType, data, true);
514 }
515
516
517
518
519
520
521
522
523
524
525
526 public boolean sendToChannel(int channelId, IToken messageType, Serializable data, boolean includeMe) {
527 if (messageType == null) return false;
528
529 TCMessage message = new TCMessage(botId, TCRecipient.CHANNEL, includeMe, messageType, data, owner.getSimTime());
530 message.setChannelId(channelId);
531
532 synchronized(mutex) {
533 if (!connected.getFlag() || session == null) return false;
534 try {
535 session.write(message);
536 } catch (Exception e) {
537 log.warning(ExceptionToString.process("Failed to sendToChannel(" + channelId + "): " + data, e));
538 return false;
539 }
540 return true;
541 }
542 }
543
544
545
546
547
548
549
550
551 public boolean sendPrivate(UnrealId targetBotId, IToken messageType, Serializable data) {
552 if (messageType == null) return false;
553
554 TCMessage message = new TCMessage(botId, TCRecipient.PRIVATE, false, messageType, data, owner.getSimTime());
555 message.setTargetId(targetBotId);
556
557 synchronized(mutex) {
558 if (!connected.getFlag() || session == null) return false;
559 try {
560 session.write(message);
561 } catch (Exception e) {
562 log.warning(ExceptionToString.process("Failed to sendPrivate(" + targetBotId.getStringId() + "): " + data, e));
563 return false;
564 }
565 return true;
566 }
567 }
568
569
570
571
572
573 protected void connected(ConnectFuture event) {
574 log.info("Connected to TC at " + getHost() + ":" + getPort());
575
576 this.session = event.getSession();
577
578 connectFuture.removeListener(connectionListener);
579 connectFuture = null;
580
581 log.info("Sending REGISTER request, expecting TCInfoStatus reply...");
582
583 registerTries = 1;
584 sendRegisterRequest();
585 }
586
587
588 private void sendRegisterRequest() {
589 TCRequestRegister request = new TCRequestRegister(owner.getSimTime());
590 TCRequestMessage message = new TCRequestMessage(botId, request);
591 synchronized(mutex) {
592 session.write(message);
593 }
594 }
595
596
597
598
599
600 @Override
601 public void exceptionCaught(IoSession session, Throwable exception) throws Exception {
602 log.warning(ExceptionToString.process("TCMinaClient Exception", exception));
603 stop();
604 }
605
606 @Override
607 public void messageReceived(IoSession session, Object message) throws Exception {
608 if (message == null) {
609 log.warning("Invalid message: " + String.valueOf(message));
610 return;
611 }
612 if (!(message instanceof TCMessage)) {
613 log.warning("Invalid message: " + String.valueOf(message));
614 return;
615 }
616
617 TCMessage tcMessage = (TCMessage)message;
618
619 if (tcMessage.getSource() == null) {
620 log.warning("TCMessage.getSource() is NULL, cannot process: " + String.valueOf(message));
621 return;
622 }
623 if (tcMessage.getMessageType() == null) {
624 log.warning("TCMessage.getMessageType() is NULL, cannot process: " + String.valueOf(message));
625 return;
626 }
627 if (tcMessage.getTarget() == null) {
628 log.warning("TCMessage.getTarget() is NULL, cannot process the message: " + String.valueOf(message));
629 return;
630 }
631
632 switch(tcMessage.getTarget()) {
633 case GLOBAL:
634 case TEAM:
635 case CHANNEL:
636 case PRIVATE:
637 Serializable data;
638 try {
639 data = tcMessage.getMessage();
640 } catch (Exception e) {
641 log.warning(ExceptionToString.process("Invalid request data, failed to deserialize TCMessage.getMessage(): " + String.valueOf(tcMessage), e));
642 return;
643 }
644 botMessage(tcMessage, data);
645 return;
646 case TC_INFO:
647 if (!(tcMessage instanceof TCInfoMessage)) {
648 log.warning("TCMessage recipient is " + tcMessage.getTarget() + ", but the message is not TCInfoMessage: " + String.valueOf(tcMessage));
649 return;
650 }
651 TCInfoData infoData = null;
652 try {
653 infoData = (TCInfoData)tcMessage.getMessage();
654 } catch (Exception e) {
655 log.warning("Invalid request data, failed to deserialize TCMessage.getMessage() as TCInfoMessageData: " + String.valueOf(tcMessage));
656 return;
657 }
658 infoMessage((TCInfoMessage)tcMessage, infoData);
659 return;
660 case TC_REQUEST:
661 log.warning("Received TC_REQUEST message, cannot process: " + String.valueOf(message));
662 return;
663 }
664 }
665
666 @Override
667 public void messageSent(IoSession session, Object message) throws Exception {
668 }
669
670 @Override
671 public void sessionClosed(IoSession session) throws Exception {
672 synchronized(mutex) {
673 if (timer != null) {
674 timer.cancel();
675 timer = null;
676 }
677
678 session = null;
679
680 log.warning("TC Server connection closed.");
681
682 connected.setFlag(false);
683 connecting.setFlag(false);
684 }
685 }
686
687 @Override
688 public void sessionCreated(IoSession session) throws Exception {
689 }
690
691 @Override
692 public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
693 }
694
695 @Override
696 public void sessionOpened(IoSession session) throws Exception {
697 }
698
699
700
701
702
703 private void botMessage(TCMessage message, Serializable data) {
704 notify(message, data);
705 }
706
707 private void infoMessage(TCInfoMessage message, TCInfoData data) {
708 if (data.getMessageType() == TCInfoStatus.MESSAGE_TYPE) {
709 status(message, (TCInfoStatus)data);
710 } else
711 if (data.getMessageType() == TCInfoBotJoined.MESSAGE_TYPE) {
712 botJoined(message, (TCInfoBotJoined)data);
713 } else
714 if (data.getMessageType() == TCInfoBotLeft.MESSAGE_TYPE) {
715 botLeft(message, (TCInfoBotLeft)data);
716 } else
717 if (data.getMessageType() == TCInfoRequestFailed.MESSAGE_TYPE) {
718 requestFailed(message, (TCInfoRequestFailed)data);
719 } else
720 if (data.getMessageType() == TCInfoRequestFailed.MESSAGE_TYPE) {
721 status(message, (TCInfoStatus)data);
722 } else
723 if (data.getMessageType() == TCInfoTeamChannelBotJoined.MESSAGE_TYPE) {
724 channelBotJoined(message, (TCInfoTeamChannelBotJoined)data);
725 } else
726 if (data.getMessageType() == TCInfoTeamChannelBotLeft.MESSAGE_TYPE) {
727 channelBotLeft(message, (TCInfoTeamChannelBotLeft)data);
728 } else
729 if (data.getMessageType() == TCInfoTeamChannelCreated.MESSAGE_TYPE) {
730 channelCreated(message, (TCInfoTeamChannelCreated)data);
731 } else
732 if (data.getMessageType() == TCInfoTeamChannelDestroyed.MESSAGE_TYPE) {
733 channelDestroyed(message, (TCInfoTeamChannelDestroyed)data);
734 } else {
735 log.warning("Unhandled INFO message type: " + data.getMessageType().getToken());
736 }
737 }
738
739
740
741
742
743 private void botJoined(TCInfoMessage message, TCInfoBotJoined data) {
744 if (data.getBotId() == null) {
745 log.warning("TCInfoBotJoined.getBotId() is NULL!");
746 return;
747 }
748
749 log.info("Bot " + data.getBotId().getStringId() + " has joined TC.");
750
751 synchronized(mutex) {
752 allBots.add(data.getBotId());
753
754 if (team == null) return;
755
756 if (data.getTeam() != team.getTeam()) {
757 return;
758 }
759
760 team.getConnectedBots().add(data.getBotId());
761 }
762
763 notify(message, data);
764 }
765
766 private void botLeft(TCInfoMessage message, TCInfoBotLeft data) {
767 if (data.getBotId() == null) {
768 log.warning("TCInfoBotLeft.getBotId() is NULL!");
769 return;
770 }
771
772 log.warning("Bot " + data.getBotId().getStringId() + " has left TC.");
773
774 synchronized(mutex) {
775 allBots.add(data.getBotId());
776
777 if (team == null) return;
778
779 if (data.getTeam() != team.getTeam()) {
780 return;
781 }
782
783 team.getConnectedBots().remove(data.getBotId());
784 for (TCChannel channel : team.getChannels().values()) {
785 channel.getConnectedBots().remove(data.getBotId());
786 }
787 }
788
789 notify(message, data);
790 }
791
792 private void requestFailed(TCInfoMessage message, TCInfoRequestFailed data) {
793 if (connecting.getFlag()) {
794 log.warning("Failed to register, will retry in " + RETRY_REGISTER_PERIOD_SECS + " seconds...");
795 if (timer == null) timer = new Timer();
796 timer.schedule(new TimerTask() {
797 @Override
798 public void run() {
799 ++registerTries;
800 log.warning("Trying to register again (" + registerTries + ")...");
801 sendRegisterRequest();
802 }
803 }, RETRY_REGISTER_PERIOD_SECS * 1000);
804 return;
805 }
806
807
808
809
810 failRequestFuture(null, data);
811
812 notify(message, data);
813 }
814
815 private void status(TCInfoMessage message, TCInfoStatus data) {
816 log.info("Received TCInfoStatus message...");
817
818 synchronized(mutex) {
819 allBots = new HashSet<UnrealId>(data.getAllBots());
820 team = data.getTeam();
821
822 if (connecting.getFlag()) {
823 log.info("Connected to TC Server at " + address.getHostName() + ":" + address.getPort());
824
825 timer = null;
826
827 connected.setFlag(true);
828 connecting.setFlag(false);
829 } else {
830 requestFinishedUnsync(TCRequestGetStatus.MESSAGE_TYPE, data.getRequestId(), data);
831 }
832 }
833
834 notify(message, data);
835 }
836
837 private void channelBotJoined(TCInfoMessage message, TCInfoTeamChannelBotJoined data) {
838 if (data.getBotId() == null) {
839 log.warning("TCInfoTeamChannelBotJoined.getBotId() is NULL!");
840 }
841 synchronized(mutex) {
842 TCChannel channel = team.getChannels().get(data.getChannelId());
843 if (channel == null) {
844 log.warning("Bot " + data.getBotId().getStringId() + " has joined unknown channel " + data.getChannelId() + "! Requesting STATUS...");
845 requestGetStatus();
846 return;
847 }
848 channel.getConnectedBots().add(data.getBotId());
849
850 requestFinishedUnsync(TCRequestJoinChannel.MESSAGE_TYPE, data.getRequestId(), data);
851 }
852 notify(message, data);
853 }
854
855 private void channelBotLeft(TCInfoMessage message, TCInfoTeamChannelBotLeft data) {
856 if (data.getBotId() == null) {
857 log.warning("TCInfoTeamChannelBotLeft.getBotId() is NULL!");
858 }
859 synchronized(mutex) {
860 TCChannel channel = team.getChannels().get(data.getChannelId());
861 if (channel == null) {
862 log.warning("Bot " + data.getBotId().getStringId() + " has left unknown channel " + data.getChannelId() + "! Requesting STATUS...");
863 requestGetStatus();
864 return;
865 }
866 channel.getConnectedBots().remove(data.getBotId());
867
868 requestFinishedUnsync(TCRequestLeaveChannel.MESSAGE_TYPE, data.getRequestId(), data);
869 }
870 notify(message, data);
871 }
872
873 private void channelCreated(TCInfoMessage message, TCInfoTeamChannelCreated data) {
874 if (team == null) return;
875 if (data.getChannel() == null) {
876 log.warning("TCInfoTeamChannelCreated.getChannel() is NULL!");
877 return;
878 }
879 synchronized(mutex) {
880 team.getChannels().put(data.getChannel().getChannelId(), data.getChannel().clone());
881 requestFinishedUnsync(TCRequestCreateChannel.MESSAGE_TYPE, data.getRequestId(), data);
882 }
883 notify(message, data);
884 }
885
886 private void channelDestroyed(TCInfoMessage message, TCInfoTeamChannelDestroyed data) {
887 synchronized(mutex) {
888 if (team == null) return;
889 team.getChannels().remove(data.getChannelId());
890 requestFinishedUnsync(TCRequestDestroyChannel.MESSAGE_TYPE, data.getRequestId(), data);
891 }
892 notify(message, data);
893 }
894
895
896
897
898
899 @SuppressWarnings("rawtypes")
900 private void requestFinishedUnsync(IToken requestMessageType, long requestId, Object result) {
901 Map<Long, RequestFuture<?>> futures = requestFutures.get(requestMessageType);
902 if (futures == null) return;
903 RequestFuture requestFuture = futures.remove(requestId);
904 if (requestFuture == null) return;
905 requestFuture.setResult(result);
906 }
907
908
909
910
911
912 private void failRequestFuture(IToken requestMessageType, TCInfoRequestFailed data) {
913 synchronized(mutex) {
914 if (requestMessageType == null) {
915 for (IToken messageType : requestFutures.keySet()) {
916 Map<Long, RequestFuture<?>> futures = requestFutures.get(messageType);
917 RequestFuture<?> requestFuture = futures.remove(data.getRequestId());
918 if (requestFuture != null) {
919 requestFuture.computationException(new TCInfoRequestFailedException(requestFuture.message, data, log, this));
920 }
921 }
922 } else {
923 Map<Long, RequestFuture<?>> futures = requestFutures.get(requestMessageType);
924 if (futures == null) return;
925 RequestFuture<?> requestFuture = futures.remove(data.getRequestId());
926 if (requestFuture == null) return;
927 requestFuture.computationException(new TCInfoRequestFailedException(requestFuture.message, data, log, this));
928 }
929 }
930 }
931
932 private void notify(TCMessage message, Serializable data) {
933 if (data instanceof IWorldChangeEvent && data instanceof IWorldEvent) {
934 teamWorldView.notify((IWorldChangeEvent)data);
935 }
936 if (message instanceof IWorldChangeEvent && message instanceof IWorldEvent) {
937 teamWorldView.notify(message);
938 }
939 }
940
941
942
943
944
945 public void stop() {
946 synchronized(mutex) {
947 log.info("Stopping TCMinaClient!");
948
949 allBots.clear();
950 team = null;
951
952 if (timer != null) {
953 timer.cancel();
954 timer = null;
955 }
956
957 try {
958 if (session != null) {
959 session.close(true);
960 }
961 } catch (Exception e) {
962 }
963 session = null;
964
965 try {
966 connected.setFlag(false);
967 } catch (Exception e) {
968 }
969 try {
970 connecting.setFlag(false);
971 } catch (Exception e) {
972 }
973
974 for (IToken messageType : requestFutures.keySet()) {
975 Map<Long, RequestFuture<?>> futures = requestFutures.get(messageType);
976 for (RequestFuture<?> future : futures.values()) {
977 future.cancel(true);
978 }
979 }
980 requestFutures.clear();
981
982 log.info("TCMinaClient stopped!");
983 }
984 }
985
986 }