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.winlist;
21
22 import java.io.*;
23 import java.util.*;
24 import java.util.logging.*;
25
26 import net.jetrix.messages.channel.*;
27 import net.jetrix.config.*;
28 import net.jetrix.*;
29
30 /***
31 * A standard winlist using the same scoring as the original TetriNET : the
32 * winner gets 3 points if there was 3 or more players (or different teams)
33 * involved in the game, 2 points otherwise; the second gets 1 point if there
34 * was 5 or more players in the game. The winlist is saved in a xxxx.winlist
35 * file.
36 *
37 * @author Emmanuel Bourg
38 * @version $Revision: 812 $, $Date: 2009-09-13 23:35:55 +0200 (Sun, 13 Sep 2009) $
39 */
40 public class SimpleWinlist implements Winlist
41 {
42 private String id;
43 protected List<Score> scores;
44 protected boolean initialized = false;
45 protected boolean persistent = true;
46
47 protected Logger log = Logger.getLogger("net.jetrix");
48 protected WinlistConfig config;
49
50 public SimpleWinlist()
51 {
52 scores = new ArrayList<Score>();
53 }
54
55 public String getId()
56 {
57 return id;
58 }
59
60 public void setId(String id)
61 {
62 this.id = id;
63 }
64
65 public void init(WinlistConfig config)
66 {
67 this.config = config;
68 }
69
70 public WinlistConfig getConfig()
71 {
72 return config;
73 }
74
75 public boolean isPersistent()
76 {
77 return persistent;
78 }
79
80 public void setPersistent(boolean persistent)
81 {
82 this.persistent = persistent;
83 }
84
85 public synchronized Score getScore(String name, int type)
86 {
87 if (!initialized && persistent)
88 {
89 load();
90 }
91
92 Score score = null;
93
94 Score example = new Score();
95 example.setName(name);
96 example.setType(type);
97
98 int i = scores.indexOf(example);
99 if (i != -1)
100 {
101 score = scores.get(i);
102 }
103
104 return score;
105 }
106
107 public synchronized List<Score> getScores(long offset, long length)
108 {
109 if (!initialized && persistent)
110 {
111 load();
112 }
113
114 return scores.subList(0, Math.min(scores.size(), (int) length));
115 }
116
117 public synchronized void saveGameResult(GameResult result)
118 {
119 if (!initialized && persistent)
120 {
121 load();
122 }
123
124 int teamCount = result.getTeamCount();
125 if (teamCount == 1)
126 {
127 return;
128 }
129
130
131 Score score1 = null;
132 long previousRank1 = 0;
133 long previousScore1 = 0;
134 Collection<GamePlayer> winners = result.getPlayersAtRank(1);
135 GamePlayer winner = winners.iterator().next();
136
137 if (winner.isWinner())
138 {
139 String name = winner.getTeamName() == null ? winner.getName() : winner.getTeamName();
140 int type = winner.getTeamName() == null ? Score.TYPE_PLAYER : Score.TYPE_TEAM;
141 score1 = getScore(name, type);
142 previousRank1 = scores.indexOf(score1) + 1;
143 previousRank1 = previousRank1 == 0 ? scores.size() + 1 : previousRank1;
144
145 if (score1 == null)
146 {
147
148 score1 = new Score();
149 score1.setName(name);
150 score1.setType(type);
151 scores.add(score1);
152 }
153
154 previousScore1 = score1.getScore();
155 int points = teamCount >= 3 ? 3 : 2;
156 score1.setScore(score1.getScore() + points);
157 }
158
159
160 Score score2 = null;
161 long previousRank2 = 0;
162 long previousScore2 = 0;
163 Collection<GamePlayer> seconds = result.getPlayersAtRank(2);
164 GamePlayer second = seconds.iterator().next();
165
166 if (teamCount >= 5)
167 {
168 String name = second.getTeamName() == null ? second.getName() : second.getTeamName();
169 int type = second.getTeamName() == null ? Score.TYPE_PLAYER : Score.TYPE_TEAM;
170 score2 = getScore(name, type);
171 previousRank2 = scores.indexOf(score1) + 1;
172 previousRank2 = previousRank2 == 0 ? scores.size() + 1 : previousRank2;
173
174 if (score2 == null)
175 {
176
177 score2 = new Score();
178 score2.setName(name);
179 score2.setType(type);
180 scores.add(score2);
181 }
182
183 previousScore2 = score2.getScore();
184 score2.setScore(score2.getScore() + 1);
185 }
186
187
188 Collections.sort(scores, new ScoreComparator());
189
190
191 Channel channel = result.getChannel();
192 if (channel != null && config.getBoolean("display.score", false))
193 {
194 channel.send(getGainMessage(score1, previousScore1, previousRank1));
195 if (score2 != null)
196 {
197 channel.send(getGainMessage(score2, previousScore2, previousRank2));
198 }
199 }
200
201
202 if (persistent)
203 {
204 save();
205 }
206 }
207
208 public void clear()
209 {
210 scores.clear();
211 save();
212 }
213
214 public int size()
215 {
216 return scores.size();
217 }
218
219 /***
220 * Load the winlist from a file.
221 */
222 protected void load()
223 {
224 if (log.isLoggable(Level.FINE))
225 {
226 log.fine("loading winlist " + getId());
227 }
228
229 if (id != null)
230 {
231 BufferedReader reader = null;
232 File file = new File(id + ".winlist");
233 if (file.exists())
234 {
235 try
236 {
237 reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), ServerConfig.ENCODING));
238 String line;
239 while ((line = reader.readLine()) != null)
240 {
241 String[] fields = line.split("\t");
242 Score score = new Score();
243 score.setName(fields[2]);
244 score.setScore(Long.parseLong(fields[1]));
245 score.setType("p".equals(fields[0]) ? Score.TYPE_PLAYER : Score.TYPE_TEAM);
246 scores.add(score);
247 }
248 }
249 catch (Exception e)
250 {
251 log.log(Level.WARNING, "Unable to read the winlist file " + file, e);
252 }
253 finally
254 {
255 close(reader);
256 }
257 }
258 }
259
260 initialized = true;
261 }
262
263 /***
264 * Save the winlist to a file.
265 */
266 protected void save()
267 {
268 if (id != null)
269 {
270 BufferedWriter writer = null;
271 File file = new File(id + ".winlist");
272 try
273 {
274 writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), ServerConfig.ENCODING));
275
276 for (Score score : scores)
277 {
278 StringBuilder line = new StringBuilder();
279 line.append(score.getType() == Score.TYPE_PLAYER ? "p" : "t");
280 line.append("\t");
281 line.append(score.getScore());
282 line.append("\t");
283 line.append(score.getName());
284 line.append("\n");
285 writer.write(line.toString());
286 }
287 writer.flush();
288 }
289 catch (Exception e)
290 {
291 log.log(Level.WARNING, "Unable to write the winlist file " + file, e);
292 }
293 finally
294 {
295 close(writer);
296 }
297 }
298 }
299
300 /***
301 * Build a message displaying the new score and rank of a winner.
302 */
303 protected PlineMessage getGainMessage(Score score, long previousScore, long previousRank)
304 {
305 StringBuilder key = new StringBuilder();
306 key.append("channel.score.");
307 key.append(score.getType() == Score.TYPE_PLAYER ? "player" : "team");
308 key.append(".");
309 key.append(score.getScore() - previousScore > 1 ? "points" : "point");
310
311 long rank = scores.indexOf(score) + 1;
312 if (rank == 0)
313 {
314 rank = scores.size() + 1;
315 }
316
317 PlineMessage message = new PlineMessage();
318 message.setKey(key.toString(), score.getName(), score.getScore() - previousScore, score.getScore(), rank, previousRank - rank);
319 return message;
320 }
321
322 /***
323 * Close quietly the specified stream or reader.
324 */
325 void close(Closeable closeable)
326 {
327 if (closeable != null)
328 {
329 try
330 {
331 closeable.close();
332 }
333 catch (IOException e)
334 {
335 }
336 }
337 }
338
339 }