001    /**
002     * Jetrix TetriNET Server
003     * Copyright (C) 2001-2003  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.winlist;
021    
022    import java.io.*;
023    import java.util.*;
024    import java.util.logging.*;
025    
026    import net.jetrix.messages.channel.*;
027    import net.jetrix.config.*;
028    import net.jetrix.*;
029    
030    /**
031     * A standard winlist using the same scoring as the original TetriNET : the
032     * winner gets 3 points if there was 3 or more players (or different teams)
033     * involved in the game, 2 points otherwise; the second gets 1 point if there
034     * was 5 or more players in the game. The winlist is saved in a xxxx.winlist
035     * file.
036     *
037     * @author Emmanuel Bourg
038     * @version $Revision: 812 $, $Date: 2009-09-13 23:35:55 +0200 (Sun, 13 Sep 2009) $
039     */
040    public class SimpleWinlist implements Winlist
041    {
042        private String id;
043        protected List<Score> scores;
044        protected boolean initialized = false;
045        protected boolean persistent = true;
046    
047        protected Logger log = Logger.getLogger("net.jetrix");
048        protected WinlistConfig config;
049    
050        public SimpleWinlist()
051        {
052            scores = new ArrayList<Score>();
053        }
054    
055        public String getId()
056        {
057            return id;
058        }
059    
060        public void setId(String id)
061        {
062            this.id = id;
063        }
064    
065        public void init(WinlistConfig config)
066        {
067            this.config = config;
068        }
069    
070        public WinlistConfig getConfig()
071        {
072            return config;
073        }
074    
075        public boolean isPersistent()
076        {
077            return persistent;
078        }
079    
080        public void setPersistent(boolean persistent)
081        {
082            this.persistent = persistent;
083        }
084    
085        public synchronized Score getScore(String name, int type)
086        {
087            if (!initialized && persistent)
088            {
089                load();
090            }
091    
092            Score score = null;
093    
094            Score example = new Score();
095            example.setName(name);
096            example.setType(type);
097    
098            int i = scores.indexOf(example);
099            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            // reward the winning player or team
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                    // add a new entry into the winlist
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            // reward the second player or team
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                    // add a new entry into the winlist
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            // sort the winlist
188            Collections.sort(scores, new ScoreComparator());
189    
190            // announce the new scores of the winners
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            // save the winlist to the external file
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    }