001    /**
002     * Jetrix TetriNET Server
003     * Copyright (C) 2001-2004  Emmanuel Bourg
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License
007     * as published by the Free Software Foundation; either version 2
008     * of the License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful,
011     * but WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013     * GNU General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
018     */
019    
020    package net.jetrix;
021    
022    import java.util.*;
023    
024    import net.jetrix.config.*;
025    import net.jetrix.messages.ShutdownMessage;
026    
027    /**
028     * Manages Channels
029     *
030     * @author Emmanuel Bourg
031     * @version $Revision: 860 $, $Date: 2010-05-06 13:21:05 +0200 (jeu., 06 mai 2010) $
032     */
033    public class ChannelManager
034    {
035        private List<Channel> channels;
036        private Map<String, Channel> channelMap;
037        private static ChannelManager instance = new ChannelManager();
038    
039        private ChannelManager()
040        {
041            channels = new ArrayList<Channel>();
042            channelMap = new TreeMap<String, Channel>();
043        }
044    
045        public static ChannelManager getInstance()
046        {
047            return instance;
048        }
049    
050        /**
051         * Create a channel and start it immediately.
052         *
053         * @param config the channel configuration
054         */
055        public Channel createChannel(ChannelConfig config)
056        {
057            return createChannel(config, true);
058        }
059    
060        /**
061         * Create a channel initialized with the specified configuration.
062         *
063         * @param config the channel configuration
064         * @param start  initial state
065         */
066        public Channel createChannel(ChannelConfig config, boolean start)
067        {
068            Channel channel = new Channel(config);
069            channel.setName("channel: " + config.getName());
070            if (start)
071            {
072                channel.start();
073            }
074            channels.add(channel);
075            channelMap.put(config.getName().toLowerCase(), channel);
076            return channel;
077        }
078    
079        /**
080         * Remove a channel.
081         */
082        public void removeChannel(String name)
083        {
084            // get the channel
085            Channel channel = getChannel(name);
086            
087            removeChannel(channel);
088            
089            // close it as soon as the last client leaves
090            channel.getConfig().setPersistent(false);
091            channel.send(new ShutdownMessage());
092        }
093    
094        /**
095         * Remove a channel.
096         */
097        public void removeChannel(Channel channel)
098        {
099            String name = channel.getConfig().getName().toLowerCase();
100    
101            // unregister the channel
102            channels.remove(channel);
103            channelMap.remove(name.toLowerCase());
104        }
105    
106        /**
107         * Returns the number of existing channels.
108         */
109        public int getChannelCount()
110        {
111            return channels.size();
112        }
113    
114        /**
115         * Returns an iterator over the channels available.
116         */
117        public Collection<Channel> channels()
118        {
119            return channels;
120        }
121    
122        /**
123         * Looks for a channel with room left.
124         *
125         * @return <tt>null</tt> if there is no room left in all available channels
126         *
127         * @deprecated
128         */
129        public Channel getOpenedChannel()
130        {
131            Channel channel = null;
132            Iterator<Channel> it = channels.iterator();
133            while (it.hasNext() && channel == null)
134            {
135                Channel channel2 = it.next();
136                if (!channel2.isFull())
137                {
138                    channel = channel2;
139                }
140            }
141    
142            return channel;
143        }
144    
145        /**
146         * Return the most suitable home channel for a player newly connected,
147         * that's the first accessible channel with players to play with.
148         *
149         * The channel selected matches the following criteria:
150         * <ul>
151         *   <li>it must be accessible for the specified access level</li>
152         *   <li>it must not be password protected</li>
153         *   <li>it must have room left</li>
154         *   <li>it should not be empty</li>
155         * </ul>
156         *
157         * @since 0.2
158         *
159         * @param level the access level of the player added in the channel
160         * @param protocol the name of the protocol used
161         */
162        public Channel getHomeChannel(int level, String protocol)
163        {
164            Channel channel = null;
165            Channel emptyChannel = null;
166    
167            Iterator<Channel> it = channels.iterator();
168            while (it.hasNext() && channel == null)
169            {
170                Channel chan = it.next();
171    
172                if (chan.getConfig().getAccessLevel() <= level
173                        && !chan.getConfig().isPasswordProtected()
174                        && !chan.isFull()
175                        && chan.getConfig().isProtocolAccepted(protocol))
176                {
177                    if (chan.isEmpty())
178                    {
179                        emptyChannel = (emptyChannel != null) ? emptyChannel : chan;
180                    }
181                    else
182                    {
183                        channel = chan;
184                    }
185                }
186            }
187    
188            return channel != null ? channel : emptyChannel;
189        }
190    
191        /**
192         * Tells if a non private channel is available for a client using the specified protocol.
193         * 
194         * @param protocol the name of the protocol used
195         * @since 0.3
196         */
197        public boolean hasCompatibleChannels(String protocol)
198        {
199            for (Channel chan : channels)
200            {
201                if (!chan.getConfig().isPasswordProtected() && chan.getConfig().isProtocolAccepted(protocol))
202                {
203                    return true;
204                }
205            }
206            
207            return false;
208        }
209    
210        /**
211         * Return the channel with the specified name. The leading # is removed from
212         * the name before searching. The name is not case sensitive.
213         *
214         * @param name the name of the channel to find
215         * @return
216         */
217        public Channel getChannel(String name)
218        {
219            return getChannel(name, false);
220        }
221    
222        /**
223         * Returns the channel with the specified name. The leading # is removed from
224         * the name before searching. The name is not case sensitive. If no channel
225         * matches the name specified, it can return the first channel starting
226         * with the name if the <code>partial</code> parameter is set to
227         * <code>true</code>.
228         *
229         * @param name    the name of the channel to find
230         * @param partial use the partial name matching
231         *
232         * @return instance of the specified channel, <tt>null</tt> if not found
233         */
234        public Channel getChannel(String name, boolean partial)
235        {
236            // stripping leading #
237            name = name.replaceFirst("#", "").toLowerCase();
238    
239            Channel channel = channelMap.get(name);
240    
241            if (channel == null && partial)
242            {
243                // match a partial name
244                Iterator<String> names = channelMap.keySet().iterator();
245                while (channel == null && names.hasNext())
246                {
247                    String name2 = names.next();
248                    if (name2.startsWith(name))
249                    {
250                        channel = channelMap.get(name2);
251                    }
252                }
253            }
254    
255            return channel;
256        }
257    
258        /**
259         * Clear the channel list.
260         */
261        public void clear()
262        {
263            channels.clear();
264            channelMap.clear();
265        }
266    
267        /**
268         * Close all channels.
269         */
270        public void closeAll()
271        {
272            for (Channel channel : channels)
273            {
274                try
275                {
276                    channel.close();
277                }
278                catch (Exception e)
279                {
280                    e.printStackTrace();
281                }
282            }
283        }
284    
285        /**
286         * Get a channel by number in the list.
287         */
288        public Channel getChannel(int num)
289        {
290            return ((num >= 0 && num < channels.size()) ? channels.get(num) : null);
291        }
292    
293    }