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 sendGlobalExcept(message, source);
320 }
321
322 private void teamMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
323 sendTeamExcept(message, player.getTeam(), source);
324 }
325
326 private void channelMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
327 sendChannelExcept(message, player.getTeam(), message.getChannelId(), source);
328 }
329
330 private void privateMessage(TCMessage message, IoSession session, UnrealId source, PlayerMessage player) {
331 sendPrivate(message, message.getTargetId());
332 }
333
334 private void requestMessage(TCRequestMessage message, TCRequestData data, IoSession session, UnrealId source, PlayerMessage player) {
335 if (data == null) {
336 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_DESERIALIZE_REQUEST_DATA, "Request data are NULL", -1, source);
337 return;
338 }
339
340 if (data instanceof TCRequestCreateChannel) {
341 requestCreateChannel(message, (TCRequestCreateChannel)data, session, source, player);
342 } else
343 if (data instanceof TCRequestDestroyChannel) {
344 requestDestroyChannel(message, (TCRequestDestroyChannel)data, session, source, player);
345 } else
346 if (data instanceof TCRequestGetStatus) {
347 requestGetStatus(message, (TCRequestGetStatus)data, session, source, player);
348 } else
349 if (data instanceof TCRequestJoinChannel) {
350 requestJoinChannel(message, (TCRequestJoinChannel)data, session, source, player);
351 } else
352 if (data instanceof TCRequestLeaveChannel) {
353 requestLeaveChannel(message, (TCRequestLeaveChannel)data, session, source, player);
354 } else
355 if (data instanceof TCRequestRegister) {
356 throw new RuntimeException("Should not reach here!");
357 } else {
358 invalidRequest(session, TCInfoRequestFailureType.FAILED_TO_PROCESS_REQUEST, "Unknown request.", data.getRequestId(), source);
359 }
360 }
361
362
363
364
365
366 private void requestCreateChannel(TCRequestMessage message, TCRequestCreateChannel data, IoSession session, UnrealId source, PlayerMessage player) {
367 synchronized(mutex) {
368 if (!running.getFlag()) return;
369 TCTeam team = teams.get(player.getTeam());
370 synchronized(team) {
371 Map<Integer, TCChannel> channels = team.getChannels();
372 synchronized(channels) {
373 int newChannelId = team.getNextChannelId();
374 TCChannel newChannel = new TCChannel();
375 newChannel.setChannelId(newChannelId);
376 newChannel.setCreator(source);
377 newChannel.getConnectedBots().add(source);
378 channels.put(newChannelId, newChannel);
379
380 TCInfoTeamChannelCreated infoDataTeam = new TCInfoTeamChannelCreated(-1, owner.getSimTime());
381 infoDataTeam.setChannel(newChannel.clone());
382 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
383 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
384
385 TCInfoTeamChannelCreated infoDataRequestee = new TCInfoTeamChannelCreated(data.getRequestId(), owner.getSimTime());
386 infoDataRequestee.setChannel(newChannel.clone());
387 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
388 sendPrivate(infoMessageRequestee, source);
389 }
390 }
391 }
392 }
393
394 private void requestDestroyChannel(TCRequestMessage message, TCRequestDestroyChannel data, IoSession session, UnrealId source, PlayerMessage player) {
395 synchronized(mutex) {
396 if (!running.getFlag()) return;
397 TCTeam team = teams.get(player.getTeam());
398 synchronized(team) {
399 Map<Integer, TCChannel> channels = team.getChannels();
400 synchronized(channels) {
401 TCChannel channel = channels.get(data.getChannelId());
402 if (channel == null) {
403 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "Cannot destroy team " + player.getTeam() + " channel " + data.getChannelId() + " as it does not exist.", data.getRequestId(), source);
404 return;
405 }
406 if (channel.getCreator() != source) {
407 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);
408 return;
409 }
410
411 channels.remove(data.getChannelId());
412
413 TCInfoTeamChannelDestroyed infoDataTeam = new TCInfoTeamChannelDestroyed(-1, owner.getSimTime());
414 infoDataTeam.setChannelId(data.getChannelId());
415 infoDataTeam.setDestroyer(source);
416 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
417 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
418
419 TCInfoTeamChannelDestroyed infoDataRequestee = new TCInfoTeamChannelDestroyed(data.getRequestId(), owner.getSimTime());
420 infoDataRequestee.setChannelId(data.getChannelId());
421 infoDataRequestee.setDestroyer(source);
422 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
423 sendPrivate(infoMessageRequestee, source);
424 }
425 }
426 }
427 }
428
429 private void requestGetStatus(TCRequestMessage message, TCRequestGetStatus data, IoSession session, UnrealId source, PlayerMessage player) {
430 TCInfoStatus infoData = new TCInfoStatus(data.getRequestId(), owner.getSimTime());
431
432 synchronized(registeredBots) {
433 infoData.setAllBots(new ArrayList<UnrealId>(registeredBots.keySet()));
434 }
435
436 TCTeam team = teams.get(player.getTeam());
437 synchronized(team) {
438 infoData.setTeam(team.clone());
439 }
440
441 TCInfoMessage infoMessage = new TCInfoMessage(infoData);
442
443 sendPrivate(infoMessage, source);
444 }
445
446 private void requestJoinChannel(TCRequestMessage message, TCRequestJoinChannel data, IoSession session, UnrealId source, PlayerMessage player) {
447 synchronized(mutex) {
448 if (!running.getFlag()) return;
449 TCTeam team = teams.get(player.getTeam());
450
451 TCChannel channel = team.getChannels().get(data.getChannelId());
452
453 if (channel == null) {
454 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "You cannot join team " + player.getTeam() + " channel " + data.getChannelId() + " as it does not exist.", data.getRequestId(), source);
455 return;
456 }
457
458 if (channel.getConnectedBots().contains(source)) {
459 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "You are already coonnected to team " + player.getTeam() + " channel " + data.getChannelId() + ".", data.getRequestId(), source);
460 return;
461 }
462
463 synchronized(channel) {
464 channel.getConnectedBots().add(source);
465
466 TCInfoTeamChannelBotJoined infoDataTeam = new TCInfoTeamChannelBotJoined(-1, owner.getSimTime());
467 infoDataTeam.setChannelId(data.getChannelId());
468 infoDataTeam.setBotId(source);
469 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
470 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
471
472 TCInfoTeamChannelBotJoined infoDataRequestee = new TCInfoTeamChannelBotJoined(data.getRequestId(), owner.getSimTime());
473 infoDataRequestee.setChannelId(data.getChannelId());
474 infoDataRequestee.setBotId(source);
475 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
476 sendPrivate(infoMessageRequestee, source);
477 }
478 }
479 }
480
481 private void requestLeaveChannel(TCRequestMessage message, TCRequestLeaveChannel data, IoSession session, UnrealId source, PlayerMessage player) {
482 synchronized(mutex) {
483 if (!running.getFlag()) return;
484 TCTeam team = teams.get(player.getTeam());
485
486 TCChannel channel = team.getChannels().get(data.getChannelId());
487
488 if (channel == null) {
489 invalidRequest(session, TCInfoRequestFailureType.CHANNEL_DOES_NOT_EXIST, "You cannot leave team " + player.getTeam() + " channel " + data.getChannelId() + " as it does not exist.", data.getRequestId(), source);
490 return;
491 }
492
493 if (!channel.getConnectedBots().contains(source)) {
494 invalidRequest(session, TCInfoRequestFailureType.NOT_CONNECTED_TO_CHANNEL, "You are not coonnected to team " + player.getTeam() + " channel " + data.getChannelId() + ".", data.getRequestId(), source);
495 return;
496 }
497
498 synchronized(channel) {
499 channel.getConnectedBots().remove(source);
500
501 TCInfoTeamChannelBotLeft infoDataTeam = new TCInfoTeamChannelBotLeft(-1, owner.getSimTime());
502 infoDataTeam.setChannelId(data.getChannelId());
503 infoDataTeam.setBotId(source);
504 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
505 sendTeamExcept(infoMessageTeam, player.getTeam(), source);
506
507 TCInfoTeamChannelBotLeft infoDataRequestee = new TCInfoTeamChannelBotLeft(data.getRequestId(), owner.getSimTime());
508 infoDataRequestee.setChannelId(data.getChannelId());
509 infoDataRequestee.setBotId(source);
510 TCInfoMessage infoMessageRequestee = new TCInfoMessage(infoDataRequestee);
511 sendPrivate(infoMessageRequestee, source);
512 }
513 }
514 }
515
516
517
518
519
520 private void sendGlobalExcept(TCMessage message, UnrealId except) {
521 synchronized(botSessions) {
522 for (Entry<UnrealId, IoSession> botSessionEntry : botSessions.entrySet()) {
523 if (botSessionEntry.getKey() == except) continue;
524 synchronized(botSessionEntry.getValue()) {
525 botSessionEntry.getValue().write(message);
526 }
527 }
528 }
529 }
530
531 private void sendTeamExcept(TCMessage message, int teamNum, UnrealId except) {
532 TCTeam team = teams.get(teamNum);
533 if (team == null) return;
534 synchronized(team) {
535 synchronized(team.getConnectedBots()) {
536 for (UnrealId target : team.getConnectedBots()) {
537 if (target == except) continue;
538 IoSession targetSession = botSessions.get(target);
539 synchronized(targetSession) {
540 targetSession.write(message);
541 }
542 }
543 }
544 }
545 }
546
547 private void sendChannelExcept(TCMessage message, int teamNum, int channelId, UnrealId except) {
548 TCTeam team = teams.get(teamNum);
549 if (team == null) return;
550 TCChannel channel = team.getChannels().get(channelId);
551 if (channel == null) return;
552 synchronized(channel) {
553 synchronized(channel.getConnectedBots()) {
554 for (UnrealId target : channel.getConnectedBots()) {
555 if (target == except) continue;
556 IoSession targetSession = botSessions.get(target);
557 synchronized(targetSession) {
558 targetSession.write(message);
559 }
560 }
561 }
562 }
563 }
564
565 private void sendPrivate(TCMessage message, UnrealId target) {
566 IoSession targetSession = botSessions.get(target);
567 if (targetSession != null) {
568 synchronized(targetSession) {
569 targetSession.write(message);
570 }
571 }
572 }
573
574 private void sendInfo(TCInfoMessage message, IoSession session) {
575 synchronized(session) {
576 session.write(message);
577 }
578 }
579
580
581
582
583
584
585
586
587
588
589
590
591 private boolean ensureSession(UnrealId source, IoSession session) {
592 if (source == null) {
593 return false;
594 }
595 if (session == null) {
596 return false;
597 }
598 IoSession existing = botSessions.get(source);
599 if (existing == null) {
600
601 synchronized(mutex) {
602 if (!running.getFlag()) return false;
603 existing = botSessions.get(source);
604 if (existing == null) {
605 log.warning(source.getStringId() + ": Binding new session for this bot.");
606 synchronized(botSessions) {
607 botSessions.put(source, session);
608 }
609 return true;
610 }
611 }
612 }
613
614 if (existing != session) {
615 return false;
616 } else {
617 return true;
618 }
619 }
620
621
622
623
624
625
626
627
628
629
630
631 private boolean registerBot(IoSession session, UnrealId source, PlayerMessage player, TCRequestMessage tcMessage, TCRequestRegister registerData) {
632 TCTeam team = null;
633 TCInfoStatus data = null;
634
635 synchronized(mutex) {
636 if (!running.getFlag()) return false;
637
638 log.warning(source.getStringId() + ": Registering this bot for team " + player.getTeam());
639
640 team = teams.get(player.getTeam());
641
642 synchronized(team) {
643 synchronized(team.getConnectedBots()) {
644 team.getConnectedBots().add(source);
645 }
646 }
647
648 synchronized(registeredBots) {
649 registeredBots.put(source, player);
650 }
651
652
653 {
654 log.info(source.getStringId() + ": Bot joined the TC, notifying connected bots.");
655
656 TCInfoBotJoined dataJoined = new TCInfoBotJoined(-1, owner.getSimTime());
657 dataJoined.setBotId(source);
658 dataJoined.setTeam(player.getTeam());
659
660 TCInfoMessage messageJoined = new TCInfoMessage(dataJoined);
661 sendGlobalExcept(messageJoined, source);
662 }
663
664
665 log.info(source.getStringId() + ": Sending initial TCInfoStatus to this newly joined bot.");
666 data = new TCInfoStatus(-1, owner.getSimTime());
667 synchronized(registeredBots) {
668 data.setAllBots(new ArrayList<UnrealId>(registeredBots.keySet()));
669 }
670 synchronized(team) {
671 data.setTeam(team.clone());
672 }
673
674 TCInfoMessage message = new TCInfoMessage(data);
675
676 sendInfo(message, session);
677
678 return true;
679 }
680 }
681
682 private void unregisterBot(UnrealId botId) {
683 if (botId == null) return;
684
685 synchronized(mutex) {
686
687 if (!running.getFlag()) return;
688 if (!registeredBots.containsKey(botId)) return;
689
690 log.warning(botId.getStringId() + ": Unregistering bot.");
691
692 PlayerMessage player;
693 synchronized(registeredBots) {
694 player = registeredBots.remove(botId);
695 }
696
697 if (player == null) {
698 log.severe(botId.getStringId() + ": Could not FULLY unregister bot as we do not have PlayerMessage for it! Data structures corrupted.");
699 } else {
700 TCTeam team = teams.get(player.getTeam());
701 if (team == null) {
702 log.severe(botId.getStringId() + ": Could not FULLY unregister bot as we do not have TCTeam[" + player.getTeam() + "] for it!");
703 } else {
704 synchronized(team) {
705 team.getConnectedBots().remove(botId);
706 Set<TCChannel> toRemove = new HashSet<TCChannel>();
707 synchronized(team.getChannels()) {
708 for (TCChannel channel : team.getChannels().values()) {
709 if (channel.getCreator().equals(botId)) {
710 toRemove.add(channel);
711 } else {
712 synchronized(channel) {
713 channel.getConnectedBots().remove(botId);
714 }
715 }
716 }
717
718 for (TCChannel channel : toRemove) {
719 team.getChannels().remove(channel.getChannelId());
720
721
722 TCInfoTeamChannelDestroyed infoDataTeam = new TCInfoTeamChannelDestroyed(-1, owner.getSimTime());
723 infoDataTeam.setChannelId(channel.getChannelId());
724 infoDataTeam.setDestroyer(botId);
725 TCInfoMessage infoMessageTeam = new TCInfoMessage(infoDataTeam);
726 sendTeamExcept(infoMessageTeam, player.getTeam(), botId);
727 }
728 }
729 }
730 }
731 }
732
733 synchronized(botSessions) {
734 IoSession session = botSessions.remove(botId);
735 if (session == null) {
736 log.severe(botId.getStringId() + ": Could not FULLY unregister bot as we do not have IoSession for it! Data structures corrupted?");
737 } else {
738 session.close(true);
739 }
740 }
741
742 TCInfoBotLeft infoData = new TCInfoBotLeft(-1, owner.getSimTime());
743 infoData.setBotId(botId);
744 infoData.setTeam(player == null ? -1 : player.getTeam());
745 TCInfoMessage infoMessage = new TCInfoMessage(infoData);
746 sendGlobalExcept(infoMessage, botId);
747 }
748 }
749
750
751
752
753
754
755
756
757
758
759
760
761 private void invalidRequest(IoSession session, TCInfoRequestFailureType failureType, String reason, long requestId, UnrealId source) {
762 if (session == null) return;
763
764 if (failureType == null) failureType = TCInfoRequestFailureType.GENERIC_FAILURE;
765 if (reason == null || reason.isEmpty()) reason = "No info.";
766
767 log.warning((source == null ? "" : source.getStringId()) + ": InvalidRequest[" + failureType + "] " + reason);
768
769 TCInfoRequestFailed data = new TCInfoRequestFailed(requestId, owner.getSimTime());
770 data.setFailureType(failureType);
771
772 TCInfoMessage message = new TCInfoMessage(data);
773
774 try {
775 synchronized(session) {
776 session.write(message);
777 }
778 } catch (Exception e) {
779 if (running.getFlag()) {
780 log.warning(ExceptionToString.process(source.getStringId() + ": Failed to send message to the bot: " + message, e));
781 }
782 }
783 }
784
785 private void sessionError(IoSession session, String reason) {
786 if (session == null) return;
787
788 synchronized(mutex) {
789
790 if (!running.getFlag()) return;
791
792 log.warning("SessionError: " + reason);
793
794 UnrealId source = null;
795
796 synchronized(botSessions) {
797 for (Entry<UnrealId, IoSession> entry : botSessions.entrySet()) {
798 if (entry.getValue() == session) {
799 source = entry.getKey();
800 break;
801 }
802 }
803 }
804
805 if (source != null) {
806 botLeft(source);
807 } else {
808 synchronized(session) {
809 try {
810 session.close(true);
811 } catch (Exception e) {
812 log.warning(ExceptionToString.process("Could not close the session... ???", e));
813 }
814 }
815 }
816
817 }
818
819 }
820
821 }