1 /***
2 * Jetrix TetriNET Server
3 * Copyright (C) 2001-2003 Emmanuel Bourg
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 package net.jetrix;
21
22 import static net.jetrix.GameState.*;
23
24 import java.io.*;
25 import java.util.*;
26 import java.util.concurrent.*;
27 import java.util.logging.*;
28 import java.net.*;
29
30 import net.jetrix.config.*;
31 import net.jetrix.filter.*;
32 import net.jetrix.messages.*;
33 import net.jetrix.messages.channel.*;
34 import net.jetrix.messages.channel.specials.*;
35 import net.jetrix.winlist.*;
36 import net.jetrix.clients.TetrinetClient;
37
38 /***
39 * Game channel.
40 *
41 * @author Emmanuel Bourg
42 * @version $Revision: 868 $, $Date: 2010-08-27 13:41:18 +0200 (ven., 27 août 2010) $
43 */
44 public class Channel extends Thread implements Destination
45 {
46 /*** The maximum number of players per channel. */
47 public static final int MAX_PLAYERS = 6;
48
49 private ChannelConfig channelConfig;
50 private ServerConfig serverConfig;
51 private Logger log = Logger.getLogger("net.jetrix");
52
53 private BlockingQueue<Message> queue = new LinkedBlockingQueue<Message>();
54
55 private boolean open;
56 private GameState gameState = STOPPED;
57 private boolean running = true;
58 private GameResult result;
59
60 /*** The start time of the channel */
61 private long startTime;
62
63 /*** set of clients connected to this channel */
64 private Set<Client> clients = new HashSet<Client>();
65
66 /*** slot/player mapping */
67 private List<Client> slots = Arrays.asList(new Client[MAX_PLAYERS]);
68 private Field[] fields = new Field[MAX_PLAYERS];
69
70 private List<MessageFilter> filters = new ArrayList<MessageFilter>();
71
72 public Channel()
73 {
74 this(new ChannelConfig());
75 }
76
77 public Channel(ChannelConfig channelConfig)
78 {
79 this.channelConfig = channelConfig;
80 this.serverConfig = Server.getInstance().getConfig();
81
82
83 for (int i = 0; i < MAX_PLAYERS; i++)
84 {
85 fields[i] = new Field();
86 }
87
88 /***
89 * Loading filters
90 */
91
92
93 if (serverConfig != null)
94 {
95 Iterator<FilterConfig> globalFilters = serverConfig.getGlobalFilters();
96 while (globalFilters.hasNext())
97 {
98 addFilter(globalFilters.next());
99 }
100 }
101
102
103 Iterator<FilterConfig> channelFilters = channelConfig.getFilters();
104 while (channelFilters.hasNext())
105 {
106 addFilter(channelFilters.next());
107 }
108 }
109
110 /***
111 * Enable a new filter for this channel.
112 */
113 public void addFilter(FilterConfig filterConfig)
114 {
115 FilterManager filterManager = FilterManager.getInstance();
116 MessageFilter filter;
117
118
119
120 try
121 {
122
123 if (filterConfig.getClassname() != null)
124 {
125 filter = filterManager.getFilter(filterConfig.getClassname());
126 }
127 else
128 {
129 filter = filterManager.getFilterByName(filterConfig.getName());
130 }
131
132
133 filter.setChannel(this);
134 filter.setConfig(filterConfig);
135 filter.init();
136
137
138 filters.add(filter);
139 }
140 catch (FilterException e)
141 {
142 log.log(Level.WARNING, e.getMessage(), e);
143 }
144 }
145
146 public void removeFilter(String filterName)
147 {
148
149
150
151
152 }
153
154 public Iterator<MessageFilter> getFilters()
155 {
156 return filters.iterator();
157 }
158
159 /***
160 * Returns the filters applied to this channel not defined at the server level.
161 */
162 public Collection<MessageFilter> getLocalFilters()
163 {
164 Collection<MessageFilter> localFilters = new ArrayList<MessageFilter>();
165 for (MessageFilter filter : filters)
166 {
167 if (!filter.getConfig().isGlobal())
168 {
169 localFilters.add(filter);
170 }
171 }
172 return localFilters;
173 }
174
175 /***
176 * Main loop. The channel listens for incoming messages until the server
177 * or the channel closes. Every message is first passed through the
178 * registered filters and then handled by the channel.
179 */
180 public void run()
181 {
182 log.info("Channel " + channelConfig.getName() + " opened");
183
184 startTime = System.currentTimeMillis();
185
186 while (running && serverConfig.isRunning()
187 && (getConfig().isPersistent() || !clients.isEmpty() || (System.currentTimeMillis() - startTime < 200)))
188 {
189 LinkedList<Message> list = new LinkedList<Message>();
190
191 try
192 {
193
194 list.add(queue.take());
195
196
197 for (MessageFilter filter : filters)
198 {
199 int size = list.size();
200 for (int i = 0; i < size; i++)
201 {
202 filter.process(list.removeFirst(), list);
203 }
204 }
205
206
207 while (!list.isEmpty())
208 {
209 process(list.removeFirst());
210 }
211 }
212 catch (Exception e)
213 {
214 log.log(Level.WARNING, e.getMessage(), e);
215 }
216
217 }
218
219
220 if (serverConfig.isRunning())
221 {
222 ChannelManager.getInstance().removeChannel(this);
223 }
224
225 log.info("Channel " + channelConfig.getName() + " closed");
226 }
227
228 /***
229 * Stop the channel.
230 */
231 public void close()
232 {
233 running = false;
234 queue.add(new ShutdownMessage());
235 }
236
237 private void process(CommandMessage m)
238 {
239
240 Server.getInstance().send(m);
241 }
242
243 private void process(TeamMessage m)
244 {
245 Client client = (Client) m.getSource();
246 if (client.getUser().isPlayer())
247 {
248 int slot = m.getSlot();
249 getPlayer(slot).setTeam(m.getName());
250 sendAll(m, slot);
251 }
252 }
253
254 private void process(GmsgMessage m)
255 {
256 sendAll(m);
257 }
258
259 private void process(PlineMessage m)
260 {
261 int slot = m.getSlot();
262 String text = m.getText();
263 if (!text.startsWith("/"))
264 {
265 sendAll(m, slot);
266 }
267 }
268
269 private void process(PlineActMessage m)
270 {
271 int slot = m.getSlot();
272 if (m.getSource() == null)
273 {
274
275 sendAll(m);
276 }
277 else
278 {
279 sendAll(m, slot);
280 }
281 }
282
283 private void process(SmsgMessage m)
284 {
285 if (m.isPrivate())
286 {
287
288 for (Client client : clients)
289 {
290 if (client.getUser().isSpectator() && client != m.getSource())
291 {
292 client.send(m);
293 }
294 }
295 }
296 else
297 {
298 sendAll(m);
299 }
300 }
301
302 private void process(PauseMessage m)
303 {
304 gameState = PAUSED;
305
306
307 if (m.getSource() instanceof Client)
308 {
309 Client client = (Client) m.getSource();
310 sendAll(new PlineMessage("channel.game.paused-by", client.getUser().getName()));
311 }
312
313 sendAll(m);
314 }
315
316 private void process(ResumeMessage m)
317 {
318 gameState = STARTED;
319
320
321 if (m.getSource() instanceof Client)
322 {
323 Client client = (Client) m.getSource();
324 sendAll(new PlineMessage("channel.game.resumed-by", client.getUser().getName()));
325 }
326
327 sendAll(m);
328 }
329
330 private void process(PlayerWonMessage m)
331 {
332 if (m.getSource() == null)
333 {
334 sendAll(m);
335 }
336 }
337
338 private void process(PlayerLostMessage m)
339 {
340 int slot = m.getSlot();
341 Client client = getClient(slot);
342 User user = client.getUser();
343 sendAll(m);
344
345 boolean wasPlaying = user.isPlaying();
346 user.setPlaying(false);
347
348
349 if (wasPlaying)
350 {
351 result.update(user, false);
352 }
353
354
355 if (wasPlaying && countRemainingTeams() <= 1)
356 {
357 Message endgame = new EndGameMessage();
358 send(endgame);
359 result.setEndTime(new Date());
360
361
362 slot = 0;
363 for (int i = 0; i < slots.size(); i++)
364 {
365 client = slots.get(i);
366
367 if (client != null && client.getUser().isPlaying())
368 {
369 slot = i + 1;
370
371 result.update(client.getUser(), true);
372 }
373 }
374
375
376 if (slot != 0)
377 {
378 PlayerWonMessage playerwon = new PlayerWonMessage();
379 playerwon.setSlot(slot);
380 send(playerwon);
381
382 User winner = getPlayer(slot);
383 PlineMessage announce = new PlineMessage();
384 if (winner.getTeam() == null)
385 {
386 announce.setKey("channel.player_won", winner.getName());
387 }
388 else
389 {
390 announce.setKey("channel.team_won", winner.getTeam());
391 }
392 send(announce);
393 }
394
395
396 Winlist winlist = WinlistManager.getInstance().getWinlist(channelConfig.getWinlistId());
397 if (winlist != null)
398 {
399 winlist.saveGameResult(result);
400
401 List<Score> topScores = winlist.getScores(0, 10);
402 WinlistMessage winlistMessage = new WinlistMessage();
403 winlistMessage.setScores(topScores);
404 sendAll(winlistMessage);
405 }
406
407
408 serverConfig.getStatistics().increaseGameCount();
409 }
410 }
411
412 private void process(SpecialMessage m)
413 {
414
415 if (channelConfig.getSettings().getLinesPerSpecial() > 0)
416 {
417 int slot = m.getFromSlot();
418 sendAll(m, slot);
419 }
420 }
421
422 private void process(LevelMessage m)
423 {
424 sendAll(m);
425 }
426
427 private void process(FieldMessage m)
428 {
429 int slot = m.getSlot();
430
431
432 fields[slot - 1].update(m);
433
434 if (m.getSource() != null)
435 {
436 sendAll(m, slot);
437 }
438 else
439 {
440 if (m.isFullUpdate())
441 {
442
443 m.setField(fields[slot - 1].getFieldString());
444 }
445 sendAll(m);
446 }
447 }
448
449 private void process(StartGameMessage m)
450 {
451 if (gameState == STOPPED)
452 {
453
454 gameState = STARTED;
455
456
457 if (m.getSource() instanceof Client)
458 {
459 Client client = (Client) m.getSource();
460 sendAll(new PlineMessage("channel.game.started-by", client.getUser().getName()));
461 }
462
463
464 result = new GameResult();
465 result.setStartTime(new Date());
466 result.setChannel(this);
467
468
469 for (Client client : slots)
470 {
471 if (client != null && client.getUser().isPlayer())
472 {
473 client.getUser().setPlaying(true);
474 }
475 }
476
477
478 for (int i = 0; i < MAX_PLAYERS; i++)
479 {
480 fields[i].clear();
481 }
482
483
484 NewGameMessage newgame = new NewGameMessage();
485 newgame.setSlot(m.getSlot());
486 newgame.setSettings(channelConfig.getSettings());
487 if (channelConfig.getSettings().getSameBlocks())
488 {
489 Random random = new Random();
490 newgame.setSeed(random.nextInt());
491 }
492
493 sendAll(newgame);
494 }
495 }
496
497 private void process(StopGameMessage m)
498 {
499 EndGameMessage end = new EndGameMessage();
500 end.setSlot(m.getSlot());
501 end.setSource(m.getSource());
502 send(end);
503 }
504
505 private void process(EndGameMessage m)
506 {
507 if (gameState != STOPPED)
508 {
509
510 if (m.getSource() instanceof Client)
511 {
512 Client client = (Client) m.getSource();
513 sendAll(new PlineMessage("channel.game.stopped-by", client.getUser().getName()));
514 }
515
516 gameState = STOPPED;
517 sendAll(m);
518
519
520 for (Client client : slots)
521 {
522 if (client != null)
523 {
524 client.getUser().setPlaying(false);
525 }
526 }
527 }
528 }
529
530 private void process(DisconnectedMessage m)
531 {
532 Client client = m.getClient();
533 removeClient(client);
534
535 sendAll(new PlineMessage("channel.disconnected", client.getUser().getName()));
536 }
537
538 private void process(LeaveMessage m)
539 {
540 removeClient((Client) m.getSource());
541 }
542
543 private void process(AddPlayerMessage m)
544 {
545 Client client = m.getClient();
546
547 Channel previousChannel = client.getChannel();
548 client.setChannel(this);
549
550
551 if (previousChannel != null && !client.supportsMultipleChannels())
552 {
553
554 for (int j = 1; j <= MAX_PLAYERS; j++)
555 {
556 if (previousChannel.getPlayer(j) != null)
557 {
558 LeaveMessage clear = new LeaveMessage();
559 clear.setSlot(j);
560 client.send(clear);
561 }
562 }
563
564 previousChannel.removeClient(client);
565
566
567 PlineMessage announce = new PlineMessage();
568 announce.setKey("channel.join_notice", client.getUser().getName(), channelConfig.getName());
569 previousChannel.send(announce);
570
571
572 if (client.getUser().isPlaying())
573 {
574 client.getUser().setPlaying(false);
575 client.send(new EndGameMessage());
576 }
577 }
578
579
580 clients.add(client);
581
582 if (client.getUser().isSpectator())
583 {
584
585 JoinMessage mjoin = new JoinMessage();
586 mjoin.setName(client.getUser().getName());
587
588 sendAll(mjoin);
589
590
591 PlayerNumMessage mnum = new PlayerNumMessage(1);
592 client.send(mnum);
593 }
594 else
595 {
596
597 int slot = 0;
598 for (slot = 0; slot < slots.size() && slots.get(slot) != null; slot++) ;
599
600 if (slot >= MAX_PLAYERS)
601 {
602 log.warning("[" + getConfig().getName() + "] Panic, no slot available for " + client);
603 client.getUser().setSpectator();
604 }
605 else
606 {
607 slots.set(slot, client);
608
609
610 JoinMessage mjoin = new JoinMessage();
611 mjoin.setSlot(slot + 1);
612 mjoin.setName(client.getUser().getName());
613 sendAll(mjoin, client);
614
615
616 PlayerNumMessage mnum = new PlayerNumMessage(slot + 1);
617 client.send(mnum);
618 }
619
620
621 updateChannelOperator();
622 }
623
624
625 if (client.getUser().isSpectator())
626 {
627 sendSpectatorList(client);
628 }
629
630
631 for (int i = 0; i < slots.size(); i++)
632 {
633 Client resident = slots.get(i);
634 if (resident != null && resident != client)
635 {
636
637 JoinMessage mjoin2 = new JoinMessage();
638 mjoin2.setChannelName(getConfig().getName());
639 mjoin2.setSlot(i + 1);
640 mjoin2.setName(resident.getUser().getName());
641 client.send(mjoin2);
642
643
644 TeamMessage mteam = new TeamMessage();
645 mteam.setChannelName(getConfig().getName());
646 mteam.setSource(resident);
647 mteam.setSlot(i + 1);
648 mteam.setName(resident.getUser().getTeam());
649 client.send(mteam);
650 }
651 }
652
653
654 for (int i = 0; i < MAX_PLAYERS; i++)
655 {
656 if (!fields[i].isEmpty() || previousChannel != null)
657 {
658 FieldMessage message = new FieldMessage(i + 1, fields[i].getFieldString());
659 client.send(message);
660 }
661 }
662
663
664 Winlist winlist = WinlistManager.getInstance().getWinlist(channelConfig.getWinlistId());
665 if (winlist != null)
666 {
667 List<Score> topScores = winlist.getScores(0, 10);
668 WinlistMessage winlistMessage = new WinlistMessage();
669 winlistMessage.setScores(topScores);
670 client.send(winlistMessage);
671 }
672
673
674 PlineMessage mwelcome = new PlineMessage();
675 mwelcome.setKey("channel.welcome", client.getUser().getName(), channelConfig.getName());
676 client.send(mwelcome);
677
678
679 if (channelConfig.getTopic() != null)
680 {
681 BufferedReader topic = new BufferedReader(new StringReader(channelConfig.getTopic()));
682 try
683 {
684 String line;
685 while ((line = topic.readLine()) != null)
686 {
687 PlineMessage message = new PlineMessage();
688 message.setText("<kaki>" + line);
689 client.send(message);
690 }
691 topic.close();
692 }
693 catch (Exception e)
694 {
695 log.log(Level.WARNING, e.getMessage(), e);
696 }
697 }
698
699
700 if (client.getUser().isPlayer())
701 {
702 sendSpectatorList(client);
703 }
704
705
706 if (gameState != STOPPED)
707 {
708 IngameMessage ingame = new IngameMessage();
709 ingame.setChannelName(getConfig().getName());
710 client.send(ingame);
711
712
713 if (gameState == PAUSED)
714 {
715 client.send(new PauseMessage());
716 }
717 }
718
719
720 if (client instanceof TetrinetClient)
721 {
722 int timeout = getConfig().isIdleAllowed() ? 0 : serverConfig.getTimeout() * 1000;
723 try
724 {
725 ((TetrinetClient) client).getSocket().setSoTimeout(timeout);
726 }
727 catch (SocketException e)
728 {
729 log.log(Level.WARNING, "Unable to change the timeout", e);
730 }
731 }
732 }
733
734 /***
735 * Send the list of spectators in this channel to the specified client.
736 */
737 private void sendSpectatorList(Client client)
738 {
739
740 List<String> specnames = new ArrayList<String>();
741
742 for (Client c : clients)
743 {
744 if (c.getUser().isSpectator())
745 {
746 specnames.add(c.getUser().getName());
747 }
748 }
749
750
751 if (!specnames.isEmpty())
752 {
753 SpectatorListMessage spectators = new SpectatorListMessage();
754 spectators.setDestination(client);
755 spectators.setChannel(getConfig().getName());
756 spectators.setSpectators(specnames);
757 client.send(spectators);
758 }
759 }
760
761 public void process(PlayerSwitchMessage m)
762 {
763 if (gameState == STOPPED)
764 {
765
766 Client player1 = getClient(m.getSlot1());
767 Client player2 = getClient(m.getSlot2());
768
769
770 slots.set(m.getSlot1() - 1, player2);
771 slots.set(m.getSlot2() - 1, player1);
772
773
774 if (player1 != null)
775 {
776 LeaveMessage leave1 = new LeaveMessage();
777 leave1.setSlot(m.getSlot1());
778 sendAll(leave1);
779 }
780
781 if (player2 != null)
782 {
783 LeaveMessage leave2 = new LeaveMessage();
784 leave2.setSlot(m.getSlot2());
785 sendAll(leave2);
786 }
787
788 if (player1 != null)
789 {
790 JoinMessage mjoin = new JoinMessage();
791 mjoin.setSlot(m.getSlot2());
792 mjoin.setName(player1.getUser().getName());
793 sendAll(mjoin);
794
795 PlayerNumMessage mnum = new PlayerNumMessage(m.getSlot2());
796 player1.send(mnum);
797 }
798
799 if (player2 != null)
800 {
801 JoinMessage mjoin = new JoinMessage();
802 mjoin.setSlot(m.getSlot1());
803 mjoin.setName(player2.getUser().getName());
804 sendAll(mjoin);
805
806 PlayerNumMessage mnum = new PlayerNumMessage(m.getSlot1());
807 player2.send(mnum);
808 }
809
810
811 updateChannelOperator();
812 }
813 }
814
815 public void process(Message m)
816 {
817 if (log.isLoggable(Level.FINEST))
818 {
819 log.finest("[" + channelConfig.getName() + "] Processing " + m.getClass().getSimpleName() + " from " + m.getSource());
820 }
821
822 if (m instanceof CommandMessage) process((CommandMessage) m);
823 else if (m instanceof FieldMessage) process((FieldMessage) m);
824 else if (m instanceof SpecialMessage) process((SpecialMessage) m);
825 else if (m instanceof LevelMessage) process((LevelMessage) m);
826 else if (m instanceof PlayerLostMessage) process((PlayerLostMessage) m);
827 else if (m instanceof PlayerWonMessage) process((PlayerWonMessage) m);
828 else if (m instanceof TeamMessage) process((TeamMessage) m);
829 else if (m instanceof PlineMessage) process((PlineMessage) m);
830 else if (m instanceof GmsgMessage) process((GmsgMessage) m);
831 else if (m instanceof SmsgMessage) process((SmsgMessage) m);
832 else if (m instanceof PlineActMessage) process((PlineActMessage) m);
833 else if (m instanceof PauseMessage) process((PauseMessage) m);
834 else if (m instanceof ResumeMessage) process((ResumeMessage) m);
835 else if (m instanceof StartGameMessage) process((StartGameMessage) m);
836 else if (m instanceof StopGameMessage) process((StopGameMessage) m);
837 else if (m instanceof EndGameMessage) process((EndGameMessage) m);
838 else if (m instanceof DisconnectedMessage) process((DisconnectedMessage) m);
839 else if (m instanceof PlayerSwitchMessage) process((PlayerSwitchMessage) m);
840 else if (m instanceof LeaveMessage) process((LeaveMessage) m);
841 else if (m instanceof AddPlayerMessage) process((AddPlayerMessage) m);
842 else
843 {
844 if (log.isLoggable(Level.FINEST))
845 {
846 log.finest("[" + channelConfig.getName() + "] Message not processed " + m);
847 }
848 }
849 }
850
851 /***
852 * Remove the specified client from the channel.
853 */
854 public void removeClient(Client client)
855 {
856 if (client != null)
857 {
858 clients.remove(client);
859
860 LeaveMessage leave = new LeaveMessage();
861 leave.setName(client.getUser().getName());
862
863 int slot = slots.indexOf(client);
864 if (slot != -1)
865 {
866 slots.set(slot, null);
867 leave.setSlot(slot + 1);
868 }
869
870
871 if (gameState != STOPPED && client.getUser().isPlaying())
872 {
873 result.update(client.getUser(), false);
874 }
875
876 sendAll(leave);
877 }
878
879
880 if (!isEmpty())
881 {
882 updateChannelOperator();
883 }
884
885
886 if (isEmpty() && running)
887 {
888 gameState = STOPPED;
889 }
890
891
892 if (clients.isEmpty() && !getConfig().isPersistent())
893 {
894 send(new ShutdownMessage());
895 }
896 }
897
898 /***
899 * Add a message to the channel message queue.
900 *
901 * @param message message to add
902 */
903 public void send(Message message)
904 {
905 queue.add(message);
906 }
907
908 /***
909 * Send a message to all players in this channel.
910 *
911 * @param message the message to send
912 */
913 private void sendAll(Message message)
914 {
915 if (message.getDestination() == null)
916 {
917 message.setDestination(this);
918 }
919
920
921 for (Client client : clients)
922 {
923 client.send(message);
924 }
925 }
926
927 /***
928 * Send a message to all players but the one in the specified slot.
929 *
930 * @param message the message to send
931 * @param slot the slot to exclude
932 */
933 private void sendAll(Message message, int slot)
934 {
935 Client client = getClient(slot);
936 sendAll(message, client);
937 }
938
939 /***
940 * Send a message to all players but the specified client.
941 *
942 * @param message the message to send
943 * @param c the client to exclude
944 */
945 private void sendAll(Message message, Client c)
946 {
947 if (message.getDestination() == null)
948 {
949 message.setDestination(this);
950 }
951
952
953 for (Client client : clients)
954 {
955 if (client != c)
956 {
957 client.send(message);
958 }
959 }
960 }
961
962 /***
963 * Tell if the channel can accept more players.
964 *
965 * @return <tt>true</tt> if the channel is full, <tt>false</tt> if not
966 */
967 public boolean isFull()
968 {
969 return getPlayerCount() >= channelConfig.getMaxPlayers();
970 }
971
972 public boolean isEmpty()
973 {
974 return getPlayerCount() == 0;
975 }
976
977 /***
978 * Returns the number of players currently in this channel.
979 *
980 * @return player count
981 */
982 public int getPlayerCount()
983 {
984 int count = 0;
985
986 for (Client client : slots)
987 {
988 if (client != null)
989 {
990 count++;
991 }
992 }
993
994 return count;
995 }
996
997 /***
998 * Returns the channel configuration.
999 */
1000 public ChannelConfig getConfig()
1001 {
1002 return channelConfig;
1003 }
1004
1005 /***
1006 * Returns the game state.
1007 */
1008 public GameState getGameState()
1009 {
1010 return gameState;
1011 }
1012
1013 /***
1014 * Finds the slot used in the channel by the specified client.
1015 */
1016 public int getClientSlot(Client client)
1017 {
1018 return (slots.indexOf(client) + 1);
1019 }
1020
1021 /***
1022 * Returns the client in the specified slot.
1023 *
1024 * @param slot slot number between 1 and 6
1025 *
1026 * @return <tt>null</tt> if there is no client in the specified slot, or if the number is out of range
1027 */
1028 public Client getClient(int slot)
1029 {
1030 Client client = null;
1031
1032 if (slot >= 1 && slot <= slots.size())
1033 {
1034 client = slots.get(slot - 1);
1035 }
1036
1037 return client;
1038 }
1039
1040 /***
1041 * Returns the client in the specified slot.
1042 *
1043 * @param slot slot number between 1 and 6
1044 *
1045 * @return <tt>null</tt> if there is no client in the specified slot, or if the number is out of range
1046 */
1047 public User getPlayer(int slot)
1048 {
1049 Client client = getClient(slot);
1050 return (client != null) ? client.getUser() : null;
1051 }
1052
1053 /***
1054 * Return an iterator of players in this channel.
1055 */
1056 public Iterator<Client> getPlayers()
1057 {
1058 return slots.iterator();
1059 }
1060
1061 /***
1062 * Return an iterator of spectators observing this channel.
1063 */
1064 public Iterator<Client> getSpectators()
1065 {
1066 List<Client> spectators = new ArrayList<Client>();
1067
1068 for (Client client : clients)
1069 {
1070 if (client.getUser().isSpectator())
1071 {
1072 spectators.add(client);
1073 }
1074 }
1075
1076 return spectators.iterator();
1077 }
1078
1079 /***
1080 * Count how many teams are still fighting for victory. A teamless player
1081 * is considered as a separate team. The game ends when there is one team
1082 * left in game OR when the last player loose if only one team took part
1083 * in the game.
1084 *
1085 * @return number of teams still playing
1086 */
1087 private int countRemainingTeams()
1088 {
1089 Map<String, String> playingTeams = new HashMap<String, String>();
1090
1091 int nbTeamsLeft = 0;
1092
1093 for (Client client : slots)
1094 {
1095 if (client != null && client.getUser().isPlaying())
1096 {
1097 String team = client.getUser().getTeam();
1098
1099 if (team == null)
1100 {
1101 nbTeamsLeft++;
1102 }
1103 else
1104 {
1105 playingTeams.put(team, team);
1106 }
1107 }
1108 }
1109
1110 return nbTeamsLeft + playingTeams.size();
1111 }
1112
1113 /***
1114 * Return the field of the specified slot.
1115 */
1116 public Field getField(int slot)
1117 {
1118 return fields[slot];
1119 }
1120
1121 /***
1122 * Promote the first player in the channel to the channel operator access
1123 * level if necessary, and demote the former channel operator to the
1124 * player access level.
1125 *
1126 * @since 0.3
1127 */
1128 private void updateChannelOperator()
1129 {
1130 boolean firstFound = false;
1131 for (Client client : slots)
1132 {
1133 if (client != null)
1134 {
1135 User user = client.getUser();
1136
1137 if (user.getAccessLevel() == AccessLevel.PLAYER && !firstFound)
1138 {
1139
1140 user.setAccessLevel(AccessLevel.CHANNEL_OPERATOR);
1141 }
1142 else if (user.getAccessLevel() == AccessLevel.CHANNEL_OPERATOR && firstFound)
1143 {
1144
1145 user.setAccessLevel(AccessLevel.PLAYER);
1146 }
1147
1148 firstFound = true;
1149 }
1150 }
1151
1152 }
1153
1154 }