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 }