View Javadoc

1   /***
2    * Jetrix TetriNET Server
3    * Copyright (C) 2001-2005  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.filter;
21  
22  import static net.jetrix.GameState.*;
23  
24  import java.util.*;
25  import java.text.*;
26  
27  import net.jetrix.*;
28  import net.jetrix.messages.channel.*;
29  import net.jetrix.messages.channel.specials.*;
30  import org.apache.commons.lang.time.StopWatch;
31  
32  /***
33   * A filter computing and displaying the number of pieces dropped per minute
34   * by each player.
35   *
36   * @author Emmanuel Bourg
37   * @version $Revision: 799 $, $Date: 2009-02-18 17:28:08 +0100 (Wed, 18 Feb 2009) $
38   */
39  public class StatsFilter extends GenericFilter
40  {
41      private List<PlayerStats> stats;
42      private StopWatch stopWatch;
43  
44      private static DecimalFormat df = new DecimalFormat("0.00");
45  
46      public void init()
47      {
48          stopWatch = new StopWatch();
49          stats = new ArrayList<PlayerStats>(6);
50          for (int i = 0; i < 6; i++)
51          {
52              stats.add(null);
53          }
54      }
55  
56      public void onMessage(StartGameMessage m, List<Message> out)
57      {
58          if (getChannel().getGameState() == STOPPED)
59          {
60              // reset and start the stop watch
61              stopWatch.reset();
62              stopWatch.start();
63  
64              // initialize the stats
65              for (int i = 0; i < 6; i++)
66              {
67                  if (getChannel().getClient(i + 1) != null)
68                  {
69                      stats.set(i, new PlayerStats());
70                  }
71                  else
72                  {
73                      stats.set(i, null);
74                  }
75              }
76          }
77  
78          out.add(m);
79      }
80  
81      public void onMessage(EndGameMessage m, List<Message> out)
82      {
83          // forward the message
84          out.add(m);
85  
86          if (getChannel().getGameState() != STOPPED)
87          {
88              stopWatch.stop();
89              displayStats(out);
90          }
91      }
92  
93      public void onMessage(PauseMessage m, List<Message> out)
94      {
95          if (getChannel().getGameState() == STARTED)
96          {
97              // suspend the stop watch
98              stopWatch.suspend();
99          }
100 
101         out.add(m);
102     }
103 
104     public void onMessage(ResumeMessage m, List<Message> out)
105     {
106         if (getChannel().getGameState() == PAUSED)
107         {
108             // resume the stop watch
109             stopWatch.resume();
110         }
111 
112         out.add(m);
113     }
114 
115     public void onMessage(FieldMessage m, List<Message> out)
116     {
117         // increase the block count for the updated slot
118         PlayerStats playerStats = stats.get(m.getSlot() - 1);
119         if (playerStats != null && (stopWatch.getTime() > 1500))
120         {
121             playerStats.blockCount++;
122         }
123 
124         out.add(m);
125     }
126 
127     public void onMessage(LinesAddedMessage m, List<Message> out)
128     {
129         out.add(m);
130 
131         updateStats(m);
132 
133         // remove 1 block count from any player in an opposite team
134         removeBlock(m);
135     }
136 
137     public void onSpecial(SpecialMessage m, List<Message> out)
138     {
139         if (!(m instanceof LinesAddedMessage))
140         {
141             // add a special received by the target
142             PlayerStats playerStats = stats.get(m.getSlot() - 1);
143             playerStats.specialsReceived++;
144 
145             // remove 2 blocks count from the target
146             playerStats.blockCount = playerStats.blockCount - 2;
147 
148             // add a special sent by the sender
149             playerStats = stats.get(m.getFromSlot() - 1);
150             playerStats.specialsSent++;
151         }
152     }
153 
154     public void onMessage(LevelMessage m, List<Message> out)
155     {
156         out.add(m);
157         PlayerStats playerStats = stats.get(m.getSlot() - 1);
158         if (playerStats != null)
159         {
160             playerStats.level = m.getLevel();
161         }
162     }
163 
164     public void onMessage(LeaveMessage m, List<Message> out)
165     {
166         out.add(m);
167         // remove the stats from the list if a player leave the channel
168         stats.set(m.getSlot() - 1, null);
169     }
170 
171     public void onMessage(PlayerLostMessage m, List<Message> out)
172     {
173         out.add(m);
174         PlayerStats playerStats = stats.get(m.getSlot() - 1);
175         if (playerStats != null)
176         {
177             playerStats.playing = false;
178             playerStats.timePlayed = stopWatch.getTime();
179         }
180     }
181 
182     /***
183      * Decrease the block count of players receiving an add to all message since
184      * they will send back a field message assimilated by mistake as a block fall.
185      */
186     private void removeBlock(SpecialMessage message)
187     {
188         int slot = message.getFromSlot();
189 
190         // find the team of the player sending the message;
191         String team = null;
192 
193         if (message.getSource() != null && message.getSource() instanceof Client)
194         {
195             Client client = (Client) message.getSource();
196             team = client.getUser().getTeam();
197         }
198 
199         // check all players...
200         for (int i = 1; i <= 6; i++)
201         {
202             Client client = getChannel().getClient(i);
203             if (i != slot && client != null)
204             {
205                 User user = client.getUser();
206 
207                 // ...still playing, and team-less or in a different team from the sender
208                 if (user.isPlaying() && (user.getTeam() == null || !user.getTeam().equals(team)))
209                 {
210                     PlayerStats playerStats = stats.get(i - 1);
211                     playerStats.blockCount--;
212                 }
213             }
214         }
215     }
216 
217     /***
218      * Update the stats of the player sending the specified message.
219      *
220      * @param message
221      */
222     private void updateStats(LinesAddedMessage message)
223     {
224         if (message.getFromSlot() > 0) // ignore messages sent by the server
225         {
226             PlayerStats playerStats = stats.get(message.getFromSlot() - 1);
227             if (playerStats != null)
228             {
229                 playerStats.linesAdded += message.getLinesAdded();
230                 if (message.getLinesAdded() == 4)
231                 {
232                     playerStats.tetrisCount++;
233                 }
234             }
235         }
236     }
237 
238     private void displayStats(List<Message> out)
239     {
240         for (int slot = 1; slot <= 6; slot++)
241         {
242             PlayerStats playerStats = stats.get(slot - 1);
243             User user = getChannel().getPlayer(slot);
244 
245             if (playerStats != null && user != null)
246             {
247                 // update the play time of the remaining players
248                 if (playerStats.playing)
249                 {
250                     playerStats.timePlayed = stopWatch.getTime();
251                 }
252 
253                 // display the stats
254                 String bpm = df.format(playerStats.getBlocksPerMinute());
255 
256                 StringBuilder text = new StringBuilder();
257                 text.append("<purple>" + user.getName() + "</purple> : ");
258                 text.append(playerStats.blockCount + " <aqua>blocks @<red>" + bpm + "</red> bpm, ");
259                 text.append("<black>" + playerStats.linesAdded + "</black> added, ");
260                 text.append("<black>" + playerStats.tetrisCount + "</black> tetris");
261                 if (getChannel().getConfig().getSettings().getSpecialAdded() > 0)
262                 {
263                     // stats on special blocks for non pure game only
264                     text.append(", <black>" + playerStats.specialsSent + " / " + playerStats.specialsReceived + "</black> specials");
265                 }
266 
267                 out.add(new PlineMessage(text.toString()));
268             }
269         }
270 
271         // display the total game time
272         PlineMessage time = new PlineMessage();
273         time.setText("<brown>Total game time: <black>" + df.format(stopWatch.getTime() / 1000f) + "</black> seconds"); // todo i18n
274         out.add(time);
275     }
276 
277     public String getName()
278     {
279         return "Stats Filter";
280     }
281 
282     public String getDescription()
283     {
284         return "Displays stats about the game (pieces dropped per minute, lines added to all, time played, etc";
285     }
286 
287     public String getVersion()
288     {
289         return "1.1";
290     }
291 
292     public String getAuthor()
293     {
294         return "Emmanuel Bourg";
295     }
296 
297     private class PlayerStats
298     {
299         long timePlayed;
300         int tetrisCount;
301         int linesAdded;
302         int blockCount;
303         int level;
304         int specialsSent;
305         int specialsReceived;
306         boolean playing = true;
307 
308         public double getBlocksPerMinute()
309         {
310             return (double) blockCount * 60000 / (double) timePlayed;
311         }
312     }
313 
314 }