1 package cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server;
2
3 import java.io.IOException;
4 import java.net.InetSocketAddress;
5 import java.util.ArrayList;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.Map;
9 import java.util.Map.Entry;
10 import java.util.Set;
11 import java.util.logging.Logger;
12
13 import org.apache.mina.core.service.IoAcceptor;
14 import org.apache.mina.core.service.IoHandler;
15 import org.apache.mina.core.session.IdleStatus;
16 import org.apache.mina.core.session.IoSession;
17 import org.apache.mina.filter.codec.ProtocolCodecFilter;
18 import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
19 import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
20
21 import cz.cuni.amis.pogamut.unreal.communication.messages.UnrealId;
22 import cz.cuni.amis.pogamut.ut2004.communication.messages.gbinfomessages.PlayerMessage;
23 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestCreateChannel;
24 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestDestroyChannel;
25 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestGetStatus;
26 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestJoinChannel;
27 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestLeaveChannel;
28 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.client.messages.TCRequestRegister;
29 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCInfoMessage;
30 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCMessage;
31 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRecipient;
32 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRequestData;
33 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.messages.TCRequestMessage;
34 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.model.TCChannel;
35 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.model.TCTeam;
36 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoBotJoined;
37 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoBotLeft;
38 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoRequestFailed;
39 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoRequestFailureType;
40 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoStatus;
41 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelBotJoined;
42 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelBotLeft;
43 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelCreated;
44 import cz.cuni.amis.pogamut.ut2004.teamcomm.mina.server.messages.TCInfoTeamChannelDestroyed;
45 import cz.cuni.amis.pogamut.ut2004.teamcomm.server.UT2004TCServer;
46 import cz.cuni.amis.utils.ExceptionToString;
47 import cz.cuni.amis.utils.NullCheck;
48 import cz.cuni.amis.utils.exception.PogamutException;
49 import cz.cuni.amis.utils.flag.Flag;
50 import cz.cuni.amis.utils.flag.ImmutableFlag;
51 import cz.cuni.amis.utils.maps.LazyMap;
52
53 public class TCMinaServer implements IoHandler {
54
55 private UT2004TCServer owner;
56
57 private InetSocketAddress address;
58
59 private Logger log;
60
61 private Object mutex = new Object();
62
63 private Flag<Boolean> running = new Flag<Boolean>(false);
64
65 private IoAcceptor ioAcceptor;
66
67 private Map<Integer, TCTeam> teams = new LazyMap<Integer, TCTeam>() {
68
69 @Override
70 protected TCTeam create(Integer key) {
71 return new TCTeam(key);
72 }
73
74 };
75
76 private Map<UnrealId, PlayerMessage> registeredBots = new HashMap<UnrealId, PlayerMessage>();
77
78 private Map<UnrealId, IoSession> botSessions = new HashMap<UnrealId, IoSession>();
79
80 public TCMinaServer(UT2004TCServer owner, InetSocketAddress bindAddress, Logger log) {
81 this.owner = owner;
82 this.address = bindAddress;
83 NullCheck.check(this.address, "bindAddress");
84 this.log = log;
85 NullCheck.check(this.log, "log");
86 }
87
88
89
90
91
92
93
94
95
96
97 public void botLeft(UnrealId botId) {
98 if (registeredBots.containsKey(botId)) {
99 unregisterBot(botId);
100 }
101 }
102
103
104
105
106
107
108 public ImmutableFlag<Boolean> getRunning() {
109 return running.getImmutable();
110 }
111
112
113
114
115
116
117 public void start() throws PogamutException {
118 try {
119 synchronized(mutex) {
120 log.warning("Starting TCMinaServer!");
121 log.warning("Opening TC Socket: " + address.getHostName() + ":" + address.getPort());
122
123 ioAcceptor = new NioSocketAcceptor();
124
125 ioAcceptor.setHandler(this);
126
127 ioAcceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
128
129 try {
130 ioAcceptor.bind(address);
131 } catch (IOException e) {
132 stop();
133 throw new PogamutException("Failed to open server socket for TCMinaServer at " + address, e, log);
134 }
135
136 log.warning("TCMinaServer running!");
137 running.setFlag(true);
138 }
139 } catch (Exception e) {
140 log.severe(ExceptionToString.process("Failed to start TCMinaServer!", e));
141 stop();
142 }
143 }
144
145
146
147
148
149
150 public void stop() {
151 synchronized(mutex) {
152 log.warning("Stopping TCMinaServer!");
153 if (ioAcceptor != null) {
154 log.warning("Closing TCMinaServer Socket!");
155 try {
156 ioAcceptor.unbind();
157 } catch (Exception e) {
158 }
159 try {
160 ioAcceptor.dispose();
161 } catch (Exception e) {
162 ioAcceptor = null;
163 }
164 }
165
166 botSessions.clear();
167 registeredBots.clear();
168 teams.clear();
169
170 try {
171 running.setFlag(false);
172 } catch (Exception e) {
173 }
174
175 log.warning("TCMinaServer stopped!");
176 }
177 }
178
179
180
181
182
183 @Override
184 public void exceptionCaught(IoSession session, Throwable exception) throws Exception {
185 sessionError(session, ExceptionToString.process("TCMinaServer uncaught exception!", exception));
186 }
187
188 @Override
189 public void messageReceived(IoSession session, Object message) throws Exception {
190 if (message == null) {
191 sessionError(session, "NULL message received.");
192 return;
193 }
194 if (!(message instanceof TCMessage)) {
195 sessionError(session, "Received message that was not TCMessage.");
196 return;
197 }
198
199 TCMessage tcMessage = (TCMessage)message;
200
201 UnrealId source = tcMessage.getSource();
202 if (source == null) {
203 sessionError(session, "TCMessage.getSource() is NULL.");
204 return;
205 }
206
207 if (!ensureSession(source, session)) {
208
209 sessionError(session, "Failed to bind the session with the source.");
210 return;
211 }
212
213 if (tcMessage.getMessageType() == null) {
214 invalidRequest(session, TCInfoRequestFailureType.INVALID_MESSAGE_TYPE, "TCMessage.getMessageType() is NULL, cannot process: " + String.valueOf(message), -1, source);
215 return;
216 }
217 if (tcMessage.getTarget() == null) {
218 invalidRequest(session, TCInfoRequestFailureType.INVALID_RECIPIENT, "TCMessage.getTarget() is is NULL, cannot process the message: " + String.valueOf(message), -1, source);
219 return;
220 }
221
222 PlayerMessage player = owner.getPlayer(source);
223
224 if (registeredBots.containsKey(source)) {
225
226 if (player == null) {
227 log.warning(source.getStringId() + ": Cannot process the message as the bot has already left the UT2004 server!");
228 unregisterBot(source);
229 return;
230 }
231
232
233
234 } else {
235
236 if (player == null) {
237 invalidRequest(session, TCInfoRequestFailureType.UNKNOWN_BOT_UNREALID, "TCServer does not have info that this bot is connected to UT2004. If you are, please try again later (in about 5 secs).", -1, source);
238 return;
239 }
240
241 if (tcMessage.getTarget() == TCRecipient.TC_REQUEST && tcMessage.getMessageType() == TCRequestRegister.MESSAGE_TYPE && tcMessage instanceof TCRequestMessage) {
242 TCRequestRegister data;
243 try {
244 data = (TCRequestRegister)tcMessage.getMessage();
245 } catch (Exception e) {
246 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_DESERIALIZE_REQUEST_DATA, "Invalid request data, failed to deserialize TCMessage.getMessage() as TCRequestData.", -1, source);
247 return;
248 }
249
250 if (!registerBot(session, source, player, (TCRequestMessage)tcMessage, data)) {
251 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_REGISTER_THE_BOT, "TCServer could not register the bot bot with UnrealId '" + source.getStringId() + "'.", -1, source);
252 return;
253 }
254
255 return;
256 }
257
258 }
259
260 switch(tcMessage.getTarget()) {
261 case CHANNEL:
262 channelMessage(tcMessage, session, source, player);
263 return;
264 case GLOBAL:
265 globalMessage(tcMessage, session, source, player);
266 return;
267 case PRIVATE:
268 privateMessage(tcMessage, session, source, player);
269 return;
270 case TEAM:
271 teamMessage(tcMessage, session, source, player);
272 return;
273 case TC_INFO:
274 invalidRequest(session, TCInfoRequestFailureType.INVALID_RECIPIENT, "TCServer does not process TC_INFO messages, they are not meant to be sent to TCServer EVER!", -1, source);
275 return;
276 case TC_REQUEST:
277 TCRequestData requestData = null;
278 try {
279 if (tcMessage instanceof TCRequestMessage) {
280 requestData = (TCRequestData)tcMessage.getMessage();
281 } else {
282 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_DESERIALIZE_REQUEST_DATA, "TCMessage recipient is TC_REQUEST, but the message is not TCRequestMessage.", -1, source);
283 return;
284 }
285 } catch (Exception e) {
286 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_DESERIALIZE_REQUEST_DATA, "Invalid request data, failed to deserialize TCMessage.getMessage() as TCRequestData.", -1, source);
287 return;
288 }
289 requestMessage((TCRequestMessage)tcMessage, requestData, session, source, player);
290 return;
291 }
292 }
293
294 @Override
295 public void messageSent(IoSession session, Object message) throws Exception {
296 }
297
298 @Override
299 public void sessionClosed(IoSession session) throws Exception {
300 }
301
302 @Override
303 public void sessionCreated(IoSession session) throws Exception {
304 }
305
306 @Override
307 public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
308 }
309
310 @Override
311 public void sessionOpened(IoSession session) throws Exception {
312 }
313
314
315
316
317
318 private void globalMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
319 if (message.isExcludeMyselfIfApplicable()) {
320 sendGlobalExcept(message, source);
321 } else {
322 sendGlobalExcept(message, null);
323 }
324 }
325
326 private void teamMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
327 if (message.isExcludeMyselfIfApplicable()) {
328 sendTeamExcept(message, player.getTeam(), source);
329 } else {
330 sendTeamExcept(message, player.getTeam(), null);
331 }
332 }
333
334 private void channelMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
335 if (message.isExcludeMyselfIfApplicable()) {
336 sendChannelExcept(message, player.getTeam(), message.getChannelId(), source);
337 } else {
338 sendChannelExcept(message, player.getTeam(), message.getChannelId(), null);
339 }
340 }
341
342 private void privateMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
343 sendPrivate(message, message.getTargetId());
344 }
345
346 private void requestMessage(TCRequestMessage message, TCRequestData data, IoSession session, UnrealId source, PlayerMessage player) {
347 if (data == null) {
348 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_DESERIALIZE_REQUEST_DATA, "Request data are NULL", -1, source);
349 return;
350 }
351
352 if (data instanceof TCRequestCreateChannel) {
353 requestCreateChannel(message, (TCRequestCreateChannel)data, session, source, player);
354 } else
355 if (data instanceof TCRequestDestroyChannel) {
356 requestDestroyChannel(message, (TCRequestDestroyChannel)data, session, source, player);
357 } else
358 if (data instanceof TCRequestGetStatus) {
359 requestGetStatus(message, (TCRequestGetStatus)data, session, source, player);
360 } else
361 if (data instanceof TCRequestJoinChannel) {
362 requestJoinChannel(message, (TCRequestJoinChannel)data, session, source, player);
363 } else
364 if (data instanceof TCRequestLeaveChannel) {
365 requestLeaveChannel(message, (TCRequestLeaveChannel)data, session, source, player);
366 } else
367 if (data instanceof TCRequestRegister) {
368 throw new RuntimeException("Should not reach here!");
369 } else {
370 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_PROCESS_REQUEST, "Unknown request.", data.getRequestId(), source);
371 }
372 }
373
374
375
376
377
378 private void requestCreateChannel(TCRequestMessage message, TCRequestCreateChannel data, IoSession session, UnrealId source, PlayerMessage player) {
379 synchronized(mutex) {
380 if (!running.getFlag()) return;
381 TCTeam team = teams.get(player.getTeam());
382 synchronized(team) {
383 Map<Integer, TCChannel> channels = team.getChannels();
384 synchronized(channels) {
385 int newChannelId = team.getNextChannelId();
386 TCChannel newChannel = new TCChannel();
387 newChannel.setChannelId(newChannelId);
388 newChannel.setCreator(source);
389 newChannel.getConnectedBots().add(source);
390 channels.put(newChannelId, newChannel);
391
392 TCInfoTeamChannelCreated infoDataTeam = new TCInfoTeamChannelCreated(-1, owner.getSimTime());
393 infoDataTeam.setChannel(newChannel.clone());
394 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
395 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
396
397 TCInfoTeamChannelCreated infoDataRequestee = new TCInfoTeamChannelCreated(data.getRequestId(), owner.getSimTime());
398 infoDataRequestee.setChannel(newChannel.clone());
399 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
400 sendPrivate(infoMessageRequestee, source);
401 }
402 }
403 }
404 }
405
406 private void requestDestroyChannel(TCRequestMessage message, TCRequestDestroyChannel data, IoSession session, UnrealId source, PlayerMessage player) {
407 synchronized(mutex) {
408 if (!running.getFlag()) return;
409 TCTeam team = teams.get(player.getTeam());
410 synchronized(team) {
411 Map<Integer, TCChannel> channels = team.getChannels();
412 synchronized(channels) {
413 TCChannel channel = channels.get(data.getChannelId());
414 if (channel == null) {
415 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "Cannot destroy team " + player.getTeam() + " channel " + data.getChannelId() + " as it does not exist.", data.getRequestId(), source);
416 return;
417 }
418 if (channel.getCreator() != source) {
419 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_NOT_OWNED_BY_YOU, "Cannot destroy team " + player.getTeam() + " channel " + data.getChannelId() + " as it is not OWNED by you!", data.getRequestId(), source);
420 return;
421 }
422
423 channels.remove(data.getChannelId());
424
425 TCInfoTeamChannelDestroyed infoDataTeam = new TCInfoTeamChannelDestroyed(-1, owner.getSimTime());
426 infoDataTeam.setChannelId(data.getChannelId());
427 infoDataTeam.setDestroyer(source);
428 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
429 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
430
431 TCInfoTeamChannelDestroyed infoDataRequestee = new TCInfoTeamChannelDestroyed(data.getRequestId(), owner.getSimTime());
432 infoDataRequestee.setChannelId(data.getChannelId());
433 infoDataRequestee.setDestroyer(source);
434 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
435 sendPrivate(infoMessageRequestee, source);
436 }
437 }
438 }
439 }
440
441 private void requestGetStatus(TCRequestMessage message, TCRequestGetStatus data, IoSession session, UnrealId source, PlayerMessage player) {
442 TCInfoStatus infoData = new TCInfoStatus(data.getRequestId(), owner.getSimTime());
443
444 synchronized(registeredBots) {
445 infoData.setAllBots(new ArrayList<UnrealId>(registeredBots.keySet()));
446 }
447
448 TCTeam team = teams.get(player.getTeam());
449 synchronized(team) {
450 infoData.setTeam(team.clone());
451 }
452
453 TCInfoMessage infoMessage = new TCInfoMessage(infoData);
454
455 sendPrivate(infoMessage, source);
456 }
457
458 private void requestJoinChannel(TCRequestMessage message, TCRequestJoinChannel data, IoSession session, UnrealId source, PlayerMessage player) {
459 synchronized(mutex) {
460 if (!running.getFlag()) return;
461 TCTeam team = teams.get(player.getTeam());
462
463 TCChannel channel = team.getChannels().get(data.getChannelId());
464
465 if (channel == null) {
466 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "You cannot join team " + player.getTeam() + " channel " + data.getChannelId() + " as it does not exist.", data.getRequestId(), source);
467 return;
468 }
469
470 if (channel.getConnectedBots().contains(source)) {
471 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "You are already coonnected to team " + player.getTeam() + " channel " + data.getChannelId() + ".", data.getRequestId(), source);
472 return;
473 }
474
475 synchronized(channel) {
476 channel.getConnectedBots().add(source);
477
478 TCInfoTeamChannelBotJoined infoDataTeam = new TCInfoTeamChannelBotJoined(-1, owner.getSimTime());
479 infoDataTeam.setChannelId(data.getChannelId());
480 infoDataTeam.setBotId(source);
481 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
482 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
483
484 TCInfoTeamChannelBotJoined infoDataRequestee = new TCInfoTeamChannelBotJoined(data.getRequestId(), owner.getSimTime());
485 infoDataRequestee.setChannelId(data.getChannelId());
486 infoDataRequestee.setBotId(source);
487 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
488 sendPrivate(infoMessageRequestee, source);
489 }
490 }
491 }
492
493 private void requestLeaveChannel(TCRequestMessage message, TCRequestLeaveChannel data, IoSession session, UnrealId source, PlayerMessage player) {
494 synchronized(mutex) {
495 if (!running.getFlag()) return;
496 TCTeam team = teams.get(player.getTeam());
497
498 TCChannel channel = team.getChannels().get(data.getChannelId());
499
500 if (channel == null) {
501 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "You cannot leave team " + player.getTeam() + " channel " + data.getChannelId() + " as it does not exist.", data.getRequestId(), source);
502 return;
503 }
504
505 if (!channel.getConnectedBots().contains(source)) {
506 invalidRequest(session, TCInfoRequestFailureType.NOT_CONNECTED_TO_CHANNEL, "You are not coonnected to team " + player.getTeam() + " channel " + data.getChannelId() + ".", data.getRequestId(), source);
507 return;
508 }
509
510 synchronized(channel) {
511 channel.getConnectedBots().remove(source);
512
513 TCInfoTeamChannelBotLeft infoDataTeam = new TCInfoTeamChannelBotLeft(-1, owner.getSimTime());
514 infoDataTeam.setChannelId(data.getChannelId());
515 infoDataTeam.setBotId(source);
516 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
517 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
518
519 TCInfoTeamChannelBotLeft infoDataRequestee = new TCInfoTeamChannelBotLeft(data.getRequestId(), owner.getSimTime());
520 infoDataRequestee.setChannelId(data.getChannelId());
521 infoDataRequestee.setBotId(source);
522 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
523 sendPrivate(infoMessageRequestee, source);
524 }
525 }
526 }
527
528
529
530
531
532 private void sendGlobalExcept(TCMessage message, UnrealId except) {
533 synchronized(botSessions) {
534 for (Entry<UnrealId, IoSession> botSessionEntry : botSessions.entrySet()) {
535 if (botSessionEntry.getKey() == except) continue;
536 synchronized(botSessionEntry.getValue()) {
537 botSessionEntry.getValue().write(message);
538 }
539 }
540 }
541 }
542
543 private void sendTeamExcept(TCMessage message, int teamNum, UnrealId except) {
544 TCTeam team = teams.get(teamNum);
545 if (team == null) return;
546 synchronized(team) {
547 synchronized(team.getConnectedBots()) {
548 for (UnrealId target : team.getConnectedBots()) {
549 if (target == except) continue;
550 IoSession targetSession = botSessions.get(target);
551 synchronized(targetSession) {
552 targetSession.write(message);
553 }
554 }
555 }
556 }
557 }
558
559 private void sendChannelExcept(TCMessage message, int teamNum, int channelId, UnrealId except) {
560 TCTeam team = teams.get(teamNum);
561 if (team == null) return;
562 TCChannel channel = team.getChannels().get(channelId);
563 if (channel == null) return;
564 synchronized(channel) {
565 synchronized(channel.getConnectedBots()) {
566 for (UnrealId target : channel.getConnectedBots()) {
567 if (target == except) continue;
568 IoSession targetSession = botSessions.get(target);
569 synchronized(targetSession) {
570 targetSession.write(message);
571 }
572 }
573 }
574 }
575 }
576
577 private void sendPrivate(TCMessage message, UnrealId target) {
578 IoSession targetSession = botSessions.get(target);
579 if (targetSession != null) {
580 synchronized(targetSession) {
581 targetSession.write(message);
582 }
583 }
584 }
585
586 private void sendInfo(TCInfoMessage message, IoSession session) {
587 synchronized(session) {
588 session.write(message);
589 }
590 }
591
592
593
594
595
596
597
598
599
600
601
602
603 private boolean ensureSession(UnrealId source, IoSession session) {
604 if (source == null) {
605 return false;
606 }
607 if (session == null) {
608 return false;
609 }
610 IoSession existing = botSessions.get(source);
611 if (existing == null) {
612
613 synchronized(mutex) {
614 if (!running.getFlag()) return false;
615 existing = botSessions.get(source);
616 if (existing == null) {
617 log.warning(source.getStringId() + ": Binding new session for this bot.");
618 synchronized(botSessions) {
619 botSessions.put(source, session);
620 }
621 return true;
622 }
623 }
624 }
625
626 if (existing != session) {
627 return false;
628 } else {
629 return true;
630 }
631 }
632
633
634
635
636
637
638
639
640
641
642
643 private boolean registerBot(IoSession session, UnrealId source, PlayerMessage player, TCRequestMessage tcMessage, TCRequestRegister registerData) {
644 TCTeam team = null;
645 TCInfoStatus data = null;
646
647 synchronized(mutex) {
648 if (!running.getFlag()) return false;
649
650 log.warning(source.getStringId() + ": Registering this bot for team " + player.getTeam());
651
652 team = teams.get(player.getTeam());
653
654 synchronized(team) {
655 synchronized(team.getConnectedBots()) {
656 team.getConnectedBots().add(source);
657 }
658 }
659
660 synchronized(registeredBots) {
661 registeredBots.put(source, player);
662 }
663
664
665 {
666 log.info(source.getStringId() + ": Bot joined the TC, notifying connected bots.");
667
668 TCInfoBotJoined dataJoined = new TCInfoBotJoined(-1, owner.getSimTime());
669 dataJoined.setBotId(source);
670 dataJoined.setTeam(player.getTeam());
671
672 TCInfoMessage messageJoined = new TCInfoMessage(dataJoined);
673 sendGlobalExcept(messageJoined, source);
674 }
675
676
677 log.info(source.getStringId() + ": Sending initial TCInfoStatus to this newly joined bot.");
678 data = new TCInfoStatus(-1, owner.getSimTime());
679 synchronized(registeredBots) {
680 data.setAllBots(new ArrayList<UnrealId>(registeredBots.keySet()));
681 }
682 synchronized(team) {
683 data.setTeam(team.clone());
684 }
685
686 TCInfoMessage message = new TCInfoMessage(data);
687
688 sendInfo(message, session);
689
690 return true;
691 }
692 }
693
694 private void unregisterBot(UnrealId botId) {
695 if (botId == null) return;
696
697 synchronized(mutex) {
698
699 if (!running.getFlag()) return;
700 if (!registeredBots.containsKey(botId)) return;
701
702 log.warning(botId.getStringId() + ": Unregistering bot.");
703
704 PlayerMessage player;
705 synchronized(registeredBots) {
706 player = registeredBots.remove(botId);
707 }
708
709 if (player == null) {
710 log.severe(botId.getStringId() + ": Could not FULLY unregister bot as we do not have PlayerMessage for it! Data structures corrupted.");
711 } else {
712 TCTeam team = teams.get(player.getTeam());
713 if (team == null) {
714 log.severe(botId.getStringId() + ": Could not FULLY unregister bot as we do not have TCTeam[" + player.getTeam() + "] for it!");
715 } else {
716 synchronized(team) {
717 team.getConnectedBots().remove(botId);
718 Set<TCChannel> toRemove = new HashSet<TCChannel>();
719 synchronized(team.getChannels()) {
720 for (TCChannel channel : team.getChannels().values()) {
721 if (channel.getCreator().equals(botId)) {
722 toRemove.add(channel);
723 } else {
724 synchronized(channel) {
725 channel.getConnectedBots().remove(botId);
726 }
727 }
728 }
729
730 for (TCChannel channel : toRemove) {
731 team.getChannels().remove(channel.getChannelId());
732
733
734 TCInfoTeamChannelDestroyed infoDataTeam = new TCInfoTeamChannelDestroyed(-1, owner.getSimTime());
735 infoDataTeam.setChannelId(channel.getChannelId());
736 infoDataTeam.setDestroyer(botId);
737 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
738 sendTeamExcept(infoMessageTeam, player.getTeam(), botId);
739 }
740 }
741 }
742 }
743 }
744
745 synchronized(botSessions) {
746 IoSession session = botSessions.remove(botId);
747 if (session == null) {
748 log.severe(botId.getStringId() + ": Could not FULLY unregister bot as we do not have IoSession for it! Data structures corrupted?");
749 } else {
750 session.close(true);
751 }
752 }
753
754 TCInfoBotLeft infoData = new TCInfoBotLeft(-1, owner.getSimTime());
755 infoData.setBotId(botId);
756 infoData.setTeam(player == null ? -1 : player.getTeam());
757 TCInfoMessage infoMessage = new TCInfoMessage(infoData);
758 sendGlobalExcept(infoMessage, botId);
759 }
760 }
761
762
763
764
765
766
767
768
769
770
771
772
773 private void invalidRequest(IoSession session, TCInfoRequestFailureType failureType, String reason, long requestId, UnrealId source) {
774 if (session == null) return;
775
776 if (failureType == null) failureType = TCInfoRequestFailureType.GENERIC_FAILURE;
777 if (reason == null || reason.isEmpty()) reason = "No info.";
778
779 log.warning((source == null ? "" : source.getStringId()) + ": InvalidRequest[" + failureType + "] " + reason);
780
781 TCInfoRequestFailed data = new TCInfoRequestFailed(requestId, owner.getSimTime());
782 data.setFailureType(failureType);
783
784 TCInfoMessage message = new TCInfoMessage(data);
785
786 try {
787 synchronized(session) {
788 session.write(message);
789 }
790 } catch (Exception e) {
791 if (running.getFlag()) {
792 log.warning(ExceptionToString.process(source.getStringId() + ": Failed to send message to the bot: " + message, e));
793 }
794 }
795 }
796
797 private void sessionError(IoSession session, String reason) {
798 if (session == null) return;
799
800 synchronized(mutex) {
801
802 if (!running.getFlag()) return;
803
804 log.warning("SessionError: " + reason);
805
806 UnrealId source = null;
807
808 synchronized(botSessions) {
809 for (Entry<UnrealId, IoSession> entry : botSessions.entrySet()) {
810 if (entry.getValue() == session) {
811 source = entry.getKey();
812 break;
813 }
814 }
815 }
816
817 if (source != null) {
818 botLeft(source);
819 } else {
820 synchronized(session) {
821 try {
822 session.close(true);
823 } catch (Exception e) {
824 log.warning(ExceptionToString.process("Could not close the session... ???", e));
825 }
826 }
827 }
828
829 }
830
831 }
832
833 }